GeSHi - Generic Syntax Highlighter を使うようにした。対応言語が多いことと、シンタックスファイルを増やし易いことがいい感じ。ただし時々 well-formed じゃないエラーがでることがあるっぽい。
ソースの色づけを復活 2
.toSource(), forEach, etc
toSource とかはじめて知った。
GreaseMonkey で設定値保存するときはこれ使うといいね! わざわざ JSON 云々のコピペなんてしなくてよさげ。
var foo = new Object; foo.bar = "test"; foo.baz = "pqpq"; foo.toSource(); //=> '({bar:"test", baz:"pqpq"})' (new Date).toSource(); //=> (new Date(1142625169156)) (function () { return ""}).toSource(); //=> '(function () { return "";})' alert.toSource(); //=> 'function alert() {[native code]}'
ていうか、forEach とかあるのね! やべ。
var sum = 0; [1, 2, 3].forEach(function (item, index, array) { sum += item; }, this); // 第二引数は callback 中の this alert(sum);
同じように (Ruby -> JS) select -> filter, all -> every, map -> map, any -> some が使えるみたいだ。どうせ Fx 用の GM しか書かないから使いまくろう。
ソースの色づけを復活
pre
だけ色付けするようにした。
前に Pure JavaScript で色つけていたことがあったけど、あからさまにブラクラだった。で、なんとなく思いついたのでもっかい別の方法で実装してみた。
なんていうか非常にローテクといいますか他力本願な方法でして、PEAR::Text_Hightlighter に Ajax で丸投げというソリューションでございますです。
<?php header("content-type: text/xml"); require_once 'Text/Highlighter.php'; require_once 'Text/Highlighter/Renderer/Html.php'; $options = array( 'numbers' => HL_NUMBERS_LI, 'tabsize' => 4, ); $renderer =& new Text_Highlighter_Renderer_HTML($options); $hl =& Text_Highlighter::factory($_GET["lang"]); $hl->setRenderer($renderer); $ret = $hl->highlight($_GET["source"]); echo '<div xmlns="http://www.w3.org/1999/xhtml">'; echo $ret; echo '</div>'; ?>
こんなスクリプト書いたら、あとは JS で pre
要素列挙して投げてあげる。レスポンスを importNode
して、pre
と置き換える。みたいな。
でもこんなクソ簡単なスクリプトなのに IE ではちゃんとうごかない。importNode がだめなのか、それとも responseXML から importNode ができないのか……謎。
あーHtml.php の &nbsp;
は全部 &#160;
に置き換えた。DTD がないからエラーになる。
XPath, $X function, NSResolver
JS の XPath なんて書きましたけど、重大なバグがありまして、っていうかなんで気がつかなかったんだろう、えーそれは application/xhtml+xml なページ、すなわち XML として、名前空間をちゃんと扱うページではまともにセレクトできないんですよーははははー、例えばこのサイトとかね。
$X = function (exp, context) { if (!context) context = document; var resolver = function (prefix) { var o = document.createNSResolver(context)(prefix); return o ? o : (document.contentType == "text/html") ? "" : "http://www.w3.org/1999/xhtml"; } var exp = document.createExpression(exp, resolver); var result = exp.evaluate(context, XPathResult.ANY_TYPE, null); switch (result.resultType) { case XPathResult.STRING_TYPE : return result.stringValue; case XPathResult.NUMBER_TYPE : return result.numberValue; case XPathResult.BOOLEAN_TYPE: return result.booleanValue; case XPathResult.UNORDERED_NODE_ITERATOR_TYPE: { result = exp.evaluate(context, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); var ret = []; for (var i = 0, len = result.snapshotLength; i < len ; i++) { ret.push(result.snapshotItem(i)); } return ret; } } return null; } alert($X("//x:p")); // Array of p elements alert($X("count(//node())")); // => node number alert($X("count(//x:body) = 1")); //=> must be true
かなり強引に修正してみた。
XPath で要素を指定するとき x という prefix (上のコードの場合は別になんでもいいんだけど、普通は x とか xhtml とかいうのをつける) を必ずつけるようにしとく。使い勝手が悪くなったけど、仕方ない。prefix がないときは resolver をよんでくれないみたいだ。
そう、で、resolver なんだけど、実はただの関数だった。evaluate
は prefix を見つけると、resolver に prefix を渡し、URI を返すように要求する。resolver は prefix に対応する URI を返す。null
の場合はエラー, ""
の場合は、名前空間が null のものとして扱われるみたい (要追試)。
つまり、上のコードの resolver がやってることは、とりあえず普通の場合のように NSResolver を作って投げてみて、ダメだったら contentType にあわせて名前空間を返してやるっていう、かなり強引な (二回目) 方法なわけです。誰かもっと美しくして!
使い勝手が悪くなったけど
と書いたけど、XSLT で使うような XPath と同じになった。まぁ名前空間を考慮するとこういうことになるっていう名前空間マジックなんだけど、やっぱり面倒くさいよなぁ。
$X("count(//x:body) = 1")
を $X("count(//*[local-name() = 'body' and namespace-uri() = "http://www.w3.org/1999/xhtml"]) = 1")
みたいに書きたくはないし、HTML なページと XHTML ページとで、同じ XPath を使おうとするとこんなもんになってしまうような気もする。
冷静に考えると x より h のほうがいいや。