JavaScript: console.time/timeEndラッパー

var bench = function(/* l, f1, f2, ... */){
    var args = Array.slice(arguments);
    var l = args.shift();
    args.forEach(function(f,fi){
        console.time(fi+1);
        for (var i=0; i<l; i++) f();
        console.timeEnd(fi+1);
    });
};
bench(2000, function(){ $x('//a'); }, function(){ $x('/descendant::a'); });
/* ->
1: 2047ms
2: 1422ms
*/

新しいところは何もない。

JavaScript: Array.prototype.flatten

Ruby互換で基本全潰し、引数で再帰回数指定できる。

Array.prototype.flatten = function(level){
    return Array.prototype.concat.apply([], (level <= 1) ? this : this.map(function(v){
        return (v instanceof Array) ? v.flatten(level-1) : v;
    }));
};

オプションなしのシンプル版。

Array.prototype.flatten = function(){
    return Array.prototype.concat.apply([], this.map(function(v){
        return (v instanceof Array) ? v.flatten() : v;
    }));
};

Array#reduceも使う手もあるけど、関数呼び出しがやたら多くなるのが微妙。

Array#concatは複数の引数がとれ、引数がnumberなどのときはそのまま、配列なら潰して繋げてくれる。

1段階のみのflattenなら、Array.prototype.concat.apply([], ary)と等価。

JavaScript: getElementXPath

var getElementXPath = function(elem){
    var ary = [], index = 1;
    for ( ; elem && elem.nodeType == 1; elem = elem.parentNode) {
        if (elem.hasAttribute("id")) {
            ary.unshift('id("' + elem.getAttribute("id") + '")');
            return ary.join("/");
        } else if (elem.hasAttribute("class")) {
            ary.unshift(elem.localName.toLowerCase() + '[@class="' + elem.getAttribute("class") + '"]');
        } else {
            index = 1;
            for (var sibl = elem.previousSibling; sibl; sibl = sibl.previousSibling) {
                if (sibl.localName == elem.localName) index++;
            }
            ary.unshift(elem.localName.toLowerCase() + "[" + index + "]");
        }
    }
    return (ary.length > 0) ? "/" + ary.join("/") : null;
};
// Googleの検索結果のリンク
// div[1]のところは「関連検索」が出るとdiv[2]になったりする

getElementXPath($0)
// -> "id("res")/div[1]/div[@class="g"]/h2[@class="r"]/a[@class="l"]"

http://code.google.com/p/fbug/source/browse/trunk/content/firebug/lib.js#685

ほとんど、FirebugのFBL#getElementXPathと同じ。

相違点は

  • [1]を省略しない
  • id関数を使う

ところ。


いまさら人に聞けない Firebug tips - bits and bytes

まさしくいまさらなんだけど、適当な要素をinspectして$0ってやると、その要素が得られるんですな……

copy()も便利そう。

追記

classNameを考慮するように変更。

JavaScript: pageElementの推定 2

javascript:(function(){
var d = document.body;
var ary = [{node: d, size: d.offsetWidth*d.offsetHeight}];
while (d.nodeType === 1) {
    var t = Array.map(d.childNodes, function(v){
        return {node: v, size: v.offsetWidth*v.offsetHeight || 0};
    });
    t.sort(function(a,b){return b.size-a.size;});
    if (t[0].size*2 < ary[0].size) break;
    ary.unshift(t[0]);
    d = t[0].node;
}
ary[0].node.style.border = "5px solid red";
})();

pageElement推定(トップダウン)


document.bodyから描画領域が大きいものを選びながら降りていって、次の描画領域が今の半分以下に落ちたところで打ち切り。

これは、微妙。

一部の広告屋だけが勝手に使いやすいと勘違いし、大抵のユーザは気にも留めず、しまいにはさっくり消されることでおなじみの右2カラムサイドバーに対応できない。

トップダウンなので子が全部floatしてるdivなどがあると、v.offsetHeight === 0となり死ぬ。

JavaScript: pageElementの推定

javascript:(function(){
const THRESHOLD = 10000;
var bottoms = [], ary = [];
var xp = document.evaluate('/html/body/descendant::*[not(child::*)]', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i=0,l=xp.snapshotLength; i<l; i++) bottoms[i] = xp.snapshotItem(i);
bottoms.forEach(function(v){
    var s = v.offsetWidth * v.offsetHeight, t;
    while ((v = v.parentNode) && !(/body/i).test(v.nodeName)) {
        t = v.offsetWidth * v.offsetHeight;
        if (s > THRESHOLD) ary.push({node: v, ratio: t/s});
        s = t;
    }
});
ary.sort(function(a,b){return b.ratio - a.ratio;});
ary[0].node.style.border = "5px solid red";
})();

pageElement推定


子ノードとの描画サイズ比から推定。

うーん、ぼちぼち。

追記

親ノードも考慮する版。

javascript:(function(){
const THRESHOLD = 10000;
var bottoms = [], ary = [];
var xp = document.evaluate('/html/body/descendant::*[not(child::*)]', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i=0,l=xp.snapshotLength; i<l; i++) bottoms[i] = xp.snapshotItem(i);
bottoms.forEach(function(v){
    var s = v.offsetWidth * v.offsetHeight, t, u, p;
    v = v.parentNode;
    t = v.offsetWidth * v.offsetHeight;
    while (!(/body/i).test(v.nodeName)) {
        p = v;
        do {
            p = p.parentNode;
            u = p.offsetWidth * p.offsetHeight;
        } while (!(u > THRESHOLD))
        if (s > THRESHOLD) ary.push({node: v, ratio: t*t/s/u});
        s = t;
        t = u;
        v = v.parentNode;
    }
});
ary.sort(function(a,b){return b.ratio - a.ratio;});
ary[0].node.style.border = "5px solid red";
})();

pageElement推定(親も使う)


前者でも十分ぽいなあ。

「$g」更新

JavaScript: $g - mayokara note

$g('//a/@href', null, function(v){return v.nodeValue;})

// 第3引数にtypeofがobjectとなるものを指定
$g('//a/@href', null, {})
$g('//a/@href', null, [])
$g('//a/@href', null, null)

で返せるようにした。

「Romtter」更新

/romtter/#user=bombtter,buzztter,nannohi,TakadaJunji


API Documentation - Twitter Development Talk | Google グループ

何か久しぶりに見たら、in_reply_to_status_idとか取れるようになってたので適当に作り直した。

これまでと同じく、jkで上下移動できる。

括弧内はfollowers_count

URLのサムネイル表示は見づらくなるので廃止。

存在しないユーザを指定すると止まる。


IEにも対応させようとexarray.jsをロードしてみたはいいものの、IE6でショートカットキーが効かなかったり突然落ちたりして挫折気味。

追記

Firefoxのみscript要素のonerrorハンドラを利用して、データの取得に失敗しても止まらないようにした。

Fw: document.evaluateの最後の引数

3:14 - document.evaluateの最後の引数

var l = 10000, xp = null;
console.time("reuse");
for (var i=0; i<l; i++) {
    xp = document.evaluate('//a', document, null, 0, xp);
}
console.timeEnd("reuse");
console.time("null")
for(var i=0; i<l; i++){
    xp = document.evaluate('//a', document, null, 0, null);
}
console.timeEnd("null");
reuse: 4625ms
null: 5687ms

$gと、nisefastforward5修正しておいた。


ちなみに、Mozilla1.8だとこのへん。

http://mxr.mozilla.org/mozilla1.8/source/extensions/transformiix/source/xpath/nsXPathEvaluator.cpp#131

http://mxr.mozilla.org/mozilla1.8/source/extensions/transformiix/source/xpath/nsXPathExpression.cpp#72

aInResultってのが第5引数で、do_QueryInterfaceした後、なければ作るみたいな処理をしてる。

XPath式の内容や、コンテキストノードの指定などとは関係ないみたい。

JavaScript: use

var use = function(src, F){
    var s = document.createElement("script");
    s.type = "text/javascript";
    s.src = src;
    if (typeof(F) === "function") s.onload = F;
    document.getElementsByTagName("head")[0].appendChild(s);
};
use("http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/prototype.js", init);

新しいところは何もない。

Bookmarklet: makeTOC

javascript:(function(){
var resolver = function(){
    return (document.contentType == "text/html") ? "" : "http://www.w3.org/1999/xhtml";
};
var xp = document.evaluate('/descendant::*[self::x:h1 or self::x:h2 or self::x:h3 or self::x:h4 or self::x:h5 or self::x:h6]', document, resolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
var ul = document.createElement("ul");
ul.style.textAlign = "left";
for (var i=0,l=xp.snapshotLength; i<l; i++) {
    (function(node){
        var li = document.createElement("li");
        li.style.marginLeft = (20 * (node.nodeName[1]-1)) + "px";
        li.style.listStyleType = (node.nodeName[1]%2 == 1) ? "disc" : "circle";
        li.innerHTML = '<a href="javascript:void(0);">' + node.textContent + '</a>';
        li.addEventListener("click", function(e){ node.scrollIntoView(true); }, false);
        ul.appendChild(li);
    })(xp.snapshotItem(i));
}
document.body.insertBefore(ul, document.body.firstChild);
})();

makeTOC


h[1-6]要素を集めて目次を作るブックマークレット - mayokara note

以前作ったこれをXPath仕様に直したもの。

言うまでもなくIEでは動きません。