<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Web scratch &#187; Greasemonkey</title>
	<atom:link href="http://efcl.info/category/firefox/greasemonkey/feed/" rel="self" type="application/rss+xml" />
	<link>http://efcl.info</link>
	<description>フリーソフトやFirefoxなどについて、web全般なサイト</description>
	<lastBuildDate>Thu, 02 Sep 2010 13:23:27 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>OAuth for Greasemonkeyのラッパーライブラリ</title>
		<link>http://efcl.info/2010/0610/res1721/</link>
		<comments>http://efcl.info/2010/0610/res1721/#comments</comments>
		<pubDate>Thu, 10 Jun 2010 03:33:32 +0000</pubDate>
		<dc:creator>azu</dc:creator>
				<category><![CDATA[Greasemonkey]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://efcl.info/?p=1721</guid>
		<description><![CDATA[Post Now browsing to TwitterをOAuth認証に対応しました &#124; Web scratchでGreasemonkeyスクリプトの「Post Now browsing   to Twitter」をO [...]]]></description>
			<content:encoded><![CDATA[<p><strong><a href="http://efcl.info/2010/0609/res1715/">Post Now browsing to TwitterをOAuth認証に対応しました | Web scratch</a></strong>でGreasemonkeyスクリプトの「<a href="http://userscripts.org/scripts/show/46441">Post Now browsing   to Twitter</a>」をOAuth認証に対応したものを作りました。<br />OAuthのJavaScript実装はいくつかあるみたいですがGreasemonkeyで使う感じになってるものは無かったみたいなので、OAuth.jsを使ったtwitterでのOAuth認証を手助けするラッパーみたいなものを書きました。</p>
<p>xAuthを使えば楽な気もしますがブラウザベースのものはxAuthを使わずに<a href="http://help.twitter.com/requests/982242">OAuthを使ってね</a>との事です</p>
<p>大部分は</p>
<ul>
<li><a href="http://blog.tomatomax.net/archives/2696">TwitterクライアントのOAuth対応(Javascript編) | tomatomax.net</a></li>
<li><a href="http://techno-st.net/2009/11/26/twitter-api-oauth-0.html">Twitter API を OAuth で認証するスクリプトを 0 から書いてみた &#8211; trial and error</a></li>
<li><a href="http://sites.google.com/site/elekmole/twitter4jtop/00-preparation/h-oauth-preparation">H.Basic認証/OAuth認証 (陽昇れども地の底に光届かず)</a></li>
</ul>
<p>を参考に書きました。<br />何となく分かるかも知れませんが、今回扱うアプリはApplication Typeがclientタイプのものです。</p>
<p><a href="http://userscripts.org/scripts/show/78102"><span id="more-1721"></span><br /></a>今回書いたラッパーは以下のOAuthライブラリを簡単に扱うためのものになってます。<br /> OAuth.js<br /> SHA-1.js<br /> <a href="http://code.google.com/p/oauth/source/browse/code/javascript/">http://code.google.com/p/oauth/source/browse/code/javascript/</a> あたりにある。<br /> ラッパーと併せて3つのjsからなりますが、3回も@requireするのは面倒だったので一つのファイルにまとめたソースコードを<strong><a href="http://userscripts.org/scripts/show/78102">OAuth for Greasemonkey for Greasemonkey</a></strong>に置いてあります。<br />OAuth.jsはApache license ver2、SHA-1.jsはBSDライセンスになっています。<br />自分が書いたラッパー部分はMITライセンスとしておきます(基本的に自由に使える)<br />しかし、いつ変更するか分からないので自分で別途保存して@requireする方が良いでしょう。<br />追記: <strong>githubに置いたのでこちらを利用した方がいいと思います</strong></p>
<ul>
<li>
<h4><a href="http://github.com/azu/OAuth-for-Greasemonkey">azu&#8217;s OAuth-for-Greasemonkey at master &#8211; GitHub</a></h4>
</li>
</ul>
<p>以下はラッパー部分だけを抜き出したものです。(2010/08/07現在のソースから)<br /><strong>ここに書いてあるのは古いと思うので上のリンクから適当に見て下さい。</strong></p>
<pre class="brush:javascript;">// TwitterOauth for Greasemonkey
function TwitterOauth(){
    this.initialize.apply(this, arguments);
}
TwitterOauth.prototype = {
    initialize: function(con) {
        var accessor = this.getAccessor();
        if(accessor){
            this.accessor = accessor;
        }else{
            this.accessor.consumerKey = con.consumerKey;
            this.accessor.consumerSecret = con.consumerSecret;
            this.accessor.token = "";
            this.accessor.tokenSecret = "";
        }
    },
    accessor : {
        consumerKey : "",
        consumerSecret: "",
        token: "",// response access_token
        tokenSecret: "", // response access_token_secret
    },
    // temp for request
    request : {
        token :"",// response oauth_token
        tokenSecret: ""// response oauth_token_secret
    },
    // トークンが取得済みかの真偽値を返す
    isAuthorize : function(){
        var accessor = this.accessor;
        if(accessor.consumerKey &amp;&amp; accessor.consumerSecret &amp;&amp; accessor.token &amp;&amp; accessor.tokenSecret){
            return true;
        }else{
            return false;
        }
    },
    getAccessor : function(){
        var accessor = GM_getValue("OAuthAccessor", null);
        if(accessor){
            return JSON.parse(accessor);
        }else{
            return false;
        }
    },
    // 取得したトークンを保存
    saveAccessor : function(){
        GM_setValue("OAuthAccessor",JSON.stringify(this.accessor));
    },
    deleteAccessor : function(){
        var clientInfo = {
            consumerKey: this.accessor.consumerKey,
            consumerSecret: this.accessor.consumerSecret,
        }
        GM_deleteValue("OAuthAccessor");
        this.initialize(clientInfo);
    },
    // 認証ページのURLを取得
    getRequestToken : function(callback){
        var message = {
          method: "GET",
          action: "https://twitter.com/oauth/request_token",
          parameters: {
            oauth_signature_method: "HMAC-SHA1",
            oauth_consumer_key: this.accessor.consumerKey
          }
        };
        OAuth.setTimestampAndNonce(message);
        OAuth.SignatureMethod.sign(message, this.accessor);
        var target = OAuth.addToURL(message.action, message.parameters);
        var self = this;
        var options = {
          method: message.method,
          url: target,
          onload: function(d) {
            if(d.status == 200){
                var res = d.responseText;
                var parameter = self.getParameter(res);
                self.request.token = parameter&#91;"oauth_token"&#93;;
                self.request.tokenSecret = parameter&#91;"oauth_token_secret"&#93;;
                // requestURLを引数にcallback
                if(callback){
                    callback("https://twitter.com/oauth/authorize?oauth_token="+self.request.token);
                }
            }else{
                alert(d.statusText);
            }
          },
        };
        GM_xmlhttpRequest(options);

    },
    // pinを元にAccess Tokenを取得して保存、callbackにはaccessorオブジェクトを渡す
    getAccessToken : function(pin ,callback) {
        var message = {
          method: "GET",
          action: "https://twitter.com/oauth/access_token",
          parameters: {
            oauth_signature_method: "HMAC-SHA1",
            oauth_consumer_key: this.accessor.consumerKey,
            oauth_token: this.request.token, // Request Token
            oauth_verifier: pin
          }
        };
        OAuth.setTimestampAndNonce(message);
        OAuth.SignatureMethod.sign(message, this.request);
        var target = OAuth.addToURL(message.action, message.parameters);
        var self = this;
        var options = {
          method: message.method,
          url: target,
          onload: function(d) {
            if(d.status == 200){
                /* 返り値からAccess Token/Access Token Secretを取り出す */
                var res = d.responseText;
                var parameter = self.getParameter(res);
                self.accessor.token = parameter&#91;"oauth_token"&#93;;
                self.accessor.tokenSecret = parameter&#91;"oauth_token_secret"&#93;;
                // Accessorの保存
                self.saveAccessor();
                if(callback){
                    callback(self.accessor);
                }
            }else{
                alert(d.statusText);
            }
          },
        };

        GM_xmlhttpRequest(options); // 送信
    },
    // api+?+query にアクセスした結果をcallbackに渡す
    get : function(api, query, callback) {
        var btquery = (query)? "?"+this.buildQuery(query) : "";
        var message = {
          method: "GET",
          action: api + btquery,
          parameters: {
            oauth_signature_method: "HMAC-SHA1",
            oauth_consumer_key: this.accessor.consumerKey,// queryの構築
            oauth_token: this.accessor.token // Access Token
          }
        };
        OAuth.setTimestampAndNonce(message);
        OAuth.SignatureMethod.sign(message, this.accessor);
        var target = OAuth.addToURL(message.action, message.parameters);
        var options = {
          method: message.method,
          url: target,
          onload: function(d) {
            if(d.status == 200){
                if(callback){
                    callback(d.responseText);
                }
            }else{
                callback(d.statusText);
            }
          },
        };
        GM_xmlhttpRequest(options); // 送信
    },
    post : function(api, content, callback) {
        var message = {
          method: "POST",
          action: api,
          parameters: {
            oauth_signature_method: "HMAC-SHA1",
            oauth_consumer_key: this.accessor.consumerKey,
            oauth_token: this.accessor.token // Access Token
          }
        };
        // 送信するデータをパラメータに追加する
        for ( var key in content ) {
          message.parameters&#91;key&#93; = content&#91;key&#93;;
        }
        OAuth.setTimestampAndNonce(message);
        OAuth.SignatureMethod.sign(message, this.accessor);
        var target = OAuth.addToURL(message.action, message.parameters);
        var options = {
            method: message.method,
            url: target,
            onload: function(d) {
                if (d.status == 200) {
                    if (callback) {
                        callback(d.responseText);
                    }
                } else {
                    // typeof d == object
                    callback(d);
                }
            }
        };
        GM_xmlhttpRequest(options); // 送信
    },
    // GM_xmlhttpRequest風に使う
    xhr : function(opts){
        if(!(opts &amp;&amp; opts.url &amp;&amp; opts.method)){
            GM_log("URLまたはメソッドが指定されていません");
            return;
        }
        var message = {
          method: opts.method,
          action: opts.url,
          parameters: {
            oauth_signature_method: "HMAC-SHA1",
            oauth_consumer_key: this.accessor.consumerKey,
            oauth_token: this.accessor.token // Access Token
          }
        };
        // POST - opts.dataは文字列でもオブジェクトでも可能にする
        if(opts &amp;&amp; opts.method.toLowerCase() == "post" &amp;&amp; opts.data){
            if(typeof(opts.data) === "string"){// 文字列からパラメータオブジェクトを作る
                opts.data = this.getParameter(opts.data);
            }
            var content = opts.data;
            if(typeof(content) === "object"){
                for(var key in content) {
                    message.parameters&#91;key&#93; = content&#91;key&#93;;
                }
            }
        }
        OAuth.setTimestampAndNonce(message);
        OAuth.SignatureMethod.sign(message, this.accessor);
        opts.url = OAuth.addToURL(message.action, message.parameters);// URLを書き換え
        GM_xmlhttpRequest(opts);// 送信
    },
    // utility関数
    // http://kevin.vanzonneveld.net
    urlencode : function (str) {
        str = (str+'').toString();
        return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').
                                                                        replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
    },
    // オブジェクトからクエリを生成
    buildQuery : function(formdata, numeric_prefix, arg_separator) {
        // *     example 1: http_build_query({foo: 'bar', php: 'hypertext processor', baz: 'boom', cow: 'milk'}, '', '&amp;amp;');
        // *     returns 1: 'foo=bar&amp;amp;php=hypertext+processor&amp;amp;baz=boom&amp;amp;cow=milk'
        // *     example 2: http_build_query({'php': 'hypertext processor', 0: 'foo', 1: 'bar', 2: 'baz', 3: 'boom', 'cow': 'milk'}, 'myvar_');
        // *     returns 2: 'php=hypertext+processor&amp;myvar_0=foo&amp;myvar_1=bar&amp;myvar_2=baz&amp;myvar_3=boom&amp;cow=milk'
        var value, key, tmp = &#91;&#93;;
        var self = this;
        var _http_build_query_helper = function (key, val, arg_separator) {
            var k, tmp = &#91;&#93;;
            if (val === true) {
                val = "1";
            } else if (val === false) {
                val = "0";
            }
            if (val !== null &amp;&amp; typeof(val) === "object") {
                for (k in val) {
                    if (val&#91;k&#93; !== null) {
                        tmp.push(_http_build_query_helper(key + "&#91;" + k + "&#93;", val&#91;k&#93;, arg_separator));
                    }
                }
                return tmp.join(arg_separator);
            } else if (typeof(val) !== "function") {
                return self.urlencode(key) + "=" + self.urlencode(val);
            } else {
                throw new Error('There was an error processing for http_build_query().');
            }
        };

        if (!arg_separator) {
            arg_separator = "&amp;";
        }
        for (key in formdata) {
            value = formdata&#91;key&#93;;
            if (numeric_prefix &amp;&amp; !isNaN(key)) {
                key = String(numeric_prefix) + key;
            }
            tmp.push(_http_build_query_helper(key, value, arg_separator));
        }

        return tmp.join(arg_separator);
    },
    // Query String から 連想配列を返す
    getParameter: function(str){
      var dec = decodeURIComponent;
      var par = {}, itm;
      if(typeof(str) == 'undefined') return par;
      if(str.indexOf('?', 0) &#62; -1) str = str.split('?')&#91;1&#93;;
      str = str.split('&amp;');
      for(var i = 0; str.length &#62; i; i++){
        itm = str&#91;i&#93;.split("=");
        if(itm&#91;0&#93; != ''){
          par&#91;itm&#91;0&#93;&#93; = typeof(itm&#91;1&#93;) == 'undefined' ? true : dec(itm&#91;1&#93;);
        }
      }
      return par;
   }
};
</pre>
<p>OAuth認証を手助けすることを目的に書いたので、アクセストークンを使って実際にAPIをたたく部分のTwitterOauth.getやTwitterOauth.postは適当です。<br />ラッパーの動作自体はそこまで難しい事はやってないので、<strong>OAuth認証の手順を理解する方が大切</strong>です。<br /><a href="http://dev.twitter.com/apps">Twitter Applications | dev.twitter.com</a> から自分の作成するGreasemonkeyをnew appから登録しておきます。<br />Application TypeはClientならPINコードを使うタイプ、Browserなら認証したときにリダイレクトするURLを決めてそのURLでアクセストークンを受け取れます。今回は<strong>ClientタイプつまりPINコードを使う方法</strong>です。</p>
<div id="attachment_1745" class="wp-caption alignnone" style="width: 310px"><a href="http://efcl.info/wp-content/uploads/2010/06/sshot-2010-06-10-4.png"><img class="size-medium wp-image-1745" title="sshot-2010-06-10-4" src="http://efcl.info/wp-content/uploads/2010/06/sshot-2010-06-10-4-300x284.png" alt="" width="300" height="284" /></a><p class="wp-caption-text">登録でClientを選ぶ</p></div>
<p>ユーザーにPINコードをコピーさせてそれを入力させるという動作が、普通のWebサービスではあまり見ない流れだと思います。このラッパーでやっているOAuth認証の流れを簡単に図にするとこんな流れです。(Browserタイプよりユーザーの負担が大きい)</p>
<p><a href="http://efcl.info/wp-content/uploads/2010/06/4853f6fc7d7316c872b273d972580a55.png"><img class="aligncenter size-medium wp-image-1724" title="シーケンス図0" src="http://efcl.info/wp-content/uploads/2010/06/4853f6fc7d7316c872b273d972580a55-270x300.png" alt="" width="270" height="300" /></a>もっとコアの流れをみるなら<a href="http://d.hatena.ne.jp/furyu-tei/20090929/1254225568">OAuthのやり取りを読み取ってみる &#8211; 風柳メモ</a> とか</p>
<p>実際にGreasemonkeyでやってる動画 <a href="http://www.youtube.com/watch?v=8oXTuZk5xAk">YouTube &#8211; Post Now browsing to Twitter for Greasemonkey</a><br />かなり大雑把な流れなので詳しくは他のサイトを参照して下さい。<br />基本的にはトークンなどを元にしてOAuth.jsのOAuth.SignatureMethod.signでシグネチャを作成して、それを元に新たなトークンを受け取ってまたOAuth.SignatureMethod.signでシグネチャを作るみたいな繰り返しです。<br />最終的にAPIを叩く際に必要なものがアクセストークンとなります。</p>
<ol>
<li>リクエストトークンとリクエストトークンシークレットをもらいに行く。(タイムスタンプとconsumerKeyで一意な情報を元にする)</li>
<li>もらってきたリクエストトークンを使って認証ページのURLを作る。<br />(https://twitter.com/oauth/authorize?oauth_token= + リクエストトークンが認証ページのURL)</li>
<li>ユーザーに認証の動作を行ってもらう。</li>
<li>認証するとPINコードが表示されるので、ユーザーにPINコードをコピーしてアプリに入力してもらう。</li>
<li>入力してもらったPINコードを元にアクセストークンとアクセストークンシークレットをもらいに行く。(アクセストークンがAPIにたたくときに必要となるものです。リクエストトークンは認証時に必要となるだけなので使い捨てです。)</li>
<li>アクセストークンをもらったら保存して完了。</li>
</ol>
<p>実際にラッパーを使った大筋の流れとして、まずラッパーを呼び出して各自のアプリ情報を渡して初期化します。</p>
<pre class="brush:javascript;">// http://dev.twitter.com/apps に書いてある。
var clientInfo = {
    name: 'アプリ名',
    consumerKey: '各自アプリ',
    consumerSecret: '各自アプリ',
}
// TwitterOauthにクライアント情報を渡してnewする(必須)
var tw = new TwitterOauth(clientInfo);
</pre>
<p>そして、tw.isAuthorize() で既にアクセストークンを持っているかを調べられる(持ってたら直ぐにAPIをたたける)ので、持っていなかったらリクエストトークンをもらいに行きます。</p>
<pre class="brush:javascript;">if(tw.isAuthorize()){// まだ認証していない
    tw.getRequestToken(callback);
    // callbackに渡る引数は認証ページのURL
}
</pre>
<p>getRequestTokenのcallback関数には認証ページのURLが渡るので、それを元にユーザーにそのページに行ってもらいアプリのOAuth認証してPINコードをコピーしてきてもらいます。<br />PINコードを入力してもらう必要があるので、適当な入力エリアを作っておく必要もあります。</p>
<pre class="brush:javascript;">// アクセストークンをもらいに行く。
// アクセストークンを取得できたら自動で保存処理を行っています。
// callbackにはaccessor(アクセストークンなどが入ったオブジェクト)を渡しますが、使い道は…
tw.getAccessToken(callback);
// 取得したアクセストークンを捨てたいときは
// tw.deleteAccessor() で破棄できます。
</pre>
<p>アクセストークンを取得して保存できたらOAuth認証は完了です。<br />アクセストークンを持っていればAPIをたたけるので、tw.get、tw.postを使ってAPIを叩きます。</p>
<pre class="brush:javascript;">   // get第二引数はクエリになるオブジェクトを渡す
   // 以下はapi?q=oauth&amp;per_page=5 となる。
    var query = {
        q : "oauth",
        per_page : 5
    }
    // get : function(api, query, callback)
    tw.get("http://api.twitter.com/1/users/search.json" , query ,function(res){
        if(typeof res != "object"){// Twitterのエラー時はリスポンスのオブジェクトをそのまま返すため
            console.log(res); // firebug
        }
    })
</pre>
<pre class="brush:javascript;">    // postはそのまま何も工夫がない。
    var content = {status: "update test", source: clientInfo.name};
    tw.post('http://twitter.com/statuses/update.json', content ,function(){
        console.log(res); // firebug
    });
</pre>
<p><a href="http://apiwiki.twitter.com/Twitter-API-Documentation">Twitter API Wiki / Twitter API Documentation</a> を参考に使う。<br />HTTP MethodがGETならそのクエリになるようにAPIのURLとクエリオブジェクトをtw.getに渡す。<br />HTTP MethodがPOSTなら、APIのURLにcontentとなるオブジェクトを渡す。という感じでかなり適当な作りをしています。<br /><a href="http://code.google.com/p/twigadge/source/browse/trunk/js/twigadge.js">twigadge</a> のようにもう少し機能別に作った方が使いやすくなると思います。</p>
<p>以上で説明のようなそうでないものは終わりですが、以下に簡単なサンプルGreasemonkeyを置いてあります。</p>
<ul>
<li><a href="http://gist.github.com/418274">GreasemonkeyでOAuth認証を手助けするライブラリとそのサンプル</a><br />(猿アイコンのユーザースクリプトから認証とget、postのテストができます。できれば自分のconsumerKeyとか使ってね。)</li>
</ul>
<p><strong>追記</strong></p>
<p>新たにxhrというメソッドを追加しました。(上記ならtw.xhr({})みたいな使い方)<br />xhrメソッドはGM_xmlhttpRequestと同じようにオプションのオブジェクトを引数にとって、APIと通信します。<br />GM_xmlhttpRequestをラップしているので、感覚的にはGM_xmlhttpRequestと同じ感じで使えると思います。<br />一つサンプル</p>
<p><script src="http://gist.github.com/511308.js?file=twitter%20OAuth%20test.user.js"></script></p>
<p>// ↓↓↓↓Your Script↓↓↓↓ の所までがOAuth認証のパネルを作ったりするテンプレート的なものです。<br />OAuth認証が済んでない（tw.isAuthorize()==false)なら途中でreturnされるので、既存コードの上部に<em>// ClientInfomation</em> から <em>// ↓↓↓↓Your Script↓↓↓↓</em>までをコピペして、API周りを修正すれば簡単にOAuth認証に対応できます。(できる限り自分の登録したアプリのclientInfoを使って下さい)<br />当たり前ですが、その時@requireでOAuth.jsの3点セットを読み込んでおいて、インストールし直す必要があります(<strong>@requireが動作するのはインストール時のみ</strong>)<br />APIを叩くときにOAuth認証で得たアクセストークンからシグネチャを作る必要があるので、GM_xmlhttpRequestでそのままAPIを叩いたままでは通りません。そこで今回追加したxhrメソッドの出番です。<br />Getメソッドの場合だとGM_xmlhttpRequestをtw.xhrに変更するだけで既存のコードは通ると思います。<br />POSTメソッドもGM_xmlhttpRequestをxhrメソッドに変えるだけで大体通ると思います。<br />GM_xmlhttpRequestだとdata部分は文字列で書くので、そのまま&#8221;foo=fizz&amp;bar=bazz&#8221;と書く方法と下のようにオブジェクト書く方法が使えます。(オブジェクトの方が効率がいい)</p>
<pre>data : {
    foo:fizz,
    bar:bazz,
}</pre>
<p>サンプルは猿アイコンを右クリックからでるユーザーコマンドで、xhrのそれぞれgetとpostをテストする関数が動作するだけです。</p>
<p>以下リンクまとめ</p>
<p>ライブラリの置き場所。こっちが多分一番新しい。</p>
<dl>
<dt><strong>azu&#8217;s OAuth-for-Greasemonkey at master &#8211; GitHub</strong></dt>
<dd><a title="azu's OAuth-for-Greasemonkey at master - GitHub" href="http://github.com/azu/OAuth-for-Greasemonkey">http://github.com/azu/OAuth-for-Greasemonkey</a></dd>
</dl>
<dl>
<dt>実際にラッパーを使ったGreasemonkey</dt>
<dt><strong>Post Now browsing to Twitter for Greasemonkey</strong></dt>
<dd><a title="Post Now browsing to Twitter for Greasemonkey" href="http://userscripts.org/scripts/show/46441">http://userscripts.org/scripts/show/46441</a></dd>
</dl>
<dl>
<dt>上のGreasemonkeyで使ってるラッパーライブラリとかをまとめたもの</dt>
<dt><strong>OAuth for Greasemonkey for Greasemonkey</strong></dt>
<dd><a title="OAuth for Greasemonkey for Greasemonkey" href="http://userscripts.org/scripts/show/78102">http://userscripts.org/scripts/show/78102</a></dd>
</dl>
<p>&nbsp;</p>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 6332px; width: 1px; height: 1px;">HTTP MethodがGETならそのクエリになるようにAPIのURLとクエリオブジェクトをtw.getに渡す。</div>
]]></content:encoded>
			<wfw:commentRss>http://efcl.info/2010/0610/res1721/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Post Now browsing to TwitterをOAuth認証に対応しました</title>
		<link>http://efcl.info/2010/0609/res1715/</link>
		<comments>http://efcl.info/2010/0609/res1715/#comments</comments>
		<pubDate>Wed, 09 Jun 2010 10:55:01 +0000</pubDate>
		<dc:creator>azu</dc:creator>
				<category><![CDATA[Greasemonkey]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[twitter]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[設定]]></category>

		<guid isPermaLink="false">http://efcl.info/?p=1715</guid>
		<description><![CDATA[Twitterへ今見ているサイトをコメントと共に投稿する「Post Now browsing  to Twitter」がOAuth認証に対応しました。このGreasemonkeyをインストールする前に、以前のバージョン( [...]]]></description>
			<content:encoded><![CDATA[<p>Twitterへ今見ているサイトをコメントと共に投稿する「<a href="http://userscripts.org/scripts/show/46441">Post Now browsing  to Twitter</a>」が<strong>OAuth認証に対応</strong>しました。<br />このGreasemonkeyをインストールする前に、<strong>以前のバージョン(2010年6月より前)のものを入れている場合はそれをアンインストール</strong>してから、改めて「<a href="http://userscripts.org/scripts/show/46441">Post Now browsing to Twitter</a>」をインストールして下さい。(以前と設定の互換が無くなったため設定ごと破棄をお勧めします)<br />ツール→Greasemonkey(もしくはステータスバーの猿アイコン)→ユーザースクリプトの管理→Post Now browsing to  Twitterを選択→✓関連付けられた設定も削除にチェック→アンインストール</p>
<p><span id="more-1715"></span>主な変更点を箇条書きすると以下のような感じです。</p>
<p>変更内容<br />1. OAuth対応<br />BASIC認証が2010年6月末に廃止されるためOAuth認証に対応しました。<br />設定画面から認証できます。<br />BlowsingNow!などの外部サーバを経由するものとは違い、PINコードを使ってGreasemonkeyスクリプト単体でOAuth認証を行います。<br />2. 入力UIがサイトの影響を受けなくなった<br />入力UI自体は以前と同じですが、サイト側CSSの影響を受けることがあったのを改善し、影響を受けないようにしました。<br />3. 設定項目の変更<br />以前と設定項目が変わったため設定の互換性がありません。<br />そのため、<strong>以前のものはアンインストールから</strong>インストールして下さい。</p>
<ol>
<li>短縮URLの選択肢が増えた<br /> 新たにbit.ly(j.mp)、goo.glを追加しました。<br /> bit.ly(j.mp)はAPIの設定も行えます。</li>
<li>Ctrl+Enterでのポスト<br /> デフォルトはEnterで送信ですが、設定のチェックの意味が逆になったという変更。(正直気にすることはない)<br /> 設定でCtrl+Enterでポストするように変更できます。</li>
<li>Twitterアカウントの入力項目廃止<br /> 以前はベーシック認証を使っていたので、Twitterアカウントの入力項目がありましたが、OAuth認証に対応したため廃止しました。</li>
</ol>
<p>OAuth認証は設定の一番下に表示されます。(いつでも認証を解除する事ができます。)<br />4. 短縮URLのリトライ機能<br />短縮URLサービスのサーバにアクセスできないときに(5秒でタイムアウト)、別の短縮URLサービスに切り替えて短縮します。</p>
<p>ぱっとした変化を見ると、OAuthに対応したのと、bit.ly、goo.glなどの短縮URLが使えるようになったぐらいかな。</p>
<h3>OAuth認証の手順</h3>
<p>Greasemonkey 単体でOAuth認証を行うようにしたので手順が少し複雑です。<br />以下の動画を参考にして下さい。</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="640" height="385" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/8oXTuZk5xAk&amp;hl=ja_JP&amp;fs=1&amp;rel=0&amp;hd=1" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="640" height="385" src="http://www.youtube.com/v/8oXTuZk5xAk&amp;hl=ja_JP&amp;fs=1&amp;rel=0&amp;hd=1" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p>文字で書くと、</p>
<ol>
<li>ステータスバーのGreasemonkeyアイコンを右クリック<br />ユーザースクリプトコマンド→Post Now browsing to Twitter Settingから設定画面へ</li>
<li>OAuth認証をまだしてないなら、&#8221;Sign in with Twitter&#8221;ボタンが表示されるのでクリック</li>
<li>OAuth認証ページが開くので、そこへ行き許可(Allow)する。</li>
<li>PINコードが表示されるのでCtrl+Cなどでコピーする。</li>
<li>再び設定画面に戻り、インプットボックスへPINコードをCtrl+V(ペースト)して、Comfirmボタンを押す。</li>
<li>Success!出たら、ページでショートカットキー(デフォルトはCtrl+Shift+Enter)を押し、入力UIを出す。</li>
<li>適当にコメントとかを書いたらEnter(設定でCtrl+Enterに変えられます)でTwitterへポストする</li>
<li>投稿できたらOK</li>
</ol>
<p>5ぐらいまでがOAuth認証の手順で、6以降は投稿する手順。</p>
<p>以下感想<br />OAuthに対応してるGreasemonkeyが皆無で結局自分で色々書かないといけなかった。<br />OAuth.jsとかライブラリを使ってるので結構サイズが大きくなった。<br />OAuthページとかのアイコンがやっつけ感酷いのでアイコン募集中。</p>
<dl>
<dt>機能説明ページ</dt>
<dt><strong>Post Now browsing to Twitter | Web scratch</strong></dt>
<dd><a title="Post Now browsing to Twitter | Web scratch" href="../post-now-browsing-to-twitter/">http://efcl.info/post-now-browsing-to-twitter/</a></dd>
</dl>
<dl>
<dt>スクリプト配布ページ</dt>
<dt><strong>Post Now browsing to Twitter for Greasemonkey</strong></dt>
<dd><a title="Post Now browsing to Twitter for Greasemonkey" href="http://userscripts.org/scripts/show/46441">http://userscripts.org/scripts/show/46441</a></dd>
</dl>
]]></content:encoded>
			<wfw:commentRss>http://efcl.info/2010/0609/res1715/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>GreasemonkeyにGrowlのような通知を呼ぶ機能を加える「Dbus Notify」</title>
		<link>http://efcl.info/2010/0606/res1708/</link>
		<comments>http://efcl.info/2010/0606/res1708/#comments</comments>
		<pubDate>Sat, 05 Jun 2010 17:10:42 +0000</pubDate>
		<dc:creator>azu</dc:creator>
				<category><![CDATA[Greasemonkey]]></category>
		<category><![CDATA[アドオン]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[growl]]></category>

		<guid isPermaLink="false">http://efcl.info/?p=1708</guid>
		<description><![CDATA[紹介するDbus Notify for GreaseMonkeyはGreasemonkeyスクリプトにGrowlのような通知を行うAPIを加えるアドオンです。Greasemonkeyにcallout.notifyというA [...]]]></description>
			<content:encoded><![CDATA[<p>紹介する<strong><a href="https://addons.mozilla.org/ja/firefox/addon/80827/">Dbus Notify for GreaseMonkey</a></strong>はGreasemonkeyスクリプトにGrowlのような通知を行うAPIを加えるアドオンです。<br />Greasemonkeyに<em>callout.notify</em>というAPIを追加するだけのシンプルなアドオンです。<br />使い方は単純でDbus Notify for GreaseMonkeyをインストールして、Greasemonkeyスクリプト内にcallout.notifyのAPIを使った記述を加えるだけで動きます。(当たり前だが、インストールしてない環境だと動かない)</p>
<p><a href="http://efcl.info/wp-content/uploads/2010/06/sshot-2010-06-06-1.png"><img class="alignnone size-full wp-image-1709" title="sshot-2010-06-06-1" src="http://efcl.info/wp-content/uploads/2010/06/sshot-2010-06-06-1.png" alt="" width="247" height="80" /></a></p>
<p>APIは凄くシンプルで、<em>callout.notify(title, message, [options])</em>となっていて、タイトルはそのまま、メッセージはタイトルの下に表示されていて、optionのhrefが設定されている場合はリンクになる。</p>
<p>optionで設定できるのもhrefとiconぐらいで、Greasemonkeyからページの外側に通知を出したいなーって思う人はそれだけを求めるならアドオンなどにしないでこれを使うのもいいかも。</p>
<p>簡単なサンプル</p>
<dl>
<dt><strong>gist: 426775 &#8211; Dbus Notify for GreaseMonkeyのテスト- GitHub</strong></dt>
<dd><a title="gist: 426775 - Dbus Notify for GreaseMonkeyのテスト- GitHub" href="http://gist.github.com/426775">http://gist.github.com/426775</a></dd>
</dl>
<p>APIの解説</p>
<dl>
<dt><strong>lackac&#8217;s callout at master &#8211; GitHub</strong></dt>
<dd><a title="lackac's callout at master - GitHub" href="http://github.com/lackac/callout">http://github.com/lackac/callout</a></dd>
</dl>
<p>Greasemonkeyに何か機能を加えるアドオンって意外と見かけない感じがする。<br /> <a href="http://d.hatena.ne.jp/Griever/20090617/1245256102">Greasemonkey でクリップボードを扱う…悪い方法（？） &#8211; Griever</a> でも言っているように受け口を持つと悪用の可能性も出てくるが、セキュリティ的な影響がでないように狭い範囲で機能追加できれば楽しそうだなーと思った。</p>
]]></content:encoded>
			<wfw:commentRss>http://efcl.info/2010/0606/res1708/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>メディアマーカーで楽しく読書管理する</title>
		<link>http://efcl.info/2010/0509/res1678/</link>
		<comments>http://efcl.info/2010/0509/res1678/#comments</comments>
		<pubDate>Sun, 09 May 2010 08:59:06 +0000</pubDate>
		<dc:creator>azu</dc:creator>
				<category><![CDATA[Greasemonkey]]></category>
		<category><![CDATA[雑記]]></category>
		<category><![CDATA[Evernote]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[クリップボード]]></category>
		<category><![CDATA[ソフトウェア]]></category>
		<category><![CDATA[小説]]></category>
		<category><![CDATA[画像]]></category>

		<guid isPermaLink="false">http://efcl.info/?p=1678</guid>
		<description><![CDATA[メディアマーカーは読書管理を行えるWebサービスで、基本的な読書管理のシステムも優れていながら、Twitterへの連携やiPhone アプリもあるので、とても使い勝手がよいサービスです。今回は自分がどのようにメディアマー [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://mediamarker.net/">メディアマーカー</a>は読書管理を行えるWebサービスで、基本的な読書管理のシステムも優れていながら、Twitterへの連携や<a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=323530721&amp;mt=8">iPhone アプリ</a>もあるので、とても使い勝手がよいサービスです。<br />今回は自分がどのようにメディアマーカーを使っているか、自分流の読書管理方法について書いてみる。</p>
<p>まず、読書管理について自分で心がけているのは</p>
<ol>
<li>どんな本だったかが直ぐ分かること</li>
<li>読書した本の登録が面倒くさくならないこと</li>
<li>読書管理を楽しめること</li>
</ol>
<p>これを目標にしていろいろな読書管理方法を試してきました。<br />1のように後から見返したときにどんな本だったかを知るために読書した本を管理しているところが大きいので、以前はどんな内容だったかをレビュー形式で書いたりしていました。<br />しかし、本のレビューみたいに詳しく 書いていくと面倒臭さが増し2や3を破ってしまう事になり、あまり長くは続きませんでした。<br />(レビュー書く事が全く苦ではない人はブログや<a href="http://www.forest.impress.co.jp/lib/home/house/hobby/shihonkanrip.html">私本管理Plus</a>を使って書きためるのもいいかもしれません)</p>
<p>後から見たときに本の内容を簡単に思い出す方法を考えていて思いついたのが、<strong>本の内容を貼り付けてしまえばいい</strong>と考えました。<br />つまり、本を読んでいるときに小説なら気になったフレーズ、漫画なら気になったコマをそのまま貼り付けてしまえばいい！<br />それを実現するためにとった方法は、本をカメラで撮影して画像にしてしまうという方法。<br />漫画などはこの方法で気になった部分を画像にできるし、小説はちょっとカメラに精度が必要となってしまうので、できなかった場合はiPhoneで気になったフレーズを書き写して、メディアマーカーiPhoneアプリで先に登録しておくといい。<br />最近流行ってる書籍の電子化で、既に電子化したものをビューアーで読んでいる場合はスクリーンショット機能を使って気になった部分を画像として保存しておく。<br />そうしてできた画像をDropboxのPublicフォルダに入れておいて外部からも参照できるようにしておく。<br />後は、メディアマーカーに数行のレビュー(レビューとはいえないレベル)とDropboxにある画像を貼り付けて画像を表示すれば、後から見たときに直ぐ内容が分かって便利になる。</p>
<div>
<dl id="attachment_1679">
<dt><a href="http://efcl.info/wp-content/uploads/2010/05/sshot-2010-05-09-2.png"><img title="sshot-2010-05-09-2" src="http://efcl.info/wp-content/uploads/2010/05/sshot-2010-05-09-2-300x198.png" alt="" width="300" height="198" /></a></dt>
<dd>簡単な書籍の内容についてと画像</dd>
</dl>
</div>
<p>この気になった部分を画像にして貼り付けるのは意外と楽しくて、かつ撮影してそこで直ぐ完結するので手間があまりかからない。特に電子化している場合はスクリーンショットを取るだけなので一瞬で終わる。<br />また画像を貼り付けるときに、Dropboxに一つの書籍について複数の画像がある場合、エクスプローラの&#8221;Copy Public  Link&#8221;だと一個づつしかURLをコピーできないし、Imgタグで囲うのが面倒なのでコマンドラインで動くバッチのようなものを作る。</p>
<ul>
<li><a href="http://gist.github.com/raw/395018/259d0871121ff326e0bba042ef228afa4d60bce4/Program.cs">Program.cs</a></li>
</ul>
<p>このProgram.csのdropboxPath部分を自分のDropbox環境に合わせてからコンパイルする。<br />csc.exeで以下のよう に指定するとコンパイルできる。(csc.exeの場所は.NETのどっかにあるかと、もしくはVisual Studio使うとか)</p>
<pre>&#62;C:\Windows\Microsoft.NET\Framework\v3.5\csc.exe Program.cs
</pre>
<p>コ ンパイルしてできたexeファイルにD&amp;Dや送るからファイルを渡すと渡したファイルのURLをimgタグにしたものがクリップボードにコピーさ れるので、それをメディアマーカーに貼り付けるだけでよくなる。</p>
<p>さらにメディアマーカーは書籍登録時にタグをつけることができ、後から見返すときにそれらの情報をつけておくと分かりやすくなるため、タグ付けを補助するGreasemonkeyを導入する。</p>
<p><a href="http://efcl.info/wp-content/uploads/2010/05/sshot-2010-05-09-1.png"><img title="sshot-2010-05-09-1" src="http://efcl.info/wp-content/uploads/2010/05/sshot-2010-05-09-1-300x48.png" alt="" width="300" height="48" /></a></p>
<ul>
<li><a title="MediaMaker  with Amazon tags" href="http://userscripts.org/scripts/show/67298">MediaMaker with Amazon tags</a></li>
<li><a title="MediaMaker  with booklog tags" href="http://userscripts.org/scripts/show/76273">MediaMaker with booklog tags</a></li>
</ul>
<p>それぞれ、登録時にAmazonとbooklogにつけられている書籍のタグを表示してくれるので、クリックするだけで書籍に関係あるタグを追加できる。</p>
<p>補助ツールを使うことで書籍の登録の面倒臭さも低減されるし、何よりも書籍の内容を画像で表示するのは思っている以上に楽しい事だと思う。<strong><br />読書メモに写真を使う！</strong></p>
<p>evernote+iPhoneで似ている事をやっている人もいます。<br />やっぱりカメラで写真を撮るのと書き出すのは圧倒的に手間が違うし、いつも持ち歩いてるiPhoneでもカメラは何とかなることが多いので面倒な手間が結構減ります。</p>
<dl>
<dt><strong>Blog  Forest: 読書メモの作成にevernoteを使う</strong></dt>
<dd><a title="Blog  Forest: 読書メモの作成にevernoteを使う" href="http://morimorishop.com/2009/03/evernote_2.html">http://morimorishop.com/2009/03/evernote_2.html</a></dd>
</dl>
<p>evernoteなら画像のOCR機能があるので検索できるという淡い期待もありますが、 Amazonから情報を引っ張れたり他とは関係の情報を切り離して見られるメディアマーカーのようなサービスで書籍を管理した方が自分にとっては便利かなーと思う。<br />後から見返したときに楽しいというのも大きな点。<br />あくまでevernte+写真はメモにして、そこから文字を書き出すというのも一つの手であると思います。</p>
<p>以上。</p>
<p>おまけ、Dropboxの画像をPublic経由で表示してるので少し重たくなってしまう。<br />表示する画像をローカルのものと差し替えるGreasemonkey</p>
<pre>@include
originSrc
newSrc
の三カ所を書き換える必要がある。
</pre>
<ul>
<li><a href="http://gist.github.com/395040">Mediamaker img local replace.user.js</a></li>
</ul>
<p>セキュリティの理由からpref.jsに以下を追加してメディアマーカーからローカルの画像を表示できるようにしておかないと機能しない。</p>
<pre>user_pref("capability.policy.localfilelinks.checkloaduri.enabled", "allAccess");
user_pref("capability.policy.localfilelinks.sites", "http://mediamarker.net");
user_pref("capability.policy.policynames", "localfilelinks");
</pre>
<dl>
<dt><strong>Muses Factory » Firefoxでcheckloaduri.enabledの設定が反映されない</strong></dt>
<dd><a title="Muses Factory » Firefoxでcheckloaduri.enabledの設定が反映されない" href="http://reva.s28.xrea.com/archives/38.html">http://reva.s28.xrea.com/archives/38.html</a></dd>
</dl>
]]></content:encoded>
			<wfw:commentRss>http://efcl.info/2010/0509/res1678/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Greasemonkeyでサイト既存CSSの影響を受けないポップアップパネルを作る方法</title>
		<link>http://efcl.info/2010/0328/res1636/</link>
		<comments>http://efcl.info/2010/0328/res1636/#comments</comments>
		<pubDate>Sat, 27 Mar 2010 16:11:20 +0000</pubDate>
		<dc:creator>azu</dc:creator>
				<category><![CDATA[Greasemonkey]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[ニコニコ動画]]></category>
		<category><![CDATA[ポップアップ]]></category>

		<guid isPermaLink="false">http://efcl.info/?p=1636</guid>
		<description><![CDATA[Greasemonkeyは各サイトでユーザースクリプトを実行できて便利ですが、ある要素を挿入したときにそれがサイトに元々書いてあるCSSの影響を受けてしまうことがあります。
 一つのサイトならまだしもhttp*で動くよう [...]]]></description>
			<content:encoded><![CDATA[<p>Greasemonkeyは各サイトでユーザースクリプトを実行できて便利ですが、ある要素を挿入したときにそれがサイトに元々書いてあるCSSの影響を受けてしまうことがあります。<br />
 一つのサイトならまだしもhttp*で動くようなものだと対応仕切れないのでiframeを使いサイトに書いてあるCSSの影響を受けないパネルを作る。<br />
 iframeの中はそのサイトにあるCSSの影響を受けないので、iframe内にGreasemonkeyで表示したいものを置けば影響を受けないものが作成できる。<br />
 makeFrameというのが今回の主題。</p>
<pre class="brush:javascript;">makeFrame(gotFrame1);
makeFrame(gotFrame2);

function gotFrame1(iframe, win, doc) {
  iframe.height = "350";
  iframe.width = "500";
  iframe.style.position = "fixed";
  iframe.style.bottom = iframe.style.left = "0";
  doc.body.innerHTML += "Frame1ですね。"
}
function gotFrame2(iframe, win, doc) {
  iframe.height = "350";
  iframe.width = "500";
  iframe.style.position = "fixed";
  iframe.style.bottom = iframe.style.right = "0";
  iframe.style.backgroundColor = "#ddd";
  doc.body.innerHTML += "Frame2ですよ。"
}
// Creates a new iframe and attaches it to the DOM, waits for it to load, tests
// that we did not hit https://bugzilla.mozilla.org/show_bug.cgi?id=295813 nor
// https://bugzilla.mozilla.org/show_bug.cgi?id=388714 (and retries otherwise),
// to finally call the provided done callback, passing the iframe, its window
// and document. (The optional name parameter, if provided, will be used to name
// the iframe in window.frames, or be created as "pane-1" onwards, otherwise.)
/*
    var cacllback = function(iframe, win, doc){

    }
    makeFrame(cacllback);
    makeFrame(cacllback , "frameName");
    makeFrame(cacllback , "frameName" , true);// debug mode
*/
function makeFrame(callback/*(iframeTag, window, document)*/, name, debug) {
    function testInvasion() {
        iframe.removeEventListener("load", done, true);
        var message = ((new Date) - load.start) + "ms passed, ";
        try { // probe for security violation error, in case mozilla struck a bug
            var url = unsafeWindow.frames&#91;framename&#93;.location.href;
            message += url == "about:blank" ? "but we got the right document." : "and we incorrectly loaded " + url;
            if (debug) console.log(message);
            done();
        }
        catch(e) {
            if (console &amp;&amp; console.error &amp;&amp; console.trace) {
                console.error(e);
                console.trace();
            }
            if (debug) console.log(message + "and our iframe was invaded. Trying again!");
            document.body.removeChild(iframe);
            makeFrame(callback, name);
        }
    }
    function done() {
        clearTimeout(load.timeout);
        if (debug) console.log("IFrame %x load event after %d ms", framename, (new Date) - load.start);
        var win = unsafeWindow.frames&#91;framename&#93;;
        var doc = iframe.contentWindow.document;
        // 苦し紛れのエスケープ
        var esframeName = "'"+framename+"'";
        // 自分自身のiframeを閉じるボタン
        var xImg = doc.createElement("img");
        xImg.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAATElEQVQoka2RSQ4AIAgD+f+jp96M0aq49AgdUiB0qZCkONQ/EBAwDOrrU7A1uZqN2hodtNwRqNdz0VOg62+jzuDUcVzkf+/I6h28UQHjW25Gob5AIAAAAABJRU5ErkJggg=="
        xImg.setAttribute("onclick", "parent.document.getElementsByName("+esframeName+")&#91;0&#93;.style.display='none';");
        xImg.setAttribute("style","background-color:red;border:3px;position:fixed;top:0px;right:0px");
        doc.body.appendChild(xImg);
        callback(iframe, win, doc);
    }
    var iframe = document.createElement("iframe");
    var framename = iframe.name = typeof name != "undefined" ? name : ("pane" + (makeFrame.id = (makeFrame.id || 0) - 1));
    iframe.setAttribute("style", "overflow:auto;z-index:18999; border:0; margin:0; padding:0;top:auto; right:auto; bottom:auto; left:auto;background-color:#fff");
    iframe.src = "about:blank";
    iframe.addEventListener("load", done, true);
    var frames = makeFrame.data || {};
    var load = frames&#91;framename&#93; || {
        start: new Date,
        sleepFor: 400
    };
    load.timeout = setTimeout(testInvasion, load.sleepFor);
    load.sleepFor *= 1.5;
    frames&#91;framename&#93; = load;
    makeFrame.data = frames;
    document.body.appendChild(iframe);
}
</pre>
<ul>
<li><a href="http://gist.github.com/raw/346174/e76861b73952461419eebe2335ee9c13b8344b40/panel.user.js">panel.user.js</a></li>
</ul>
<p>makeFrame関数は(callback ,[フレームのname , debugモード])という引数なので、gotFrame1という関数(callback)を引数にしてmakeFrameを読んでいる。 callbackには(iframe, iframeのwindow, iframeのdocument)が入ってるいるので、iframeのstyleをいじればパネルの色やサイズ、表示する場所などを指定できる。 上記のソースだと左下にframe1、右下にframe2が表示できる。</p>
<p><a href="http://efcl.info/wp-content/uploads/2010/03/sshot-2010-03-28-1.png"><img class="aligncenter size-medium wp-image-1638" title="sshot-2010-03-28-1" src="http://efcl.info/wp-content/uploads/2010/03/sshot-2010-03-28-1-300x219.png" alt="表示例" width="300" height="219" /></a></p>
<p><strong>元ネタからの改良点 </strong></p>
<ul>
<li>パネルを閉じるボタンの追加</li>
</ul>
<p>それぞれのパネル右上に表示されているxボタンは、パネルを閉じるボタンとして機能している。 innerHTMLが+になってるのはiframe内にxボタンを追加しているため。</p>
<ul>
<li>Flashより上部に表示</li>
</ul>
<p>パネルなのでFlashがあってもその上に表示できるようにした方が動作的にはいいと思うのでCSSを少し修正した。<br />
 要素をFlashより上部する方法は以前書いた<a href="http://efcl.info/2008/0622/res227/">Flashよりも前面にポップアップを表示する</a>か<a href="http://d.hatena.ne.jp/gifnksm/20100129/1264797620">Firefox 3.6でFlashの上に position: fixed; な要素を表示できなくなった件 &#8211; なんとなく目記</a>を見るといい。<br />
 Firefox3.6では挙動が変わったので注意。<br />
 Flashより上に表示するには&#8221;position: fixed; <span class="keyword">overflow</span>: auto;&#8221;とした要素であり、以下の条件を満たす必要がある。</p>
<ol>
<li>透過でない(Opacityを使わない or 背景色を無指定にしない)</li>
<li>CSS3?(-moz-)系のstyleを使わない。(-moz-box-shadowなどは陰の部分だけFlashより後ろに表示されてしまう<br />
 角丸border-radiusなどは要素に適応されるので要素全体がFlashより後ろに表示される。)</li>
</ol>
<p>このmakeFrameを使ったサンプルGreasemonkeyとして<br />
 ニコニコ動画再生ページからその動画のマイリストコメントをパネル上で参照できる</p>
<dl>
<dt><strong>Nico MylistComments for Greasemonkey</strong></dt>
<dd><a title="Nico MylistComments for Greasemonkey" href="http://userscripts.org/scripts/show/72319">http://userscripts.org/scripts/show/72319</a></dd>
</dl>
<p>を作ってみた。</p>
<p>元ネタ(元ネタのサンプルはtypoがあるので、そのままだと動かない)</p>
<dl>
<dt><strong>HTML Injection Tips &#8211; greasemonkey &#8211; GitHub</strong></dt>
<dd><a title="HTML Injection Tips - greasemonkey - GitHub" href="http://wiki.github.com/Martii/greasemonkey/html-injection-tips">http://wiki.github.com/Martii/greasemonkey/html-injection-tips</a></dd>
</dl>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">makeFrame</div>
]]></content:encoded>
			<wfw:commentRss>http://efcl.info/2010/0328/res1636/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>gistへGreasemonkeyを貼るときに自動でファイル名を入力するスクリプト</title>
		<link>http://efcl.info/2010/0320/res1614/</link>
		<comments>http://efcl.info/2010/0320/res1614/#comments</comments>
		<pubDate>Sat, 20 Mar 2010 14:31:57 +0000</pubDate>
		<dc:creator>azu</dc:creator>
				<category><![CDATA[Greasemonkey]]></category>
		<category><![CDATA[Git]]></category>

		<guid isPermaLink="false">http://efcl.info/?p=1614</guid>
		<description><![CDATA[GistにGreasemonkeyスクリプトをペーストする時に、自動でファイル名を入力してくれるGreasemonkeyを書きました。
 入力されるファイル名は@nameの要素+.user.jsとなるので、コードをペース [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://gist.github.com/">Gist</a>にGreasemonkeyスクリプトをペーストする時に、自動でファイル名を入力してくれるGreasemonkeyを書きました。<br />
 入力されるファイル名は@nameの要素+.user.jsとなるので、コードをペーストするだけでRawからインストールできるようになります。</p>
<pre id="source">// @name           gist fill in fileName automatically</pre>
<p>という感じで書いてあったなら、gist fill in fileName automatically.user.jsというファイル名が勝手に入力されます。</p>
<p>Add another file…で追加したテキストエリアにも対応させてあります。</p>
<p><a href="http://efcl.info/wp-content/uploads/2010/03/sshot-2010-03-20-1.png"><img class="aligncenter size-medium wp-image-1615" title="sshot-2010-03-20-1" src="http://efcl.info/wp-content/uploads/2010/03/sshot-2010-03-20-1-300x226.png" alt="" width="300" height="226" /></a></p>
<ul>
<li><a href="http://userscripts.org/scripts/show/71914">gist fill in fileName automatically</a></li>
</ul>
<p>gistのトップページ( http://gist.github.com/ ) <span style="text-decoration: line-through;">と編集ページ</span>で動作します。</p>
<dl>
<dt><strong>gist fill in fileName automatically for Greasemonkey</strong></dt>
<dd><a title="gist fill in fileName automatically for Greasemonkey" href="http://userscripts.org/scripts/show/71914">http://userscripts.org/scripts/show/71914</a></dd>
</dl>
<p><span id="more-1614"></span><strong>技術的な動作<br />
 </strong></p>
<pre class="brush:javascript;">var fileExtension = ".user.js";
var addAutoEvent = function(fileID){
    var editArea = document.getElementsByName("file_contents&#91;gistfile"+fileID+"&#93;")&#91;0&#93;;
    var editFileName = document.getElementsByName("file_name&#91;gistfile"+fileID+"&#93;")&#91;0&#93;;
    editArea.addEventListener("paste", function(e){
        // この瞬間はまだtargetのvalueが空である。
        var obj = e.target;
        obj.addEventListener('input', function(evt){
            // クリップボードの内容がinputされた
            evt.currentTarget.removeEventListener(evt.type, arguments.callee, false);
            var pasteValue = obj.value;
            var m = pasteValue.match(/@name\s+(&#91;\w\s&#93;+)\n/i);
                m = m &amp;&amp; m&#91;1&#93;;
            if(m &amp;&amp; !editFileName.value){
               m = m.replace(/\s+$/ , "");
               // m = m.replace(" " , "_" , "g");// 第３引数 繰り返し
               editFileName.value = m + fileExtension;
               // console.log(m + fileExtension);
               editFileName.focus();
            }
        }, false);
    }, false);
};
// 追加されたテキストエリアにもイベントをつける
var addButton = document.getElementById("add-gist");
addButton.addEventListener("click",function(){
    document.getElementById("files").addEventListener("DOMNodeInserted",function(e) {
        // テキストエリアが追加された
        e.currentTarget.removeEventListener(e.type, arguments.callee, false);
        var editAreaLength = document.getElementsByClassName("file").length;
        addAutoEvent(editAreaLength);
    } ,false);
}  ,false);
// 最初から表示されているテキストエリアにイベントをつける
addAutoEvent(1);</pre>
<p><a href="http://twitter.com/Griever2">Griever</a>さんが書いてくれた<a href="http://twitter.com/Griever2/statuses/10767886150">ヒント</a>を元にしてます。</p>
<pre>addEventListener("paste" , ...);
</pre>
<p>でペーストに対してイベントリスナーを付け加えることができるのですが、この瞬間に</p>
<pre>e.target.value
</pre>
<p>としても内容は空(もしくは元からそこに書いてあったもの)となり、期待するテキストエリアの文字列は取得できません。<br />
 なので、pasteのイベントリスナー内で</p>
<pre>    editArea.addEventListener("paste", function(e){
        // この瞬間はまだtargetのvalueが空である。
        var obj = e.target;
        obj.addEventListener('input', function(evt){...
</pre>
<p>のように、inputイベントをつけると、実際に文字列がテキストエリアに入った瞬間にinputイベントが動くので、<br />
 inputイベント内で</p>
<pre>obj.value
</pre>
<p>を見るととテキストエリア内の文字列が取得できます。<br />
 (ペーストされた部分のみを扱いたい場合は<a href="http://gist.github.com/338606">gist: 338606 &#8211; GitHub</a>を参考に)<br />
 後は、inputのイベントはそのまま残っているとジャマなので自身を参照してremoveEventListenerしておいて、やりたかったことをやる。</p>
<p>追加されたテキストエリアにもイベントを加えるのもほとんど同じ形をしています。<br />
 まず、addボタンに対してclickイベント付け加えます。<br />
 gistでは元々addボタンに対してclickイベントがつけられていて、複数のclickイベントがあったときはつけられた順に実行されるので、元々あったclickイベント→Greasemonkeyでつけたclickイベントの順に実行される。<br />
 しかし、元々あったclickイベントではxmlhttprequestを使って非同期にテキストエリアを追加しているので、<br />
 テキストエリアを追加(gist側)→テキストエリアにイベントをつける(Greasemonkey) ということをやりたい場合にテキストエリアが追加されるまで待たないといけません。<br />
 JavascriptではそういうときにsetIntervalで回したり、watch(IEでは動かない)でプロパティを監視したりできると思いますが、<br />
 今回は<a href="https://developer.mozilla.org/ja/DOM_Events#DOMNodeInserted">DOMNodeInserted</a>を使ってDOMに要素が追加されたら発火するイベントをつけることにした。</p>
<pre>var addButton = document.getElementById("add-gist");
addButton.addEventListener("click",function(){
    document.getElementById("files").addEventListener("DOMNodeInserted",function(e) { ...
</pre>
<p>#files 要素内に新しいテキストエリアが追加されると、DOMNodeInsertedイベントが発火するので、非同期で追加されたテキストエリアに関しても、最初に作成したpasteイベントをつけることができる。<br />
 この時に前と同様にDOMNodeInserted内で自分自身をremoveEventListenerしておく。<br />
 DOMNodeInsertedとかドキュメントがあんまり見当たらない。</p>
<dl>
<dt><strong>DOMNodeInserted event JavaScript</strong></dt>
<dd><a title="DOMNodeInserted event JavaScript" href="http://help.dottoro.com/ljmcxjla.php">http://help.dottoro.com/ljmcxjla.php</a></dd>
</dl>
<dl>
<dt><strong>DOM Mutation Events は非同期にして使おう &#8211; JavaScriptで遊ぶよ &#8211; g:javascript</strong></dt>
<dd><a title="DOM Mutation Events は非同期にして使おう - JavaScriptで遊ぶよ - g:javascript" href="http://javascript.g.hatena.ne.jp/edvakf/20100204/1265312155">http://javascript.g.hatena.ne.jp/edvakf/20100204/1265312155</a></dd>
</dl>
<p>偶然にもpaste→input、click→DOMNodeInsertedという感じで両方とも、イベントリスナー内でaddEventListenerという形になった。<br />
 以上</p>
]]></content:encoded>
			<wfw:commentRss>http://efcl.info/2010/0320/res1614/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>twilogに日付毎のページへ飛ぶリンクをつけるGreasemonkey</title>
		<link>http://efcl.info/2010/0310/res1595/</link>
		<comments>http://efcl.info/2010/0310/res1595/#comments</comments>
		<pubDate>Wed, 10 Mar 2010 05:35:41 +0000</pubDate>
		<dc:creator>azu</dc:creator>
				<category><![CDATA[Greasemonkey]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://efcl.info/?p=1595</guid>
		<description><![CDATA[twilogの日付の隣あたりに日付毎のページへ飛ぶリンクをつけるGreasemonkeyです。
 要は下のスクリーンショットみたいに日付ページへ飛ぶリンクをつけるだけです。


twilog date linker

カ [...]]]></description>
			<content:encoded><![CDATA[<p>twilogの日付の隣あたりに日付毎のページへ飛ぶリンクをつけるGreasemonkeyです。<br />
 要は下のスクリーンショットみたいに日付ページへ飛ぶリンクをつけるだけです。</p>
<p><a href="http://efcl.info/wp-content/uploads/2010/03/sshot-2010-03-07-1.png"><img class="aligncenter size-medium wp-image-1596" title="sshot-2010-03-07-1" src="http://efcl.info/wp-content/uploads/2010/03/sshot-2010-03-07-1-299x35.png" alt="" width="299" height="35" /></a></p>
<ul>
<li><a href="http://userscripts.org/scripts/show/70683">twilog date linker</a></li>
</ul>
<p>カッとなってつけたけどデフォルトでないのが意外。</p>
<dl>
<dt><strong>twilog date linker for Greasemonkey</strong></dt>
<dd><a title="twilog date linker for Greasemonkey" href="http://userscripts.org/scripts/show/70683">http://userscripts.org/scripts/show/70683</a></dd>
</dl>
]]></content:encoded>
			<wfw:commentRss>http://efcl.info/2010/0310/res1595/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Twitterへ見ているサイトを投稿するGreasemonkey「Post Now browsing to Twitter」更新</title>
		<link>http://efcl.info/2010/0221/res1569/</link>
		<comments>http://efcl.info/2010/0221/res1569/#comments</comments>
		<pubDate>Sun, 21 Feb 2010 14:33:45 +0000</pubDate>
		<dc:creator>azu</dc:creator>
				<category><![CDATA[Greasemonkey]]></category>
		<category><![CDATA[twitter]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[ショートカット]]></category>

		<guid isPermaLink="false">http://efcl.info/?p=1569</guid>
		<description><![CDATA[今見ているサイトのURLをコメントとともにTwitterに投稿するGreasemonkeyであるPost Now browsing to Twitterを更新したので更新内容について。 どんな感じのものかは以前の記事を見 [...]]]></description>
			<content:encoded><![CDATA[<p>今見ているサイトのURLをコメントとともにTwitterに投稿するGreasemonkeyである<a href="http://userscripts.org/scripts/show/46441">Post Now browsing to Twitter</a>を更新したので更新内容について。<br /> どんな感じのものかは以前の記事を見てください。</p>
<dl>
<dt><strong>今見ているサイトをTwitterに投稿する「Post Now browsing to Twitter」 | Web scratch</strong></dt>
<dd><a title="今見ているサイトをTwitterに投稿する「Post Now browsing to Twitter」 | Web scratch" href="http://efcl.info/2009/0929/res1369/">http://efcl.info/2009/0929/res1369/</a></dd>
</dl>
<h3>更新点</h3>
<ul>
<li>入力UIの変更</li>
</ul>
<p><a href="http://efcl.info/wp-content/uploads/2010/02/sshot-2010-02-21-1.png"><img class="size-medium wp-image-1570 alignnone" title="sshot-2010-02-21-1" src="http://efcl.info/wp-content/uploads/2010/02/sshot-2010-02-21-1-300x58.png" alt="" width="300" height="58" /></a></p>
<p>今まではコメントの入力を通常のプロンプトで行っていましたが、入力用のテキストエリアをショートカットを押した際に表示する用にしました。<br /> コメントするUIとポストした際の並びに統一感がでたのと、コメントの入力に対してリアルタイムで合計の文字数をカウントアップします。(140文字をオーバーしても自動でタイトルを切って140文字に納めるのでカウントアップにしてます。)<br /><strong>コメント入力後(未記入でもOK)Ctrl+Enterを押すとTwitterにポストします。</strong><br /> <strong>コメント入力をキャンセルしたい場合はESCキーを押すことでキャンセルできます。</strong></p>
<p>また、promptの時とは違いテキストエリアで入力するので、<a href="http://e-words.jp/w/E383A2E383BCE38380E383ABE38380E382A4E382A2E383ADE382B0.html">モーダルダイアログ</a>のように入力欄が表示されているとき、ページ上のテキストを選択できないということがなくなります。</p>
<p>後は細かい修正。<br /> インストールは<a href="http://userscripts.org/scripts/show/46441">こちら</a>から</p>
<dl>
<dt><strong>Post Now browsing to Twitter</strong></dt>
<dd><a title="Post Now browsing to Twitter" href="http://userscripts.org/scripts/show/46441">http://userscripts.org/scripts/show/46441</a></dd>
</dl>
]]></content:encoded>
			<wfw:commentRss>http://efcl.info/2010/0221/res1569/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ニコニコ動画のマイリストページに共起タグクラウドを表示するGreasemonkey</title>
		<link>http://efcl.info/2010/0113/res1531/</link>
		<comments>http://efcl.info/2010/0113/res1531/#comments</comments>
		<pubDate>Tue, 12 Jan 2010 16:32:10 +0000</pubDate>
		<dc:creator>azu</dc:creator>
				<category><![CDATA[Greasemonkey]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[ニコニコ動画]]></category>

		<guid isPermaLink="false">http://efcl.info/?p=1531</guid>
		<description><![CDATA[
ニコニコ動画の検索ページに共起タグクラウドを表示するChrome用ユーザスクリプト作ってみた &#8211; あたご型護衛艦日記
http://d.hatena.ne.jp/aTaGo/20100112/1263285 [...]]]></description>
			<content:encoded><![CDATA[<dl>
<dt><strong>ニコニコ動画の検索ページに共起タグクラウドを表示するChrome用ユーザスクリプト作ってみた &#8211; あたご型護衛艦日記</strong></dt>
<dd><a title="ニコニコ動画の検索ページに共起タグクラウドを表示するChrome用ユーザスクリプト作ってみた - あたご型護衛艦日記" href="http://d.hatena.ne.jp/aTaGo/20100112/1263285275">http://d.hatena.ne.jp/aTaGo/20100112/1263285275</a></dd>
</dl>
<p>という便利なものがあったので公開マイリストページにもタグクラウドを表示するGreasemonkeyを書いてみた。</p>
<p><a href="http://efcl.info/wp-content/uploads/2010/01/sshot-2010-01-13-1.png"><img class="alignnone size-medium wp-image-1533" title="sshot-2010-01-13-" src="http://efcl.info/wp-content/uploads/2010/01/sshot-2010-01-13-1-300x129.png" alt="" width="300" height="129" /></a></p>
<ul>
<li><a href="http://gist.github.com/raw/275310/b111171970596648f75432d0febf16e837a6eb0c/Mylist%20TagCloud.user.js">Mylist TagCloud</a></li>
</ul>
<pre class="brush:javascript;">// ==UserScript==
// @name           Mylist TagCloud
// @namespace      http://efcl.info/
// @include        http://www.nicovideo.jp/mylist/*
// ==/UserScript==
evalInPage(function(){

	var as = my.currentItems
	var ids = "";
	if(as.length &#62; 1){
		insertcallback();
		for(var i = 0; i &#60; as.length; i++){
			var smid = as&#91;i&#93;.item_data.video_id;
			ids += smid + "+";
		}
		//console.log(ids);
		var uri = "http://nicotag.sakura.ne.jp/getcollocationtag.php?smid=" + ids + "&amp;rand=" + Math.floor(Math.random()*1000);;
		calljsonp(uri);
	}

function calljsonp(uri){
	var scr = document.createElement('script');
	scr.type = "text/javascript";
	scr.src = uri;
	document.body.appendChild(scr);
}

function insertcallback(){
	var callscr = document.createElement('script');
	callscr.type = "text/javascript";
	callscr.text = ''
			+ 'function callback(val){'
			+ 'var footer = document.querySelectorAll("div#SYS_box_mylist_body");'
			+ 'var foot = footer&#91;0&#93;;'
			+ 'var div = document.createElement("div");'
			+ 'div.innerHTML = val;'
			+ 'foot.parentNode.insertBefore(div, foot);'
			+ '}';
	document.body.appendChild(callscr);
}
})
function evalInPage(fun) {
  location.href = "javascript:void (" + fun + ")()";
}
</pre>
<p>JSONPでしか取得できないような気がしたので、ほぼそのままな感じ。<br />
 確かめてないけどChromeでも動くかも。</p>
]]></content:encoded>
			<wfw:commentRss>http://efcl.info/2010/0113/res1531/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Googleの検索結果画面にTwitter検索の結果も表示するGreasemonkey(焼き直し)</title>
		<link>http://efcl.info/2010/0103/res1522/</link>
		<comments>http://efcl.info/2010/0103/res1522/#comments</comments>
		<pubDate>Sun, 03 Jan 2010 10:27:28 +0000</pubDate>
		<dc:creator>azu</dc:creator>
				<category><![CDATA[Greasemonkey]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://efcl.info/?p=1522</guid>
		<description><![CDATA[Googleの検索結果画面にTwitter検索の結果も表示するGreasemonkeyスクリプトです。
以前、Twitter Search Results on Google for Greasemonkey [ htt [...]]]></description>
			<content:encoded><![CDATA[<p>Googleの検索結果画面に<a href="http://pcod.no-ip.org/yats/">Twitter検索</a>の結果も表示するGreasemonkeyスクリプトです。</p>
<p>以前、Twitter Search Results on Google for Greasemonkey [ http://userscripts.org/scripts/show/43451 ]を元に書いたものの焼き直しです。</p>
<dl>
<dt><strong>Googleの検索結果にtwitter検索の結果を同時に表示するGreasemonkey | Web scratch</strong></dt>
<dd>http://efcl.info/2009/0826/res1266/</dd>
<dd>
 </dd>
</dl>
<p>また一から書き直してみた。<br />
 機能はほとんど変わってません。<br />
 <a href="http://efcl.info/wp-content/uploads/2010/01/sshot-2010-01-03-1.png"><img class="alignnone size-medium wp-image-1523" title="sshot-2010-01-03-1" src="http://efcl.info/wp-content/uploads/2010/01/sshot-2010-01-03-1-300x120.png" alt="" width="300" height="120" /></a></p>
<p>少し似た目が変わったのと、その場でTwitter検索の結果を継ぎ足せるようにしたぐらいです。<br />
 インストールは<a href="http://userscripts.org/scripts/show/65540">こちら</a>から</p>
<dl>
<dt><strong>Twitter search(ja) result on Google for Greasemonkey</strong></dt>
<dd><a title="Twitter search(ja) result on Google for Greasemonkey" href="http://userscripts.org/scripts/show/65540">http://userscripts.org/scripts/show/65540</a></dd>
</dl>
]]></content:encoded>
			<wfw:commentRss>http://efcl.info/2010/0103/res1522/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
<!-- This Quick Cache file was built for (  efcl.info/category/firefox/greasemonkey/feed/ ) in 0.84539 seconds, on Sep 3rd, 2010 at 6:07 am UTC. -->
<!-- This Quick Cache file will automatically expire ( and be re-built automatically ) on Sep 3rd, 2010 at 7:07 am UTC -->