2006-11-04 [長年日記]
_ JavaScript で外部ライブラリの load
分かっている人には今さらなネタです。自分用に整理してみました。
Ecmascript そのものには外部ファイルを読み込む機能はない。ホスト環境の機能に依存する。ブラウザで動く JavaScript は HTML を解釈するブラウザの機能を利用し、
- <script> 要素を書き出す
- XMLHttpRequest
のどちらかの方法で外部ファイルを load する。(HTML の中に script 要素を予め書いておく方法は何も難しいことはないのですっぱり割愛 。)
※ ここに書いたものよりもう少し詳しい内容を「JavaScript の動的ロードで(結果的に)Classic Mac を除外する」、「XMLHttpRequest を使ったスクリプトのロードは意外と面倒」に書きました。よければそちらも読んでいただければと思います。
1. の方法の場合、どのドメインにある JavaScript でも load できる。この方法は外部のドメインにあるデータの読み込みによく使われるが、HTML の読み込みが完了した文章からこれを実行してもスクリプトの load がいつ完了したか知る方法がない。*1また、他のドメインに置いてあるスクリプトは常に読み取れるとは限らないので自分のページのロード時間が外部の要因に左右されることになる。*2
2. の方法の場合は XMLHttpRequest の特性上読み込めるスクリプトが同一ドメインのものに限定されるが、スクリプトのロード完了をイベントで知ることができるし、1 の場合よりユーザーを不用意に待たせてしまう可能性も低くなるだろう。
XMLHttpRequest を備えているブラウザしか相手にしないのであればこの方法は安全だし分かりやすいのでオススメだと思う。
以下、注意事項がいろいろある 1 の場合について。
user javascript での load、あるいはユーザーのアクションによる load
この場合、すでに HTML 文書そのものは load し終わっているので、スクリプトの load の完了は window.onload イベントでは補足できない。したがって何らかの手を加える必要がある。
具体的には 最速 ma.la 方式に手を加えるのが恐らくいちばん楽。
cf. JSAN DEMO
function onloaded( objname, func, thisobj ) {
var obj = undefined;
try {
eval( "obj = " + objname );
} catch ( e ) {
;
}
if ( typeof obj != 'undefined' ) {
func.call( thisobj );
} else {
setTimeout( function() {
onloaded( objname, func, thisobj );
}, 100 );
}
}
一応オブジェクトを作っていても動きますよバージョン*3。呼び出しは
onloaded( ObjectName, function() {
function();
}, this );
という形になる。*4
ただしこれは読めば分かるが終了条件が危うい。目的とするオブジェクトが永遠に生成されない場合*5は onloaded の処理は終了しない。
ということで本当はこれは
- 外部ライブラリの load などをまかなうオブジェクトの中に綴じ込め、その中のメンバ変数とかを頼りに再帰回数を制限しておく
- 外部ライブラリの名前はフルのファイル名ではなくオブジェクト名を与えると
ObjeactName + ".js"
に展開されてロードされるとか、命名規則を作っておく
などの工夫をした方がよいだろう。
そうすればすでに読み込み済みのライブラリを多重 load してしまうこともない。なんか書いてるうちに段々 DoJo や JSAN に似てくるな。
JSAN はこの bootstrap の部分だけ利用するのもアリかもしれない。Perl 界隈向けっぽいがおいしいところは利用すべきかな、と。
GreaseMonkey
どうも名前空間と言うか、セキュリティを考えて何か細工が入っているっぽい。(全然ちゃんと調べてない。)Module か greasemonkeyService の中で実行されるのかな? <script>要素書き出し方式で読み込んだライブラリが GreaseMonkey 上では動かずに Firebug 上では動くので恐らくそう。
- gm script の中で script 要素を書き出してスクリプトを読む場合は unsafeWindow を利用するとよさげ
- gmXHR を利用すると unsafeWindow は不要っぽい
上に書いたモジュールの load などを行うものをオブジェクトとして作っておいて、実行環境を判別して gm と通常のページ、bookmarklet などの環境を気にせずに load() が呼べたりするとステキっぽい。
参考
- GreaseMonkey で MochiKit 使ってみる。すなわち外部ライブラリの読み込み。あるいははてなのグラフが綺麗じゃない
- Greasemonkeyでprototype.jsやscript.aculo.usを使う方法
- manalang.com » Loading External JavaScript Libraries in Greasemonkey
JSAN
JSAN も JSModule も中で XMLHttpRequest でスクリプトを取得するようになってるのね。Array#push も使ってるし、MacIE は無視、っと。
※ まぁ今さら MacIE だけ特別に気を使わなきゃいけないのもどうかとは思うんだけど。MacIE も MacOS 9 もとっくの昔にメーカーがサポート打ち切ってるんだから。どうしてもという人は本当に真剣に iCab の支援をすべきだろう。あれが唯一の希望なんだから。
*1 Firefox の場合は、文書の読み込みと同時に実行すれば window.onload の瞬間にはすべてのスクリプトが揃っているので難しいことを考える必要はないっぽい。localhost でわざと setTimeout() で時間を遅らせて load してみたけど大丈夫だった。でも他のブラウザは全滅なんだよな。あと iframe 使えばその辺の問題は回避できるのかもしれないけど、試してない。iframe 好きじゃないしなぁ。
*2 404 の場合はまだしも、外部ドメインのサーバが落ちているなどの場合はその判断が確定されるまでの間、ブラウザが待たされてしまう。
*3 ただし MacIE では Function#call メソッドがないので動かない。
*4 もとの wait() って関数名が、どうも待ったあとに関数を実行するイメージが湧かないので名前は変えてあります。
*5 オブジェクトの名前を間違って書いてしまったとか呼び出し先のサーバが落ちている場合、もっと言えばオブジェクトの構成が変わってしまった場合なんかもそう。
2006-11-06 [長年日記]
_ JavaScript の動的ロードで(結果的に)Classic Mac を除外する
※ JSAN 使えというのはとりあえず忘れてください。7日の記事で触れます。
半年近く前の話題にやっと決着ついた。
- script 要素書き出し方式
- XMLHttpRequest
の二つの方法があると書いたけれども、結論から言うと MacIE はどっちも動きませんでした。
まず、XMLHttpRequest が動かないのはいいですね。これは当然。そんなオブジェクトは組み込まれていないし、ActiveX で使うこともできません。はい、一つ片付いた。では 1. の script 要素書き出し方式。
script 要素書き出し方式も厳密には
- document.write()
- DOM
の二つの方法があります*1。しかし document.write() はいつでも使えるわけではありません。(やってみりゃすぐ分かります。)
そこで実際に使うのは DOM を操作するって方法になるわけです。まぁこれは誰が書いても大差ない、こういうコードになるはずです。(ならなかったらオレすっげー勘違いしてることに。)
function require( url ) {
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.src = url;
var head = document.getElementsByTagName( 'head' )[0];
head.appendChild( script );
}
これが MacIE では動きません。
しばらく悩みましたが、
script.src
に代入する処理が動きません。setAttribute だろうがなんだろうが動きません。値の取得はできるんですが、セットできない。
はい終了。src 指定できないんじゃ何もできない。ちなみに、よく調べてないけど iCab も動きませんでした。なんでかは知らない。まぁいずれにせよ分かったことは、
DOM による script 要素の書き出しを使ったスクリプトのロードは MacIE でも iCab でも動かない。XMLHttpRequest を使っても使わなくてもこれらのブラウザはこの段階で除外できる。
ということです。あとは require() 内の処理を丸ごと try - catch で囲んでやれば MacIE, iCab は華麗にスルーされてくれます。
*1 例によって iframe は意図的に無視しています。
2006-11-07 [長年日記]
_ XMLHttpRequest を使ったスクリプトのロードは意外と面倒
- XMLHttpRequest を使ったスクリプトのロードを自分で書いてみた
- JSAN を読んでみた
- JSAN よくできてる
という話。
- XMLHttpRequest で取得できるのは「データ」である
- 「データ」を「スクリプト」にするためには eval() する必要がある
- eval() した結果できるコードは eval() された場所のスコープに制限される
つまり、例えば
// スクリプトをロードするつもりの関数
function load( url ) {
// XMLHttpRequest で同期通信でコードを取得する処理
// 取得に成功したコードを eval()
}
load( hogege );
// ここでエラー
function_defined_external();
ということ。function_defined_external() という関数が load() で読み込んだスクリプトの中で定義されていても、それは load() の中で eval() したら load() の中でしかコードとして存在できないわけです。
これはこうなっていれば ok です。
function load( url ) {
..
// 読み込んだデータをそのまま返す
return xhr.responseText;
}
var script = load( url );
if ( script ) {
eval( script );
}
// これは実行可能
function_defined_external();
// (ちゃんとライブラリをロードできていれば)
- 読み込む関数は読み込みしかしない
- 返って来たテキストデータを「自分の意思で」「動かしたいスコープで」 eval() する
という二段階の操作になっちゃうけども、こうすれば問題なく使える。二段階はどうしてもイヤだっていうんでなければこの方法がいちばん手軽だと思う。*1
しかし JSAN はこういうかっこ悪いことはしていない。use(), require(), export() が自在に活躍してくれる。これは、
- オブジェクトをちゃんと活用しているから
- 命名規則を設けているから
まぁ簡単に言えば JSAN.use() の中で eval() してできあがったオブジェクトのプロパティを Global なオブジェクトのプロパティにコピーすれば使えますよということ。これは prototype.js とか最近のモダンなライブラリがみんなやってる「プロパティコピペしまくりの術」の応用なわけですな。うまいこと考えたなぁ。
ただ、逆に言うと
ちゃんと JavaScript でオブジェクトを扱える人でないと JSAN を利用してロードするライブラリを書けない
ということです。
つかまず一発勝負関数とかあるわけですよ、やっぱり。すべてのコードが名前空間やオブジェクトを意識して作られた整然としたコードにはなっていないわけですし、最近の便利なフレームワークも別に JSAN の枠組みには則っていないわけです。
そういうときは上の二段階方式が扱いやすくていいな、と思っています。先の記事で書いた require() は bookmarklet など他ドメインのスクリプトをロードしたいときに、XMLHttpRequest を利用した load() は同一ドメインの無作法なライブラリ(笑)や、そのまま script 要素に書いて読み込むと MacIE などの古いブラウザでコンパイルエラーが起きてしまうライブラリ*2のロードに利用していったらいいかなと思っています。
_ text/javascript って obsolete なんだ
RFC4329 - Scripting Media Types
application/javascript application/ecmascript
にしろってか。ブラウザの対応とかどうなってんだろ。
まぁ JSON が application/json になったんだから、その時点で気づけってんだよな。
なんかネタかぶりしてるけど、ぼかぁたまたま IANA Media Types を見に行っただけですよ。
というかチェックしてるはずの神崎さんのメモをなんとなくスルーしてる段階でアンテナ感度が鈍いっつー話なんだよなぁ。あ、今 Camino 1.0.3 でチェック用のボタンを押してみたら全部動いた。てことはこの方法でちょっとでも古いブラウザを根こそぎ排除できるかも(笑)
と思ったら IE7 はダメらしい。おいこら。
※ まったく本題じゃないんだけど、リンク先のページを改めて見て気がついた。Camino でも Gecko 風のボタンが表示されている。これは Safari 1.3 でも同じ。input type="button" の場合はちゃんと Aqua なボタンになるのに、button 要素の場合は昔ながらのボタンぽい表示になる。Opera 9.0.2 ではどちらも Aqua 風になる。
2006-11-08 [長年日記]
_ Trac 0.10 が出てたんだ
TracDev/ReleaseNotes/0.10 - The Trac Project - Trac
し〜らなかったよぉ〜。Wiki の文法に定義リストが増えてる。こりゃいいな。
2006-11-10 [長年日記]
_ MozRepl の使いどころって?
なんか自分的にはちょっと微妙。
まず DOM の操作をしようと思っても日本語の文字列が unicode escape sequence で渡ってくるので terminal で表示できない。*1サンプルにあるように title って入れていきなり訳が分からなくなるのは結構悲しい。また、Firebug だと HTML collection なんかは配列っぽく自動的に表示してくれるがそういうこともない。まぁそんな感じで DOM いじりが便利ってわけでもない。
じゃあ Firebug より使いやすいコンソールなのかというと、rlwrap なんかで readline の恩恵にあずかることはできるが、Rhino のように長いコードがその場で書けるわけではない。Rhino なら例えば for 文の途中で改行してもそのまま書き続けられるが、MozRepl はその場で評価されてしまうのでやっぱり長いワンライナーにせざるを得ない*2。
今のところいいなと思ったのは inspect() で、これがあればこのオブジェクトにはどんなプロパティあったっけー?と思ったときに放り込むだけでだいたいのことが分かる。
しかし Firefox で気軽にこれをやるととんでもない量のプロパティが列挙されることがよくあるので、more() とか grep() とかあったら便利かもしれないなと思った。screen 上でやってればスクロールバックさせることはできるけど、この操作ってなんだかトロくさくてやなんだよな。
まー遠隔の自動操縦が本来の狙いで、日本語の文字列を避けてできる範囲で使えばすごい便利なんだろうけどなぁ。細かいチェックを Firebug を併用して行って、自動化を MozRepl で行う。そんな感じか。
※ ちょっとずつ慣れてきた。Firebug と違って「タブを区別せずに現在の content の状態に対してアクセスできる」のは便利かもしんない。やっぱ grep() とかほしいな。
[2006-11-11 追記]
repl.search( /RE/, context )
でイケるっぽい。例えば
repl> repl.search( /f/, document ) defaultView lookupPrefix isDefaultNamespace firstChild insertBefore prefix createEntityReference lastModified referrer preferredStylesheetSet domConfig
こんな感じ。
repl.search( string, context )
だと完全一致になる。
repl> repl.search( 'first', document ) repl> repl.search( /first/, document ) firstChild
ふむふむ。
2006-11-18 [長年日記]
_ 東京タワー
金も定職もないっつーのは本当につらくて情けなくて不必要に自分を蔑んで反動で無意味に遊びほうけてしまったりして、とてもつらいよね。
と、まったく勘違いした共感を覚えていたりするのであった。
全体的には、宣伝はすごかったけど話はイマイチ。時間軸をいじりすぎた演出もちょっとどうかと。半分くらいの時間はついていけてなかった。田中裕子はあの手の芝居は良い悪いを別にしてちょっと板につきすぎてる感じ。蟹江敬三と BEGIN の歌がとてもよかった。
と、冷静に見れているようなフリはしているが、最近はすっかりお涙頂戴物には弱くなっちまった。
2006-11-22 [長年日記]
_ XREA+ の容量が 2GB に
XREA+サービス:基本容量増量 - XREA SUPPORT BOARD
もっと活用しないと。まともにアイディアすら練ってないけど。
/* 自作 CopyURL+ 代替*1がなかなか便利だな。なんでもっと早く作らなかったんだ。*/
って上の文をコメント風味に行頭に // を置いて書いたら表示されなかった。えーっ。そういう大事なことは言ってくんないと。困ったなぁ。結構 // 使ってる記事あるのに。
*1 だって Camino だもの
2006-11-23 [長年日記]
_ PHPUnit って Pear channel ではなくなったのか
PHPUnit 3.0 - Sebastian Bergmann
- PHPUnit 3 では xdebug を利用した code coverage, selenium を利用したクライアントサイドのテストも統合されているみたい
- Pear ではなく独自の channel サーバを用意してそっちで全部やるみたい
channel という概念でいろいろ独自に工夫できるようになったのはいいけど、Pear を見ればいいという安心感はなくなってきたな。PHP のライブラリ用の Freshmeat みたいなものがあったらいいのかも。
2006-11-25 [長年日記]
_ PentiumM サーバ、とりあえず形に
自宅サーバ高速化の計画がずいぶん前からあったんだけど、やっと形になってきた。途中でリビングに置いて違和感のないミニタワーが手に入らなくてやる気がなくなったのもあるけど、ハードウェアが苦手なのと必要以上にケチりすぎてずいぶん時間が経ってしまった。ポイントとしては
- mPGA479 + Pentium M の固定がちゃんとできてなかった
- NIC はとりあえず Intel PRO を一つ用意しておくと安心
- CD-ROM ドライブくらいは用意しておくと安心
て辺り。
今回、Marvell NIC + フロッピーのみという構成で FreeBSD 6.1 をインストールしようとしたんだけど、さすがに無謀だったみたい。NIC を認識しなきゃネットワークインストールはできないし、追加のドライバを食わせようにも最小限のインストールだけは済んでないといけない。結局、CD ドライブを一時的に借りてインストールした。*1
ハードウェアはほとんど買ったものを組み合わせただけなんだけど、付属クーラーのファンがうるさかったのでクーラーそのものは交換せずに無理矢理ファンだけ回転数の少ないものに取り替えて済ませた。さすが Pentium M、ちょっとでも風があれば十分。
いやしかし、はえーな、これ。portsdb の更新がもう信じられないくらいに速い。何これ。
*1 もしかして USB で Flash 刺してなんとかなったか? そんなでかいの持ってないけど。
2006-11-29 [長年日記]
_ スーパーITハイスクールとか?
スラッシュドット ジャパン|情報処理技術者試験が資格試験になるかも?、更新制になるかも?
資格試験化するかもしれんつー話も興味深いっちゃー興味深いんだけども、人材育成という意味ではそこだけ見ててもしゃーないと思うんで、とりあえず置いておく。
で、ふと思いついたのがタイトルのスーパー IT ハイスクール。いや実はこの制度よく知らないんだけどね。スーパーハイスクール自体を端的に整理してる情報ってないよね?*1 まぁなんだ、すっかり流行らなくなった情報系の人材をどうやって育てましょうかっつー話なんだから、スキル標準と能力試験以外にも工夫のしようがあるべ?ってことですよ。セキュリティキャンプとかはやっぱまだまだヲタク受けっぽい感じだし、もっと認知しやすいものを文部科学省の方で用意してもバチは当たらないっしょ。何をどうスーパーにするかはあとで考えるとして。
あとなんだ。民間の人材を投入しやすくすればいんじゃないの? 大学の授業が実践から離れてるとかって意見もあることだし。まーでもそれで Java とか IDE の勉強ばっかするようなことになったら目も当てられないけどね。「実践」て言葉はヤバイですよ。OJT なんかもそうだけど都合良く拡大解釈しやすい言葉を持ち込むのはやめた方がいいと思う。持ち込むならもっと咀嚼してからね。
*1 なんかスーパーサイエンスハイスクールとスーパーイングリッシュランゲージハイスクールってのは見つかったけど。