404 motivation not found | t_ishidaのブログ

CAT | javascript

ピーチクを vi っぽく見る user.jsです。
ピーチクのタイムラインをキーボード操作で遡るためのuser.jsです。中の人にuser.jsページを作ろうよーと言っても対応してくれないのでこちらで公開します。当たり前ですがOperaのuser.js向けに作っているものなのでOperaで動作させる事を目的としています。でも、多分以下の環境でも動くと思います。思うだけなので、動くかどうかは分かりません。

  • Firefoxのグリモン
  • chromeのextension
  • IEのTrixie(ヘッダコメントに適当なnamespace切らないとダメかもね)
// ==UserScript==
// @name           [ptic] viっぽいの
// @description    viっぽく見るuser.js
// @include        http://ptic.jp/u/tl*
// @author         t_ishida
// ==/UserScript==
(function( w, $ ) {
  var vi_like = {
    'COLOR'        : 'rgb(240, 150, 240)',
    'messageBlock' : null,
    'pageID'       : null,
    'currentIndex'   : 0,

    //
    // j: scrollDOWN
    //
    '74' : function scrollDown(){
      if( !this.canScrollDown() ) return;
      this.clearFocus()
      this.currentIndex++;
      w.scrollTo( 0, this.getLT()[1] - 50 );
      this.setFocus();
      if( !this.canScrollDown() ) this.pageMore();
    },

    //
    // K: scrollUP
    //
    '75' : function (){
      if(this.currentIndex < 1 ) return;
      this.clearFocus();
      this.currentIndex--;
      w.scrollTo( 0, this.getLT()[1] - 50 );
      this.setFocus();
    },

    //
    // R: reply
    //
    '82': function(){
      var a = this.current().getElementsByTagName('a');
      for( var i = 0, l = a.length; i < l; i++ )
        if( a[i].href.match( /javascript:reply/ ) )
          location.href = a[i].href;
    },

    //
    // T: RT
    //
    '84': function(){
      var a = this.current().getElementsByTagName('a');
      for( var i = 0, l = a.length; i < l; i++ )
        if( a[i].href.match( /javascript:retweet/ ) )
          location.href = a[i].href;
    },

    //
    // メッセージブロックを収集
    //
    'getMessageBlock' :  function (){
      var result = [];
      if( !this.pageID ) return ;
      var buf = $( this.pageID ).getElementsByTagName( 'li' );
      for( var i = 0, l = buf.length; i < l; i++ ) result.push( buf[i] );
      buf = $( this.pageID + '_more'  ).getElementsByTagName( 'li' );
      for( var i = 0, l = buf.length; i < l; i++ ) result.push( buf[i] );
      this.messageBlock = result;
      return result;
    },

    //
    // エレメントの絶対位置の取得
    //
    'getLT' : function () {
      var element = this.current();
      var valueT = 0, valueL = 0;
      do {
        valueT += element.offsetTop  || 0;
        valueL += element.offsetLeft || 0;
        element = element.offsetParent;
      } while (element);
      return [valueL, valueT];
    },

    //
    // 現在のインデックスに該当するオブジェクトを返却する
    //
    'current' : function(){
      return this.messageBlock[this.currentIndex];
    },

    //
    // フォーカスをクリアする
    //
    'clearFocus' : function(){
      this.current().style.backgroundColor = '';
    },

    //
    // フォーカスをセットする
    //
    'setFocus' : function(){
      this.current().style.backgroundColor = this.COLOR;
    },

    //
    // 次ページを足す
    //
    'pageMore' : function(){
      if  ( w.tweet_json_more ) w.tweet_json_more( this.pageID );
      else                      location.href = 'javascript:tweet_json_more("' + this.pageID + '");';
    },

    'canScrollDown' : function(){
      return ( this.messageBlock.length > ( this.currentIndex + 1 ) );
    },
    ///
    /// new
    ///
    'initialize' : function(){
      if( !location.href.match( /ch=(.+)(?:&|$)/ ) ) return;
      var self = this;
      self.pageID = RegExp.$1;
      self.getMessageBlock();
      self.setFocus();
      (function ( target, type, listener ){
        if( target.addEventListener ) target.addEventListener( type, listener, false );
        else                          target.attachEvent( 'on' + type, function() {listener.call( target, w.event ); });
      })(
        document.all ? document.body : w, 'keydown', function( e ){
          if( document.activeElement.tagName.match( /(?:text|select|input)/i ) ) return;
          if( !e ) e = event;
          if( self[e.keyCode] ){
            self.getMessageBlock();
            self[e.keyCode]();
          }
        });

      (function ( target, type, listener ){
        if( target.addEventListener ) target.addEventListener( type, listener, false );
        else                          target.attachEvent( 'on' + type, function() {listener.call( target, w.event ); });
      })(
        document.all ? document.body : w, 'scroll', function( e ){
          if( document.activeElement.tagName.match( /(?:text|select|input)/i ) ) return;
          if( !e ) e = event;
          /*
          var top = w.scrollHeight - w.innerHeight - w.pageYOffset;
          alert( top );
          if( top - 50 > this.getLT()[1] ){
            alert( 'hogehoge' );
            this['74']();
          }
          else if( top - 50 <  this.getLT()[1] ){
            this['75']();
          }
          */
        });

    }
  };
  vi_like.initialize();
})( this.unsafeWindow || window, function(id) { return document.getElementById( id ); } );
Share and Enjoy:
  • Digg
  • del.icio.us
  • Google Bookmarks
  • Tumblr
  • email
  • Facebook
  • FriendFeed

No tags

2月/10

3

OpenSocial(2)

GadgetXML

要約するとHTMLを書くためのXMLです。
こんなんです。

<?xml versionmixiアプリでは="1.0" encoding="UTF-8"?>
<Module>
  <!-- モジュールの設定をゴニョゴニョ書きます  -->
 <ModulePrefs title="アプリケーションのタイトルを書きます" description="アプリケーションの概要を書きます">
    <!-- 使う機能なんかをここで設定できます -->
  <Require feature="opensocial-0.8"/>
 </ModulePrefs>

  <!-- プロフィールページのガジェットとして出すやつの定義 -->
 <Content type="html" view="profile">
 <![CDATA[
  <p>プロフィールページに出すコンテンツを書きます。mixiで言えばprofile.pl?id=hogehoge</p>
 ]]></Content>

  <!-- homeページのガジェットとして定義するやつ-->
 <Content type="html" view="home">
 <![CDATA[
  <p>プロフィールページに出すコンテンツを書きます。mixiで言えばhome.pl?id=hogehoge</p>
 ]]></Content>

  <!-- canvasとして定義するやつ-->
 <Content type="html" view="canvas">
 <![CDATA[
  <p>アプリを全面起動できるページに出すコンテンツを書きます。mixiで言えばrun_appli.pl</p>
 ]]>
 </Content>
</Module>

見てもらうと分かると思うんですが、つまりはgadget.xmlのタグの中にHTMLの断片を書き込むだけです。そうすると、view=”ほげほげ” アトリビュートの指示に従ってOpenSocialコンテナが適当にiframeの中に書き出してくれてガジェットとして扱えるという代物ですね。勿論、単にHTMLを出すだけだとアプリでもなんでもないので、JavaScriptを使ってゴニョゴニョするというのが目的になっていく訳です。

さて、ソース中のコメントやら何やら見てもらうと分かると思うんですがcanvas,home,profile というviewについて定義しています。各ページの意味についてはソースの中に書いてあるので適当に想像してください。
一応まとめておくと

  • canvas – 全面でアプリケーションを実行できるview
  • profile – プロフィールページの横にちょろっと出すガジェット。当然ですがガジェットのownerのプロフィールにまつわるコンテンツを出力してあげるべきでしょう。このページは主にownerのプロフィールを見に来たユーザーの目に触れることになります。
  • home – ユーザーのhome(ログイン直後に表示されるページ。ダッシュボードとか)の横にちょろっと出す。ガジェット。当然ですがログイン直後に表示されるものなので、アプリケーションの概況を出してあげるのが好ましいでしょう。

他にpreviewというビューがあるのですがmixiが対応していないため、今回は試していません。よって詳しいことは書けません。アプリケーションをインストールするためのショーケースで表示されるコンテンツのようです。

なお、ちゃんとしており、かつ詳細な情報が欲しい人、または、t_ishidaの失礼な口調が嫌いな方は、本家を参照してください。ただ、SNSのOpenSocialコンテナごとに実装してたりしてなかったりするようなのでRFCを読む気分で読むことをお勧めいたします[要出典]

gadgetsオブジェクト、opensocialオブジェクト

当然ですが単にJavaScriptとHTMLの断片をSNS上で動かすだけではSNSの上でやる意味はありません。gadgetsオブジェクトというのがOpenSocialコンテナの上では使えます。
こいつを通して、実行者のプロフィールを取得したり、実行者の友達のプロフィールを取得したり、外部サーバーにJSONP使わずにリクエストしたりしてマッシュアップ(笑)をしてアプリケーションの面白味を出していくことになる訳です。

出来ることは本家を参照のこと。

独断と偏見的に大事なのは、

gadgets.io.makeRequest();
opensocial.Person;
opensocial.Activity;
opensocial.Message;

辺りだと思うので、この辺を読んで出来ることのイメージを掴むと良いと思います。

とは言えJavaScriptでゴリゴリ書くのとかアレだよね・・・・とか思っている人も多いんじゃないかと思います。普段からJavaScriptを扱う人でもJSばっかで作らなきゃイケないとか思ったら、ちょっと凹むと思います。なのでmixiアプリを見てみるとFlashが多いわけですね。FlashからJavaScriptの色々をキックする事が出来るようなので、そういう作り方をしているのでしょう。

RESTful APIとOAuth

OpenSocialでは”key => value ストレージ”を用意してくれているみたいなので、それさえ使えばJavaScriptだけでデータの永続化が可能なアプリが作れます!やったね!な感じなのですが、さすがGoogleさんが考えただけあって、実にストイックな発想です。ちょっと複雑なものになってきたら、そうもイカないでしょう。自前でサーバー用意して自分のサーバーでゴニョゴニョしたりして通信したいですよね。ここで出てくるのがRESTful APIとOAuthです。自分のサーバーからアクセスしてきているユーザーの認証をしたりJavaScriptのopensocialオブジェクト相当のデータを取得したりするのに使います。ですが、mixiさんはPC向けのRESTful APIはPeople & Friendしか開いていないので、全面的にアクセスできる訳ではありません。というかPC向けのRESTful APIからActivityを流し込んだり出来ないので普通の単体で動くWebアプリにOpenSocialをアドオンしてソーシャルアプリ化したものをiframeを使って転用するとかは出来ない訳です。ちなむと携帯電話向けのRESTful APIでは、やれるみたいです。
とりあえず、mixi向けにPC用のソーシャルアプリを作るときにはちゃんとJSで作れという事みたいです。他人の褌で相撲とろうとしてるので、その褌にサイズを合わせるしかありません。血涙流しながらやっていきましょう。

さて、やる気が尽きたので、この辺で。
次は実際に作ってみたり、開発環境を説明したりしたいと思います。

Share and Enjoy:
  • Digg
  • del.icio.us
  • Google Bookmarks
  • Tumblr
  • email
  • Facebook
  • FriendFeed

No tags

1月/10

8

OpenSocial (1)

能書き

OpenSocialについて調べる機会があったので頭整理しながら書いてみる。この道の専門家ではないしまだ踏み込むための準備をしている段階なので内容の正誤は保証できません。むしろ、詳しい人とかが読んで突っ込みたいこと有るようならガシガシ突っ込んでください。大歓迎です。
また、そういう事情なのでとっかかりが欲しいというレベルではない本気で勉強したい人は、公式なドキュメントを当たることをオススメします。

OpenSocialって何?

要約するとSNSとかにアプリケーションを作るためのAPIとか土台のこと。mixiアプリとかFacebookアプリ(Facebookは違いますね)とかを作るために使う技術。mixiアプリとかでプロフィールページになんかiGoogleのガジェットっぽいものを載せている人居るでしょ?アレ作るために使う技術です。GoogleがiGoogleのために作った仕組を流用しているのでiGoogleガジェットっぽいのは、つまりはそういうことです。

作り方

実は鬱陶しいことにOpenSocial依存の部分は対象のサイトの上で開発するしか無いです。勿論上手くスタブを作ってローカルで開発する事も出来るかも知れませんが。まだ、効率的な開発方法に触れられるほど僕自身が慣れている訳ではないので、ここでは触れません。あくまでOpenSocialってなんなのさ?っていう話だけで。
前置きが長くなりましたが、以下の手順で開発します。
とりあえずはmixiにしときます。

用意するもの

  • ガラパゴスケータイ
  • PC
  • 公開しているWebサーバー

Warning: 絶対にガラパゴス携帯が必要です。

まず、mixiのユーザーになるためにはガラパゴス携帯が必要でさらにデベロッパー登録の認証でもう一回必要になります。しかも、ガラパゴス携帯ならなんでも使える訳ではなく、ドコモ、AU、Softbankのガラパゴス携帯でなければなりません。emobileを使ってる人や、willcomのPHSを使っている人は、もうこの時点で無理です。そして、SoftbankだったとしてもiPhoneはガラパゴス携帯ではないので無理です。
さらにiPhoneはsoftbank.ne.jpのメールアドレスを使えばバリデーションが通ってしまいますが認証時にGUIDが取れずにワークフローが進まなくなるという形になります。これは誘ってくれた人に申し訳ない事になるので注意しましょう。

対応しているガラパゴス携帯を持っていない人は、もうmixiアプリの開発を諦めるか、mixiユーザーになるためにもう一台携帯を買うしかありません。実は、特定の人にとっては、ここが一番のハードルです。かく言う僕もiPhoneユーザーなので、どハマりしました。かなり頑張って裏道を探しましたが無理っぽいです。ガラパゴス携帯を使う以外の道は見つけられませんでした。
いずれにせよ、他人の褌で相撲を取ろうとしているので、借りた褌のサイズに合わせるしか有りません。
ひょっとしたら、そういった場合はmixi専用のAPIを使わずにgooやFacebook(Facebookは無理ですね)を使って作って知り合いのmixiユーザーに頼んで上げてもらうというのが一番健全かも知れません。

開発の手順

  • mixiのユーザーになります。絶対にmixiのアカウントが必要です。つまり、友達居ないのでmixiアカウントが無い人はmixiアプリを作る資格を得られません
  • ここからデベロッパー登録をします
  • ガラパゴス携帯のメールで認証依頼メールを受け取ってガラパゴス携帯のGUID認証をします
  • アプリケーション登録をします
  • 開発します

最後に開発します。とか言ってるけどアプリケーション登録のところで躓きましたね。誤解を恐れずに言えばガジェットXMLというのは、つまりアプリ本体と言っても良いでしょう。でも、きっと、その解釈だと正解であり誤解です。つまり、ガジェットXMLというのはmixiアプリとして提供するviewを定義する部分だけの話なのでviewだけで完結させられる小さなアプリならば、それだけが本体と言えるかも知れませんが自分のところでもサーバーサイドプログラミングしてデータ受け取って表示したりゴニョゴニョとマッシュアップ(笑)したりするつもりがあるのならば、それだけが本体ではないと言えるでしょう。ともあれ、そのXMLを自分で公開しているWebサーバーにアップロードして、そこまでのURLを登録するというのが、アプリ登録で出てきた項目”ガジェットXML”という項目の意味です。

例えばガジェットXMLというのは以下のようなものです

<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs title="Hello, mixi">
    <Require feature="opensocial-0.8" />
  </ModulePrefs>
  <Content type="html"><![CDATA[
  <h3>はじめてのmixiアプリ</h3>
  <div>
  <script type="text/javascript">
  for( var i = 0, list = 'はじめてのmixiアプリ'.split(''); i < list.length; i++ ) document.write( list[i] + '<br />' );
  </script>
  </div>
  ]]></Content>
</Module>

という訳で見ての通りHTMLの断片をXMLにCDATAで組み込んだものですね。その通り、このXMLの中でHTMLとJavaScriptを使って動的にガジェットを描画するのがOpenSocialの基礎です。このHTMLの断片の中からOpenSocialのAPIと自分でWebサーバーに用意したAPIをマッシュアップ(笑)させることでアプリを構築していきます。

やる気が尽きたので、今回はここまで。
次は、ガジェットXMLの色々とOpenSocialAPIについて書きます。

Share and Enjoy:
  • Digg
  • del.icio.us
  • Google Bookmarks
  • Tumblr
  • email
  • Facebook
  • FriendFeed

No tags

2月/09

26

JQuery

JQuery入門

のうがき

prototype.jsを使って重さに泣いて以来、「JavaScriptはスクラッチから書く」に限るの時代が続いていましたが、次回案件の調査に引っかけてJQueryに入門してみました。そのメモをここに残しておきます。

概要

JQueryとは身も蓋も無い言い方をすれば、JavaScriptのフレームワークです。特徴としては以下。

  • グローバル,prototypeの汚染を極力避けている
  • クロスブラウザ
  • 軽い

いずれも有り難いのですが、特に有り難いのが、グローバルやprototypeの汚染を避けてくれるところです。例えば、prototype.js派の人もスクラッチ派の人も、こんな書き方しますよね?

[1,2,3,4,5,6].each( function(x){
	document.write( x );
});

これは実装の仕方によるとは思いますが、

Array.prototype.each = function( f ){
  for(var i = 0, l = this.length; i < l; i++ ) f( this[i] );
}

とか、

function $A( obj ){
  obj.each = function(){
    for(var i = 0, l = this.length; i < l; i++ ) f( this[i] );
  }
}

とかやってるんだと思います。JQueryはこういう事を避けてグローバルな名前空間は"$"と"JQuery"しか汚染していませんし、prorotypeも汚しません。なので、他のライブラリ群と、ほとんど干渉せずに使用する事が出来ます($が干渉する可能性は高いですが、これも回避する方法が有りませす)

JQueryの基本的な仕組みとしては以下です。
x

  1. $('.class')でDOMを検索する
  2. JQueryオブジェクト.eachでDOMを走査する
  3. JQueryオブジェクトのメソッドで、DOMを操作する

以上です。

//
// idと言うidの振られているelementにonclickを追加する
//
$('#id').click( function(){
	$(this).text( 'clicked' );
	ev.stopPropagation();
});

//
// classと言うクラスの振られているelement全部に対してonclickを追加する
//
$('.class').each( function(){
	// thisは現在回しているelement, $にthisを渡しているのは
	// elementをJQueryオブジェクトに格納して、
	// dom操作を楽にしたいから
	$(this).click( function( ev ){
		$(this).text('clicked');
		ev.stopPropagation();
	});
});
///
/// 実は上のコードは以下と等価です
///
$('.class').click( function( ev ){
  $(this).text('clicked');
  ev.stopPropagation();
});

$について

"$"は、JQuery関数(コンストラクタ)のショートカットです。基本中の基本です。これから始めて、DOMを走査して操作して、動きの有るページを作っていきます。

$は以下の引数をオーバーロードしています。

$( '[selector]'[, element])
[selector]はCSSセレクタ(後述)の文字列です。"#id"とか".class"とかです。CSSセクレタの検索結果をJQeuryオブジェクトに格納して返します。elementを渡せば、その下位のDOMから探します。
$(element)
elementそのものをJQueryオブジェクトに格納して返します。
$( function(){} )
window.onloadの短縮系

本当は$(html)も有るんですが使った事無いので、割愛させて下さい。

CSSセレクタ

CSSの指定に使用する書式を利用して、DOMのelementを選択する仕組みです。$('selector')のように、$の引数として渡す文字列として使用します。DSL(domain specific language)みたいなものだと思ってください。

#id
getElementByIdです。対象のIDと一致するelementを選択する
.class
getElementsByClassNameです。対象のクラス名と一致するelementを選択する(複数)
tagName
getElementsByTagNameです。対象のタグ名と一致するelementを選択する(複数)
parent child
CSSの書き方で、div.className p {} とか書くアレです。例で言えばclassNameと言うクラスが振られているdivの下層のpタグを選択します。
#id, #id2, .class1, div, p
これより上で説明した書式をカンマ区切りで複数指定します。マッチした全部の要素を選択します。

本当は、もっと複雑な問い合わせも出来ますが書いているとキリがないので、後述の参考資料のリファレンスをご覧ください。

get

JQueryオブジェクトからelementを取りだす時に使用します。JQueryオブジェクトの操作だけでは対応しきれない時などに使用します。

$('#id').get(0).innerHTML= 'ヽ(゜▽、゜)ノ ';

Effects

基本的なエフェクトは割と簡単に定義出来ます。
ここでは基本説明と例示だけに留めて詳細はリファレンスに譲ります。

大体の場合、以下の引数を受け付けます。そして大体の場合省略できます。
function(speed, callback);

speed
スピードはミリ秒単位で指定します。そのアニメーションが、"どれ位の時間をかけて終了するか?"を指定します。
callback
そのEffectが終わった時に実行する関数を渡します。イベントハンドリングのようなものだと思ってください。
///
/// ボタンを押したら、表示・非表示の切り替わるdiv
///
$('#btn').click( function(){
	$('div#id').toggle( 200 );
});

イベント

イベントの登録に使用します。大概はhelperで事足りると思うので、下記に挙げるhelper系メソッドを使用しましょう。基本的にはイベントハンドラとなる関数を渡すだけで登録できます。ただし、バブリングするので、ev.stopPropagation()を忘れないようにしましょう。

$('#id').click(
  function(ev){
     // evはJQueryイベントオブジェクト
     // このイベントハンドリングに関する情報や、
     // イベント制御に必要なメソッドが格納されています
  });
  • blur(fn)
  • change(fn)
  • click(fn)
  • dblclick(fn)
  • error(fn)
  • focus(fn)
  • keydown(fn)
  • keypress(fn)
  • keyup(fn)
  • mousedown(fn)
  • mouseout(fn)
  • mouseover(fn)
  • mouseup(fn)
  • resize(fn)
  • scroll(fn)
  • select(fn)
  • submit(fn)
  • unload(fn)

CSS

styleの値を変更してブロックに色を付けたり、高さや幅を変えたり、移動したりします。JavaScriptと言えば、この値を変更する事で動きのあるページを表現する事が多いので、基本と言えば基本ですね。

height,width
高さを返したり、幅を返したり、引数に渡すと値を設定したりします
offset
親要素からの相対的なleftとtopを返します。

		var lt = $('#id').offset();
		alert( 'left:' + lt.left + ',top:' lt.top );
		
css( key, value );
値名と値を対にして設定します。
$('#id').css('color', 'red' );
css({ 'key' : value, 'key2' : 'value2' });
JSONでスタイルを指定します。
$('#id').css({ 'border' : 'solid thin black', 'color' : 'white'});
css(key);
指定したkeyのstyleの値を取得します。

		if( $('#id').css('display') != 'none' ){
			$('#id').css('display', 'none' );
		} else {
			$('#id').css('display', '');
		}
		

メソッドチェーン

大体の場合においてelementに対する副作用を期待するメソッドが多い中、そう言った処理の戻り値はthisが返ってきます。そこで、同じJQueryオブジェクトのメソッドをつないで一気にDOM操作をする事が出来ます。何を言っているのか分からないと思うので、実際にソースを見てみます。

$('#id')
	.mouseover( function( ev ){
		$(this).text( 'mouseover' );
		ev.stopPropagation();
	})
	.mouseout( function(){
		$( this ).text( 'mouseout' );
		ev.stopPropagation();
	})

これを見やすいとするかはセンスの問題だと思いますが、個人的にはすっきりするので好きです。

参考サイト

Share and Enjoy:
  • Digg
  • del.icio.us
  • Google Bookmarks
  • Tumblr
  • email
  • Facebook
  • FriendFeed

No tags

のうがき

モニタープラザのプロジェクトに参加してみる事にしました。僕は、0個でした。

プロジェクト

ESET NOD32アンチウイルス

「あなたのウイルス感染度チェック!」 by EC studioファンブロガー

と言う訳で、皆さんもチェックしてみてください。

【チェック項目】

  1. パソコンやインターネットのトラブルはお手上げだ
  2. 個人情報は自分に関係ないと思う
  3. インターネット以外ではウイルス感染しないと思う
  4. ウイルス対策ソフトの有効期限がきれても平気だ
  5. 知らない人からのメールやファイルを開くことがある

Share and Enjoy:
  • Digg
  • del.icio.us
  • Google Bookmarks
  • Tumblr
  • email
  • Facebook
  • FriendFeed

No tags

概要

「日本中のひとこと」から現在購読済のユーザーの発言を隠します。未購読のユーザーの発言を追いやすくなり、面白い発言をしている未購読のユーザーを探す時に便利です。主に、「沢山購読しているけど、さらに面白い発言をしている人を探したい。」とか言うWassr廃人向けのグリモンです。

インストールはWassrのグリモンから。

viっぽいのと併用は可能ですが上記のグリモンのリリースに併せて修正が入りました。併用する場合は再インストールして下さい。また併用するには実行順序に注意が必要です。

グリモンの実行順序

  1. AutoPagerize
  2. 日本中のひとことフィルター
  3. viっぽいの

また、Operaのユーザースクリプトとしても利用が可能です。と言うよりOperaで使えないと僕が困るヽ(゜▽、゜)ノ

TODO

  1. ボタンとかキー操作でtoggle出来た方が良くない?
  2. toggle出来るならチャンネルとかでも効くようにした方が良い?
Share and Enjoy:
  • Digg
  • del.icio.us
  • Google Bookmarks
  • Tumblr
  • email
  • Facebook
  • FriendFeed

No tags

1月/09

30

zeroclipboard

zeroclipboard

http://code.google.com/p/zeroclipboard/

これです。
JavaScriptからclipboardを扱おうとした場合IEならclipboardをJavaScriptから扱う仕組みが用意されているもののセキュリティ的に普通じゃ扱えなかったりして色々面倒くさかったり、他のブラウザでは、そもそも仕組みが用意されていなかったりしました。そこで、通常JavaScriptからclipboardを扱おうとした場合にはFlashを利用するのが一般的です。ただし、Flashを使える人以外は気軽に使える仕組みではありません。そこで登場したのがこのライブラリですが・・・・・動かせない!!!!
と、かなりの挌闘の末に諦めました。折を見て、retryしてみようと思います。

<script
   type="text/javascript"
   src="/js/zeroclipboard/ZeroClipboard.js">
</script>
<script type="text/javascript">
 ZeroClipboard.setMoviePath( 'http://ishida-tak.sakura.ne.jp/js/zeroclipboard/ZeroClipboard.swf'' );
var c = new ZeroClipboard.Client();
c.setHandCursor( true );
c.addEventListener( 'mouseover', function (){
   alert(
     document.getElementById('txt').value  +
     '  をクリップボードにコピー'
    );
   c.setText(
    document.getElementById('txt').value
    );
});

c.glue( 'flash_btn' );
</script>
<textarea id="txt">このデータね</textarea>
<div id="flash_btn"><b>Copy To Clipboard...</b></div>
<textarea>こっちに貼り付けてみてね</textarea>
Share and Enjoy:
  • Digg
  • del.icio.us
  • Google Bookmarks
  • Tumblr
  • email
  • Facebook
  • FriendFeed

No tags

使い方

  1. グリモンにインストールします
  2. wassrでポップアップ禁止を外します
  3. 使います
  4. LDRize使ってる人は、これを使わないか、LDRizeをWassr上で実行しないように設定して下さい。
  5. Firemacs使ってる人は挙動が訳分からなくなるかも知れません
  6. AutoPagerizeで開いたページはイイネボタンのイベントが登録されてないと思います。が、これを使ってイイネすればページ遷移しないで、イイネの実行が出来ます
  7. AutoPagerizeでの通信が遅い時とかは、せっかちにjボタン押さないで、ちょっと待ったほうが良いです。このグリモンは画面遷移型の勝手にページングに対応してますが、AutoPagerizeが始まる前に、勝手にページングが始まると、AutoPagerize使うタイプの人は逆にそれがウザイと思うので

インストール

viっぽく見る奴

http://ishida-tak.sakura.ne.jp/gm/wassr-key.user.js

「全部イイネ・イラネ」ボタンを設置

http://ishida-tak.sakura.ne.jp/gm/irane.user.js

治ったところ

  • URLのゴミ
  • rでリプライができるようになった
  • sで写真が見れるようになった
  • oで個人別のページに飛べるようになった

コマンド

  • rでリプライ
  • sで写真(写真開いた窓はキーボード押すと閉じます)
  • oで個人別ページ
  • 0でmyに飛ぶ
  • jで下に下がる
  • kで上に上がる
  • iでイイネする(イイネしてたらイラネする)

その他

コメントつけたので、適当にメンテして使ってください。

追記

2008/08/28

  • autopagerizeを考慮に入れて修正しました。割と便利。
  • 「f」で、全国のヒトコト出すようにしてみました。
  • window.openとか、画面遷移してたのを全部フロートパネルに出すようにしました。
  • 「q」で、フロートパネル閉じます
TODO

・LDRizeと併用できるようにLDRizeをハックする方法を考えたり考えなかったりする

// ==UserScript==
// @name           [Wassr] viっぽいの
// @description    viっぽく見るグリモン
// @include        http://wassr.jp/*
// @author         t_ishida
// ==/UserScript==
(function() {
//設定の類
var COLOR = 'rgb(240, 150, 240)';
var divs        = [];
var current_idx = 0;
var float_panel     = document.createElement( 'div' );
var button      = document.createElement( 'input' );
var iframe      = document.createElement( 'iframe' );
var p           = document.createElement( 'p' );
iframe.width    = '600px';
iframe.height   = '300px';
iframe.name     = '__ifr';

button.type     = 'button';
button.value    = '(^o^)/~';
observe( button, 'click', function(){ this.parentNode.parentNode.style.display = 'none'; } );
p.appendChild( button );
float_panel.appendChild( p );
float_panel.appendChild( iframe );
float_panel.id = '__float_panel';
float_panel.style.position        = 'fixed';
float_panel.style.top             = 0;
float_panel.style.left            = 0;
float_panel.style.display         = 'none';
float_panel.style.backgroundColor = 'black';
document.body.appendChild( float_panel );

initDivs();
if( location.href.match( /prev=1/ ) ){
// 前方ページングの時にはメッセージ末尾に移動
current_idx = divs.length - 2;
scrollDown();
} else {
// それ以外なら頭に移動
divs[current_idx].style.backgroundColor = COLOR;
}

//イベントのインストール
observe( document.all ? document.body : window, 'keydown', main  );

/***************************************************
   * main
   ***************************************************/
function main( e ){
if( document.activeElement.tagName.match( /(?:text|select|input)/i ) ) return;
initDivs();
if( !e ) e = event;
var cmds = {
'79' : showUser ,                         //o
'83' : showPicture ,                      //s
'73' : toggleIine ,                       //i
'74' : scrollDown ,                       //j
'75' : scrollUp ,                         //k
'82' : showEditor ,                       //r
'70' : showSearcher ,                     //f
'48' : function(){ location.href = 'http://wassr.jp/my'; } ,        //0
'81' : function(){ $('__float_panel').style.display = 'none'; } //q
};
setActive();
if( cmds[e.keyCode] ) cmds[e.keyCode]();
}

/*****************************************************
   * コマンド群
   *****************************************************/
//
// 一個上ににさがる
//
function scrollUp(){
if( current_idx < 1 ) {
if( !$('PagerPreviousLink') ) return;
var url_param = $('PagerNextLink').href.split( /?/ );
var url   = url_param[0];
var param = url_param[1].split( /&/ );
var buf = [];
for( var i = 0, l = param.length; i < l; i++ ){
if( !param[i].match( /ws_key/ ) ){
buf.push(  param[i] );
}
}
location.href =  url + '?' + buf.join( '&' ) + '&ws_key_prev=1';
return;
}
divs[current_idx].style.backgroundColor = '';
current_idx--;
window.scrollTo( 0, getLT( divs[current_idx] )[1] - 50 );
divs[current_idx].style.backgroundColor = COLOR;
}

//
// 一個下にさがる
//
function scrollDown(){
if( !divs[current_idx + 1] ){
if( !$('PagerNextLink') ) return;
var url_param = $('PagerNextLink').href.split( /?/ );
var url   = url_param[0];
var param = url_param[1].split( /&/ );
var buf = [];
for( var i = 0, l = param.length; i < l; i++ ){
if( !param[i].match( /ws_key/ ) ){
buf.push( param[i] );
}
}
location.href =  url + '?' + buf.join( '&' );
return;
}
divs[current_idx].style.backgroundColor = '';
current_idx++;
window.scrollTo( 0, getLT( divs[current_idx] )[1] -50 );
divs[current_idx].style.backgroundColor = COLOR;
}

//
// イイネする
//
function toggleIine(){
var elms = divs[current_idx].getElementsByTagName('input');
for( var i = 0, l = elms.length; i < l; i++ ){
if( elms[i].className != 'favorite_button' ) continue;
elms[i].parentNode.target = '__ifr';
if( elms[i].src.match( /E725/ ) ){
elms[i].src = "/img/pictogram/E72A.gif";
} else {
elms[i].src = "/img/pictogram/E725.gif";
}
elms[i].click();
break;
}
}

//
// リプライを表示
//
function showEditor(){
var elms = divs[current_idx].getElementsByTagName('a');
for( var i = 0, l = elms.length; i < l; i++ ){
if( elms[i].className.match(/taggedlink/) ) continue;
$('__float_panel').getElementsByTagName( 'iframe' )[0].src = elms[i].href;
$('__float_panel').style.display = '';
break;
}
}

//
// 写真を表示
//
function showPicture(){
var elms = divs[current_idx].getElementsByTagName('img');
for( var i = 0, l = elms.length; i < l; i++ ){
if( elms[i].alt == 'photo' ){
$('__float_panel').getElementsByTagName( 'iframe' )[0].src = elms[i].parentNode.href;
$('__float_panel').style.display = '';
break;
}
}
}

//
// その人の単票を開く
//
function showUser(){
var elms = divs[current_idx].getElementsByTagName('a');
for( var i = 0, l = elms.length; i < l; i++ ){
if( elms[i].className == 'MsgUserName' ){
$('__float_panel').getElementsByTagName( 'iframe' )[0].src = elms[i].href;
$('__float_panel').style.display = '';
break;
}
}
}

//
// メッセージブロックを収集
//
function initDivs(){
divs = [];
var buf = document.getElementsByTagName('div');
for( var i = 0, l = buf.length; i < l; i++ ){
if( buf[i].className == 'MsgBody' ){
divs.push( buf[i] );
}
}
}

function setActive(){
var elm = getParentNodeByClassName( 'MsgBody' ,document.activeElement  );
for( var i = 0, l = divs.length; i < l; i++ ){
if( elm == divs[i] ) {
current_idx = i;
elm.focus();
window.scrollTo( 0, getLT( elm )[1] );
break;
}
}
}
/*****************************************
   * 共通関数の類
   *****************************************/
//
// 長いから
//
function $( id ){ return document.getElementById( id ); }

//
// イベント設定のなんかほらアレ
//
function observe(target, type, listener) {
if   (target.addEventListener) target.addEventListener(type, listener, false);
else target.attachEvent('on' + type, function() { listener.call(target, window.event); });
}

//
// エレメントの絶対位置の取得
//
function getLT(element) {
var valueT = 0, valueL = 0;
do {
valueT += element.offsetTop  || 0;
valueL += element.offsetLeft || 0;
element = element.offsetParent;
} while (element);
return [valueL, valueT];
}

function getParentNodeByClassName( class_nm, elm ){
if( !elm ) return null;
if( elm.tagName.match( /body/i ) ) return null;
if( elm.className == class_nm )    return elm;
return getParentNodeByClassName( class_nm, elm.parentNode );
}
function showSearcher( ){
$('__float_panel').getElementsByTagName( 'iframe' )[0].src = 'http://wassr.jp/search/status';
$('__float_panel').style.display = '';
}
})();
Share and Enjoy:
  • Digg
  • del.icio.us
  • Google Bookmarks
  • Tumblr
  • email
  • Facebook
  • FriendFeed

No tags

使い方

jで下がって

kで上がって

iでイイネして

0でホームに戻ったりする。

備考

ページングに対応してないので、ちょっと微妙です。

多分、id:itengineerがやってくれると思います。

ページング対応しました。

最後まで行った時にjを押すと勝手にページングします。

モット見るタイプじゃなくて本当にページングするので、微妙です。それはid:itengineerがやってくれると思います。でも、現状だとURLにゴミが残るので注意。原因調査してる時間ないので、とりあえず、注意して使ってください。

以上。

// ==UserScript==
// @name           [Wassr] viっぽく見るの
// @description    viっぽいの
// @include        http://wassr.jp/*
// @author         t_ishida
// ==/UserScript==
(function() {
var divs = [];
var buf = document.getElementsByTagName('div');
for(var i = 0, l = buf.length; i < l; i++ ){
if( buf[i].className == 'MsgBody' ){
divs.push( buf[i] );
}
}
var current_idx = 0;
if( location.href.match( /prev=1/ ) ){
current_idx = divs.length - 2;
scrollDown();
}
observe(
window,
'keydown',
function( e ){
if( !e ) e = event;
if( e.keyCode == 73 ) return toggleIine();
if( e.keyCode == 74 ) return scrollDown();
if( e.keyCode == 75 ) return scrollUp();
if( e.keyCode == 82 ) return showEditor();
if( e.keyCode == 48 ) location.href = 'http://wassr.jp/my';
});
function $( id ){ return document.getElementById( id ); }
function observe(target, type, listener) {
if   (target.addEventListener) target.addEventListener(type, listener, false);
else target.attachEvent('on' + type, function() { listener.call(target, window.event); });
}
function getLT(element) {
var valueT = 0, valueL = 0;
do {
valueT += element.offsetTop  || 0;
valueL += element.offsetLeft || 0;
element = element.offsetParent;
} while (element);
return [valueL, valueT];
}
function scrollUp(){
if( current_idx < 1 ) {
if( !$('PagerPreviousLink') ) return;
location.href = $('PagerPreviousLink').href.replace(
/(?|&)ws_key_prev=1/g ,
function( x, v1 ){ return v1 == '?' ? '?' : ''; }
) + '&ws_key_prev=1';
return;
}
divs[current_idx].style.backgroundColor = '';
current_idx--;
window.scrollTo( 0, getLT( divs[current_idx] )[1] - 50 );
divs[current_idx].style.backgroundColor = 'rgb(200, 0, 200)';
}
function scrollDown(){
if( !divs[current_idx + 1] ){
if( !$('PagerNextLink') ) return;
location.href= $('PagerNextLink').href.replace(
/(?|&)ws_key_prev=1/g ,
function ( x, v1 ){ return v1 == '?' ? '?' : ''; }
);
return;
}
divs[current_idx].style.backgroundColor = '';
current_idx++;
window.scrollTo( 0, getLT( divs[current_idx] )[1] -50 );
divs[current_idx].style.backgroundColor = 'rgb(200, 0, 200)';
}
function toggleIine(){
var elms = divs[current_idx].getElementsByTagName('input');
for( var i = 0, l = elms.length; i < l; i++ ){
if( elms[i].className != 'favorite_button' ) continue;
elms[i].click();
break;
}
}

function showEditor(){
var elms = divs[current_idx].getElementsByTagName('a');
for( var i = 0, l = elms.length; i < l; i++ ){
if( elms[i].className != 'taggedlink' ) continue;
alert( 'ごめんまだこれ動かないんだ(T_T)' );
//elms[i].click();
break;
}
}
})();
Share and Enjoy:
  • Digg
  • del.icio.us
  • Google Bookmarks
  • Tumblr
  • email
  • Facebook
  • FriendFeed

No tags

洒落で作ったものなので、公開するのはあんまり良くない気がしてきたけど、使う方の良心に任せます。想定としては、内輪的なチャンネルとかで、片っ端からイイネして遊んだりしてる人とかが洒落で使う感じです。多用するとイイネ自体の存在意義が問われかねないので、使うときは十分注意して下さい。

// ==UserScript==
// @name           [Wassr] 全部イラネ
// @description    全部イラネ
// @include        http://wassr.jp/*
// @author         t_ishida
// ==/UserScript==
(function(){

var $ = function( id ){ return document.getElementById(id); }
function observe(target, type, listener) {
if   (target.addEventListener) target.addEventListener(type, listener, false);
else target.attachEvent('on' + type, function() { listener.call(target, window.event); });
}
///
/// 全部イラネ
///
var btn = document.createElement( 'input' );
btn.type = 'button';
btn.value = '全部イラネ\(^o^)/';
observe( btn, 'click', function(){
//確認する?
var canConfirm = confirm( '全部イラネやる前に聞く?' );
var x = 0;
var elms = document.getElementsByTagName('input');
for( var i = 0, l = elms.length; i < l; i++ ){
var elm = elms[i];
if( elm.className != 'favorite_button' ) continue;
if( elm.src.match( /E72A/ ) ){
if( canConfirm ){
elm.style.width = '150px';
elm.style.height = '150px';
elm.focus();
if( !confirm('イラネ?') ){
elm.style.width  = '';
elm.style.height = '';
continue;
}
elm.style.width  = '';
elm.style.height = '';
}
elm.click();
x++;
}
}
alert( x + '個イラネしたで。' );
});
$('Header').appendChild( btn );

///
/// 全部イイネ
///
var btn2 = document.createElement( 'input' );
btn2.type = 'button';
btn2.value = '全部イイネ(*^_^*)';
observe( btn2, 'click', function(){
//確認する?
var canConfirm = confirm( '全部イイネやる前に聞く?' );
var x = 0;
var elms = document.getElementsByTagName('input');
for( var i = 0, l = elms.length; i < l; i++ ){
var elm = elms[i];
if( elm.className != 'favorite_button' ) continue;
if( elm.src.match( /E725/ ) ){
if( canConfirm ){
elm.style.width = '150px';
elm.style.height = '150px';
elm.focus();
if( !confirm('イイネ?') ){
elm.style.width  = '';
elm.style.height = '';
continue;
}
elm.style.width  = '';
elm.style.height = '';
}
elm.click();
x++;
}
}
alert( x + '個イイネしたで。' );
});
$('Header').appendChild( btn2 );
})()
Share and Enjoy:
  • Digg
  • del.icio.us
  • Google Bookmarks
  • Tumblr
  • email
  • Facebook
  • FriendFeed

No tags

Older posts >>

Find it!

Theme Design by devolux.org

Tag Cloud