evaluateとAutoPagerizeの動作に悩まされた
ソースの上のほうはほとんどA smart dashboard is mineからの流用で、CSSを読みやすいようにCDATAで書き直したくらい。
で、問題はこの部分。
function is_reblogged_mine(doc, xpath){ var target = document.evaluate(xpath,doc,null,7,null); for(var i = 0, maxi = target.snapshotLength; i < maxi; i++){ target.snapshotItem(i).className += " is_reblogged_mine ";//" notification is_reblogged_mine "; } } var boot=function(){ is_reblogged_mine(document,'.//li[contains(@class,"not_mine")][contains(@class,"is_reblog")][div[@class="post_info"][text()[contains(self::text(),"reblogged you")]]]'); window.addEventListener('AutoPagerize_DOMNodeInserted', function(evt){ is_reblogged_mine(evt.target,'.//div[@class="post_info"][text()[contains(self::text(),"reblogged you")]]/parent::li'); }, false); } if(window.AutoPagerize){ boot(); }else{ window.addEventListener('GM_AutoPagerizeLoaded', boot, false); }
AutoPagerize対応にするにはhttp://autopagerize.jottit.com/apis_(ja)に載っているAPIを上手く使えばいい。目的とするreblogged youを含むノードをXPathを使って特定したうえでほんの少し言え書き換える。なので、addDocumentFilterかaddFilterを使えば何とかなるだろう、と最初は思っていた。addResponseFilterでも可能なのだろうが、XPathを使いたい=DOM化は済んでいて欲しいというのもあって、前二者にターゲットを絞った。
しかし、どういうわけか上手く動かなったのでid:blooo:20091012:1255323254にあるようにAutoPagerize_DOMNodeInsertedイベントを拾ってやる方式に切り替えた。
最初のうちは以下の関数の基本形をそのままに、ちょいちょいといじれば何とかなるだろうと楽観して動かしていたのだが、どうにも様子がおかしい。
function is_reblogged_mine(doc){ var target = document.evaluate('.//li[contains(@class,"not_mine")][contains(@class,"is_reblog")][div[@class="post_info"][text()[contains(self::text(),"reblogged you")]]]',doc,null,7,null); for(var i = 0, maxi = target.snapshotLength; i < maxi; i++){ target.snapshotItem(i).className += " is_reblogged_mine ";//" notification is_reblogged_mine "; } }
関数自体は2ページ目以降も動作している風なのに、XPathに対象ノードがヒットしていない。色々調べた結果どうやらid:janus_wel:20080908:1221034110に書いてあることが近そうだと目星をつけて、document.evaluateをdoc.evaluateに変えたりdoc.ownerDocumentを取り出したりと悪戦苦闘。結局それも空振りで、一番分かりやすく効果があったのはevaluateの第二引数をdocument.bodyに戻したとき。
全体に対してのXPath探索なら上手くいくのに、部分に対する探索がうまく行かないのは何で? となったときに、答えは意外なところにあった。
アイテム詳細: Tumblr(dashboard) url ^http://www\.tumblr\.com/ nextLink id("next_page_link") pageElement id("posts")/* exampleUrl http://www.tumblr.com/dashboard
via http://wedata.net/items/552
そんでもって、dashboardのpageElement周りの構成は、大雑把に書くとこんな感じ。
<ol id="posts"> <li>ポストデータとか色々</li> <li>同じく色々</li> </ol>
つまり、liノードがAutoPagerizeによって元ページに追加される対象で、イベントで拾ったりaddDocumentFilterやaddFilterで叩けるのもこれらliノード。だというのに、こっちが必死になってXPathで探索しようとしていたのは、
var target = document.evaluate('.//li[contains(@class,"not_mine")][contains(@class,"is_reblog")][div[@class="post_info"][text()[contains(self::text(),"reblogged you")]]]',doc,null,7,null);
追加されたliノードのさらに内側にあるliノード。id:janus_wel:20080908:1221034110に書いてあるcurrentNodeあれこれはこのことかとようやく理解。確かにこれはMDCのドキュメントじゃ勘違いすると思う……。
つまり、追加されたノードの内部を探索するのなら、ということで次のコードを試す。
var target = document.evaluate('.//div[@class="post_info"][text()[contains(self::text(),"reblogged you")]]/parent::li',doc,null,7,null);
今度こそ思い通りの動作になったので、1ページ目での実行時と2ページ目以降での実行時で動作を分けるためにis_reblogged_mineに引数xpathを追加。よって完成。
たぶん、evaluateの評価対象を意識して書き換えれば、AutoPagerizeのAPIからでも同様に動かせそうなのだが、最低限の動作には問題がないのでとりあえずこれで公開とする。