JavaScript: Firefox2でも配列内包・分割代入が使えることを知った

The features that do not introduce new keywords (destructuring assignment and array comprehensions) can be used without specifying the JavaScript version.

New in JavaScript 1.7 - MDC

今さらな話だけど、yieldキーワードとletキーワードを使わなければ、Firefox2でも何の断りもなしに1.7の配列内包・分割代入が使えることを知った。

てっきりFirefox3からだと思ってたので使ってなかったのだけど、そもそも気にする必要がなかったんですね……

というわけで、使えそうなコードのメモ。

分割代入・値の交換

var [a,b]=[4,9];
[b,a]=[a,b];
console.log([a,b]);
// -> [9, 4]

プロパティの部分取得

ObjectからObject?への分割代入 - 0x廃棄階層 - 統治局

window.addEventListener("click", function(e){
    var {pageX: x, pageY: y} = e;
    console.log([x, y]);
    this.removeEventListener("click", arguments.callee, false);
}, false);

(click) // -> [289, 115]

parseurl

DOM:window.location - MDC # Location object - Properties

const SAMPLE_URL = "http://www.google.com:80/search?q=devmo#test";
const RE_URL = /^([a-z]+:)\/\/(([\u0021-\u007e]+?)(?:\:(\d+))?)(\/[\u0021-\u007e]*?)(\?[\u0021-\u007e]+?)?(#[\u0021-\u007e]+)?$/;
var [href, protocol, host, hostname, port, pathname, search, hash] = SAMPLE_URL.match(RE_URL);
[href, protocol, host, hostname, port, pathname, search, hash]
// -> ["http://www.google.com:80/search?q=devmo#test", "http:", "www.google.com:80", "www.google.com", "80", "/search", "?q=devmo", "#test"]

正規表現はRFCに従った厳密なものではありません。手抜きです。

せめて非ASCII文字にはマッチしないようにちょっと修正。

配列内包

var range = function(start, end){
    var o = {};
    for (var i=start; i<end; i++) o[i] = i;
    return o;
};
[i*i for each (i in range(0,10))]
// -> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

range関数はジェネレータ(yieldキーワード)が使えないので、普通のオブジェクトで代用していますが、一般にfor/for each ~ in文において参照される順序は不定なのでちょっと微妙。

この場合は数値順にプロパティを与えているのでうまくいくようですが……

ハッシュから配列への変換

逆に言えば順序が不定でも問題なければ良いわけで、ハッシュから最も値の大きいキーを調べるコードは次のように書ける。

var o = {
    a: 5,
    b: 7,
    c: 0,
    d: 2,
    e: 9,
    f: 3,
};
[[i, o[i]] for (i in o)].sort(function(a,b){return b[1]-a[1];})[0][0]
// -> "e"

これがFirefox2やGreasemonkey上でも問題なく動くというところが新発見。

Amebloとはてなの比較

2人に1人はAmeba(アメブロ)?!|サイバーエージェントではたらく広報担当のブログ

はてなブックマーク - 2人に1人はAmeba(アメブロ)?!|サイバーエージェントではたらく広報担当のブログ

スパマーが喜ぶアメブロの不思議な「マナー」 : WebとPCのメモ帳

Ameblo はてな
ペタ スターメッセージ
芸能人ブログ こんな人も書いてます
芸能スパムブログ WEB開発日記

最後のはちょっと違うか。


まあ、似たようなサービスなら結果も同じって具合ではあるのだけど、ここまではっきりと印象が違うってのはすごいなあと思った。

はてなブックマークの一部(あくまで一部)も、mixiのようなSNSにある「ニュースで記事を書く」みたいな機能とあんまり変わらないような気がする。


スターは強要してないっていうのは確かにそうなんだけど、Amebloのペタも見たところコンセプト的な段階ではそんなに違いはない。

何で広報で明言するくらいにまでなったんだろう。

スターはダイアリーに直結してないところがミソ?


この辺の設計に、どういうノウハウがあるのか気になる。

「3Users目でセルフブックマークすると注目エントリ入りで人気者」みたいなのがあるんだろうか。

乃木坂春香の秘密 #1 見逃した

ああー……


しかも、こういうのに限ってネット配信ないのな。

ytvの「MONDAY PARK」枠は

  • EPGが枠全体で1時間/1時間半ぶっ続け
  • 冒頭3分がCMなので、30分ずつに割って時間通りに録るとEDの直前で切れる

というアレなので、録画する気力がもはやほとんどない(もう録ってないので後者は今どうなのかわからないけど)。

「無敵看板娘」も最後まで見れなかったし、「RD 潜脳調査室」は開始時期ズレまくりで結局見てないし、もうこの枠のアニメは全部ネット配信にすればいいのに。

ビジネス的にはいろいろあるのかもしれないけど……


しょうがないので「RD」と一緒に来週チェックします。忘れなければ。

Bookmarklet: 見ているタブを開いたページに戻る

javascript:if(document.referrer)location.href=document.referrer;else undefined

Back to Referrer


このURLどこから来たんだっけっていうときとか、タブを開いたけど404で参照元に戻りたいときとかに使う。

このアプローチを最初にどこで見たのか忘れてしまった。

あと、Firefoxの拡張機能などでReferrerを切ってる場合は動きません。おそらく。

JavaScript: E4X+DOMParserでDOM生成

var iso8601 = function(d){
    var ary = [d.getMonth()+1, d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds()].map(function(v){ return ("0"+v).slice(-2); });
    // Date#getTimezoneOffsetとかいうのもあるけどパス
    return d.getFullYear() + "-" + ary.slice(0,2).join("-") + "T" + ary.slice(2).join(":") + "+09:00";
};

var xml = <div class="hentry">
    <h2 class="entry-title">
        <a rel="bookmark" href="{location.href}">ENTRY-TITLE</a>
    </h2>
    <div class="entry-content">
        <p>ENTRY-CONTENT</p>
    </div>
    <abbr class="updated" title="{iso8601(new Date())}">{(new Date()).toString()}</abbr>
    <address class="vcard author">
        <a class="url fn" href="{location.href}">AUTHOR</a>
    </address>
</div>;
var dom = document.importNode((new DOMParser()).parseFromString(xml.toXMLString(), "application/xml").documentElement, true);
console.dirxml(dom);
<div class="hentry">
    <h2 class="entry-title">
        <a rel="bookmark" href="{location.href}">ENTRY-TITLE</a>
    </h2>
    <div class="entry-content">
        <p>ENTRY-CONTENT</p>
    </div>
    <abbr class="updated" title="{iso8601(new Date())}">Mon Jul 14 2008 23:24:16 GMT+0900 (JST)</abbr>
    <address class="vcard author">
        <a class="url fn" href="{location.href}">AUTHOR</a>
    </address>
</div>

変数展開のルールよくわからない。

あとで調べて追記する。

追記

var x = <{tagname} {attributename}={attributevalue}>{content}</{tagname}>;

ECMAScript for XML (E4X) 仕様邦訳 # 11.1.4 XML 初期化子

{attributevalue}の前後にはクオートいらない。

<a rel="bookmark" href={location.href}>のようにクオートを外せばちゃんと評価される。

location.hrefの参照も、iso8601の実行もできる。無名関数を走らせることもできる。


XMLListを別に作って、初期化子の中に埋め込むことはできない。多分。

一旦作った後選択して挿入して、その後DOMに変換する。

XMLListを別に作って、初期化子の中に埋め込むことはできます(コメント欄より)。

var children = <><child/><child/></>;
(<parent>{ children }</parent>).toXMLString();
/* ->
"<parent>
  <child/>
  <child/>
</parent>"
*/

これすごいな。{ children }の前後にテキスト入れてもいける。

「continue_reading.user.js」更新 その5

Greasemonkey: continue_reading.user.js - mayokara note

  • MICROFORMATS_USE_STRICTLYオプションの追加
    • 私が間違ってました
    • microformatsに全部同じ要素でなければいけないなんてルールはないし、自分ルールを強制した挙句互換性取るためにはパッチ当てろってのはあんまりだと思った
    • trueなら*falseならlastNode.nodeName.toLowerCase()で絞り込み
    • デフォルトはfalse
    • 「SITEINFOとの連携」をさせる場合はtrueにしてください
  • FORCE_TARGET_WINDOWにおける@class="autopagerize_link"が内部的なものだとわかったので除外
  • ナビゲーションにtr+td要素を使うとき、他のセルとの区別がしにくかったのでpaddingをつけた
  • 継ぎ足しナビゲーションのアンカーテキストをデコードするようにした

アンカーテキストのデコード

デコード前、デコード後の図

上が今まで、下がこれから。

「continue_reading.user.js」更新 その4

Greasemonkey: continue_reading.user.js - mayokara note

  • pageElement絡みの修正
    • pageElementがrowsプロパティをもっている(つまりtable/tbody要素)のときは各行をpageElementとするようにした
      • 要はtable自体ではなくその中身だけを継ぎ足す
      • Wassr検索がちゃんと動くようになった
    • microformatsを使うときは、見つかった最後のノードと同じ要素だけを選択するようにした
      • 要はdiv.hentryとtr.hentryが混在するとき、最後の要素(tr)だけが選択されるようになる
        • 「最後の要素」ってのが若干手抜きだけど、そもそも違う要素につけること自体が……って感じなので、これでfix
      • Twitterがちゃんと動くようになった

onscroll手動発火の件は、自分でウィンドウサイズを縮めれば何とでもできることに気づいたので保留。

State#notifyはあってもそんなにうれしくないので見送り。

Re: continue_readingとSITEINFOの連携

continue_readingとSITEINFOの連携 - 素人がプログラミングを勉強するブログ

continue_reading用というより、SITEINFOに従ってDOMツリーにAutoPagerize独自のmicroformatsを加えるスクリプト。

正直SITEINFOのことはあまり考えて作ってないので、今回の2番目の修正でpageElementがid("content")/*のようになっているとき、うまく動作しなくなってしまったかもしれません。

必要があればconst定数を使ってstrictモードとして実装し直すこともできますが、コンセプトとして外部との依存をなくす方向で作っているので、私的にはあまり……といった感じです。


なので、とりあえず現段階でのパッチを置いておくことにします。

これを当てれば、今回の2つ目の修正をなかったことにできます。

--- continue_reading.user.js	Mon Jul 14 12:46:00 2008
+++ continue_reading.user.js	Mon Jul 14 12:46:00 2008
@@ -195,7 +195,7 @@
         if (node) {
             pageElement = {
                 node: node,
-                xpath: '/descendant::x:' + node.nodeName.toLowerCase() + '[contains(concat(" ",@class," ")," ' + v + ' ")]',
+                xpath: '/descendant::x:*[contains(concat(" ",@class," ")," ' + v + ' ")]',
             };
             return true;
         }

continue_reading.user.js予定メモ

  • 継ぎ足し要素が大きすぎる、不適切
    • 今は一番小さい子ノードのサイズを代表値として拾っている
      • 平均値にしてみた→リストの羅列みたいな規則的な間違いが抽出されてダメ
      • 最大値にしてみた→まったくダメ
      • 中央値にしてみた→これもダメ
    • 結果的に、最小値をとった場合が一番自然に動いている
      • FIND_PAGE_ELEMENT_THRESHOLDのおかげ
      • 他の兄弟と極端な差があった場合除外する?
        • 本文中の1文字段落とかで誤爆しそう
    • 手詰り対処済
  • position: absolute;なノードの継ぎ足しで壊れる
    • position: relative;にする→崩れたページがあったし、元のドキュメントには極力手を加えたくない
    • 継ぎ足し要素のtopにオフセットを加える→どのみちフッタが被る
    • 代わりに親要素を取る→長いサイドバーまで一緒についてくることになる
    • 手詰り
  • Twitterのdiv.hentry問題
    • 仲間外れ要素を除外するFilter(ふつうの)を書くといいかも?
    • でもTwitter以外でそういうところないし、同梱するメリット薄い
      • どうしても気になる人は自分でFilter書くだろうし……
    • 保留対処済
  • Wassr検索のtbody継ぎ足しにおけるdivナビゲーション問題
    • tbodyの中のtr.cellsを見ればごまかせそうな気もするが、tbodyにまで挿げ替えるようなことはしたくない
    • 保留対処済
  • スクロールバーが出ないようなページだと継ぎ足そうにもonscrollが発生しない
    • document.body.style.marginBottom/paddingBottomあたりを触る?
    • ホイールの回転を検出・自前でonscrollイベントをdispatchする?
      • ON/OFFめんどい
      • 上下キーとかスペースまで拾うのはしんどい
    • 問答無用で次を取る
      • 鬱陶しいけど一番楽な気が
      • position: absolute;とのコンボで無限ループに入る可能性
        • まずないだろうけど
        • init内で1回だけ呼ぶ、とか
    • 検討保留
  • State#notify
    • pageElementのXPathとかエラーの内容とかをアイコンのマウスオーバーで見せる
    • 正直あまりEventListenerは多用したくない
    • console.info/errorで十分な気もする
    • 検討見送り

「continue_reading.user.js」更新 その3

Greasemonkey: continue_reading.user.js - mayokara note

  • application/xhtml+xmlなページで動かなかった問題を解決
  • $nを使うようにした
    • varとwithから解放された
  • findPageElementのアルゴリズムを修正

新しいfindPageElementのアルゴリズム

JavaScript: pageElementの推定 - mayokara note

元々使ってたこれの「親要素も見る」版は一度通ったノードを何回も計算するので非常に効率が悪い。

そこで

$g('/descendant::x:*[child::x:*]', null, function(v){
    var p = v.parentNode;
    if (p.nodeType !== 1 || (/^(html|head)$/i).test(p.nodeName)) return;
    var s = sizeofChildNodes(v), t = (sizeof(v) || s), u = (sizeof(p) || t);
    var ratio = t*t/s/u;
    if (isFinite(ratio) && ratio > candidate.ratio) {
        candidate.node = v;
        candidate.ratio = ratio;
    }
});

こんな風に、最初に計算対象となるノードを全部選択して、一つ一つ調べていくようにした。

以前のように一つ前の計算結果を流用することはできなくなったけど、最高で3回しかチェックされないことが保証されるようになったので、大抵の場合効率化される、はず。

これで、サイドバーに大量のa要素があるブログとかでも重くならない。

推定方法・得られる結果は旧版と同じ。

0割りされているかどうかを調べるには

isFinite関数を使う。

有限値ならtrue、±InfinityやNaNならfalseを返す。

0割りすると±InfinityかNaN(0/0)のどちらかになるので、これで判定できる。

PERSONA - trinity soul - #25~#26

残り100メートルで全力疾走。


逃げ切った!……というか、振り切った!、みたいな。

植木鉢くんの扱いが小さすぎる点以外は概ね良かったですよ。

叶鳴ちゃん@中原麻衣とか、アヤネちゃん@能登麻美子とか。

いかん、根本的に真面目に見ていないことがバレる。


いつの間にかはてなダイアリーキーワードまでできている某氏の今後が気になります。

「かんなぎ」やるんでしょうか?