2008/01/31


追記1
タイトルがうまく取れていないっぽい。
あとリンクも見付けれてない...orz
時間見つけて直します。
一応、現在見ているページならポスト出来るみたい。

追記2
直した。

Tomblooが良く出来てて素晴らしい。
Tomblooは現在のコンテンツに合わせてポストする形式を集約し、コンテキストメニューから集約したアイテムをポスト先へ送信する、すばらしい拡張です。
現状、標準のポスト先としてFlickr、Tumblr等がポスト先として選択出来る様になっています。
Minibufferフリークとしては、これをMinibufferから使えないのは物悲しい!
ってことで適当にハックしてMinibufferからpostする方法をご紹介します。
とは言っても今日ご紹介する方法は、ブックマークレットでもなければグリースモンキーでもありません。Firefoxの拡張を弄る事になりますので自己責任でお願い致します。
まずTomblooの拡張が格納されているフォルダ、
${FIREFOX_PROFILE}/extensions/tombloo@brasil.to/chrome/content/library/Mozaic.html
を開き <script type="text/javascript" src="../library/20_Tumblr.js"></script>
<script type="text/javascript" src="../library/30_Tombloo.Service.js"></script>
<style>
となっている部分に <script type="text/javascript" src="../library/20_Tumblr.js"></script>
<script type="text/javascript" src="../library/30_Tombloo.Service.js"></script>
<script type="text/javascript" src="../library/32_Minibuffer.js"></script>
<style>
と32_Minibuffer.jsを追加します。次に
${FIREFOX_PROFILE}/extensions/tombloo@brasil.to/chrome/content/library/32_Minibuffer.js
を作成し、以下のソースをペーストして保存します。
var GreasemonkeyServiceClass = Components.classes["@greasemonkey.mozdev.org/greasemonkey-service;1"];
log(GreasemonkeyServiceClass);
if (GreasemonkeyServiceClass) {
    function update(target, src){
        for(var key in src)
            target[key] = src[key];
        
        return target;
    }
    function addBefore(target, name, before) {
        var original = target[name];
        target[name] = function() {
            before.apply(target, arguments);
            return original.apply(target, arguments);
        }
    }
    var GreasemonkeyService = GreasemonkeyServiceClass.getService().wrappedJSObject;
    addBefore(GreasemonkeyService, 'evalInSandbox', function(code, codebase, sandbox){
        if (sandbox.Minibuffer && sandbox.LDRize) {
            sandbox.Minibuffer.addCommand({
                name: 'tombloo',
                command: function(stdin) {
                    var view = sandbox.LDRize.getSiteinfo()['view'];
                    var xpath_link = sandbox.LDRize.getSiteinfo()['link'];
                    var xpath_title = view || 'descendant::text()[normalize-space(self::text()) != ""]';
                    var nodes = sandbox.Minibuffer.execute('pinned-or-current-node');
                    forEach(nodes, function(node){
                        try {
                            var context = update(update({
                                document  : sandbox.unsafeWindow.document,
                                window    : sandbox.unsafeWindow,
                                title     : sandbox.unsafeWindow.document.title,
                                selection : '',
                                event     : {},
                                mouse     : {x:0,y:0},
                                menu      : null,
                            }, null), sandbox.unsafeWindow.location);

                            var text = '', url = '';
                            with(sandbox.unsafeWindow) {
                                var nodesSnapshot;
                                nodesSnapshot = document.evaluate(xpath_link, node, null,
                                    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
                                if (nodesSnapshot.snapshotLength > 0)
                                    context.href = nodesSnapshot.snapshotItem(0).href;

                                nodesSnapshot = document.evaluate(xpath_title, node, null,
                                    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
                                var allstringlength = 0;
                                var ypos = 0;
                                for(var n = 0; n < nodesSnapshot.snapshotLength; n++) {
                                    var item = nodesSnapshot.snapshotItem(n);
                                    var str = item.nodeValue.replace(/\s/g,'');
                                    allstringlength += str.length;
                                    if(allstringlength > 30){
                                        text += str.slice(0, 30) + '...';
                                        break;
                                    }else{
                                        text += item.nodeValue;
                                    }
                                }
                            }
                            if (text.length) context.title = text;

                            var exts = Tombloo.Service.check(context);
                            var service = Tombloo.Service;
                            forEach(exts, function(ext){
                                service.share(context, service.extracters[ext.name]);
                            });
                        } catch(e) {alert(e)}
                    });
                }
            });
        }
    });
}
あとはブラウザを再起動すれば、Minibufferにtomblooというコマンドが追加され、pinを付けて :tombloo と実行すれば、Tumblrにリンクがポスト出来る様になっています。
現状はTomblooが扱えるコンテンツの中でも、リンクのポストにしか対応していませんが、selection等を弄ればQuote、画像を選択させれば画像ポスト(Tumblr以外からのReblog)がMinibufferから可能になるかと思います。
今後やる気が続行すれば、作者殿にTomblooをプラガブルにする為の提案をさせて頂きたい...と思っています。
※やる気が続行すれば...の話です。

ただ、やっぱり特権のある部分からMinibufferを操作する行為は幾らevalInSandboxとは言え怖い...苦笑


リンク:Tombloo 0.1.0
Posted at by




以前、個人的なブームでPlaggerのPublish::XXXプラグインを作ってた頃の知識を再利用。

Minibufferには、ソーシャルブックマークへ登録するコマンド「bookmark」があります。
minibufferbookmarkcommand.user.js
ちょっと前これを色んなSBMに対応出来る様にプラガブルに修正したのですが、ノウハウを生かして以下2本のbookmarkプラグインを作成しました。

GooBookmarkプラグイン
minibufferbookmarkcommand.goobookmark.user.js
NiftyClipプラグイン
minibufferbookmarkcommand.niftyclip.user.js
それぞれ :bookmark -g
:bookmark -n
で実行出来る様になっています。
Publish::XXXの時と同様に利用している人は皆無かと思いますが、よかったらどうぞ。
Posted at by




MinibufferBookmarkCommand - 3.14

適当ですが...
はてなブックマークと違いdel.icio.usの場合は、クローラでタイトルを補完してくれませんので、XHRで自前で取得しています。その際、responseTextが文字化けを起こしている可能性がありますので要注意。
--- minibufferbookmarkcomman.user.js.orig   Sat Nov 24 14:53:01 2007
+++ minibufferbookmarkcomman.user.js    Sat Nov 24 14:51:57 2007
@@ -140,6 +140,90 @@
        postToHatenaB(nodes, urls);
    }
    
+   // delicious bookmark
+   var delicious = function(nodes, urls, tags, comment){
+       if(!urls.length) return;
+       var total = urls.length;
+       var time = new Date().getTime();
+       var getParam = function(node, url, fn, afterPost){
+           if(!url) return;
+           var callback = function(res){
+               var html = res.responseText.createHTML();
+               var form = window.Minibuffer.$X('//form[@id="delForm"]', html)[0];
+               var inputs = window.Minibuffer.$X('//form[@id="delForm"]//input', html);
+               var res = {};
+               inputs.forEach(function(node){
+                   res[node.getAttribute('name')] = node.getAttribute('value');
+               });
+               if(keys(res).length == 1){
+                   // already bookmarked
+                   var text = url;
+                   if(window.LDRize){
+                       var siteinfo = window.LDRize.getSiteinfo();
+                       if(siteinfo && siteinfo.view){
+                           var view = window.Minibuffer.$X(siteinfo.view, node)[0];
+                           if(view) text = view.textContent;
+                       }
+                   }
+                   window.Minibuffer.message('<small>'+text+'</small><br/> has already bookmarked.', 2000);
+                   afterPost();
+               }else{
+                   res.action = 'http://del.icio.us/' + form.action.replace(/^https?:\/\/[^\/]+\//, '');
+                   // not yet bookmarked
+                   fn(res, afterPost);
+               }
+           }
+           GM_xmlhttpRequest({
+             method: 'GET',
+             url: "http://del.icio.us/post?v=4&url="+encodeURIComponent(url),
+             onload: callback,
+             onerror: function(res){log('onerror',res.responseText, '\n',res.responseHeaders)},
+           });
+       }
+       var post = function(opt, afterPost){
+           if (!opt.description) {
+               var x = new XMLHttpRequest();
+               x.open('GET', opt.url, false);
+               x.send(null);
+               if (x.readyState == 4 && x.status == 200) {
+                   try {
+                       var html = x.responseText.createHTML();
+                       opt.description = window.Minibuffer.$X('//title', html)[0].text;
+                   } catch(e) {}
+               }
+           }
+           var request = [
+               "url=", encodeURIComponent(opt.url),
+               "&oldurl=", encodeURIComponent(opt.url),
+               "&private=", opt.private,
+               "&description=", encodeURIComponent(opt.description),
+               "&notes=", encodeURIComponent(comment),
+               "&tags=", encodeURIComponent(opt.tags),
+               "&v=", opt.v,
+               "&key=", opt.key,
+               ].join('');
+           GM_xmlhttpRequest({
+             method: 'POST',
+             url: opt.action,
+             headers: {
+                 'Content-Type': 'application/x-www-form-urlencoded'
+               },
+             onload: afterPost,
+             onerror: function(res){log('onerror',res.responseText, '\n',res.responseHeaders)},
+             data: request
+           });
+       }
+       var postToDelicious = function(nodes, urls){
+           if(!urls.length){
+               window.Minibuffer.status('bookmark'+time,'Bookmark 100 %', 1000)
+               return;
+           }
+           window.Minibuffer.status('bookmark'+time,'Bookmark ' + Math.floor((total-urls.length) / total * 100) + '%');
+           getParam(nodes.shift(), urls.shift(), post, function(){postToDelicious(nodes, urls)});
+       }
+       postToDelicious(nodes, urls);
+   };
+
 // var delicious = function(urls, tags){
 //     log('delicious: urls:tags',urls, tags);
 // }
@@ -147,7 +231,7 @@
 //     log('livedoor: urls:tags',urls, tags);
 // }
    addBookmark('hatena',    'h', hatenab);
-// addBookmark('delicious', 'd', delicious);
+   addBookmark('delicious', 'd', delicious);
 // addBookmark('livedoor',  'l', livedoorclip);
    
    window.Minibuffer.addCommand({
Posted at by