次の画面ショットは、Catyに描かせた「ログインモジュールを可視化した図」を、ブラウザで表示させたものです*1。
実際のWebページには、次のURLでアクセスできます。
このWebページに含まれる図はラスター画像ではなくて、SVGをHTML5仕様に従い直接埋め込んでいます(インラインSVG、ソースを見ると確認できます)。SVGの部分も文書のDOMツリーのなかに現れるので、JavaScriptで操作することができます。例えば、ボタンを押すことでモジュールの図(ハイパーリンク・グラフ)の一部を赤く塗ることができます。
しかし、赤く塗る作業が一瞬で終わってしまうので面白くない、とKuwataさんに言われました。jsAnim(http://jsanim.com/)とJSDeferred(http://cho45.stfuawsc.com/jsdeferred/doc/intro.html)あたりを組み合わせてなんとかしようか、とも思ったのですが、どうせ使い捨てで大したことはしないので、ツギハギして小さなライブラリを作ったほうが手っ取り早いし、それで十分だろうと判断しました。
「タスク」と呼ぶことにした“作業を行うオブジェクト”達 task1, task2, ... を配列にしたものを、先頭から順にゆっくりと実行するためのライブラリ taskman.js を作りました。70行(実質は40行程度)しかないソースなので、このエントリーの最後に貼り付けます。http://www.chimaira.org/misc/fire2.html の [fire] というボタンがデモになっています。乱数を使っているので、赤く塗られる経路は実行ごとに変化します*2。
ここでのタスクとは、init() と step() というメソッドを備えたオブジェクトです。init()でまず初期化してから、step()が一定の間隔で呼び出されます。step() がnullを返したら作業終了というお約束です。作業が継続するときのstep()の値は、null以外なら何でもいいのですが、そのオブジェクト自身を戻すようにすると、task.init().step().step() のようなメソッドチェーンが書けてちょっと便利です。
taskman.jsに含まれるtaskman.Nopとtaskman.Idleがタスクの(つまらない)例です。http://www.chimaira.org/misc/fire2.html では、fire2.js内のfire.Burnが赤く色を塗るタスクになっています。このサンプルを見れば、使い方は分かると思います。
// -*- coding: utf-8 -*- // taskman.js taskman = {}; taskman.running = false; taskman.start = function(tasks, timeSlice_) { if (taskman.running) { return; } taskman.tasks = tasks; if (!(taskman.tasks instanceof Array) || taskman.tasks.length === 0) { return; // nothing to do } if (typeof timeSlice_ === 'undefined') { taskman.timeSlice = 40; } else { taskman.timeSlice = timeSlice_; } taskman.running = true; taskman.current = 0; taskman.task = taskman.tasks[taskman.current]; taskman.task.init(); taskman.step(); }; taskman.step = function() { if (!taskman.running || !taskman.task) return; var result = taskman.task.step(); if (result === null) { taskman.current++; if (taskman.tasks.length <= taskman.current ) { taskman.running = false; taskman.current = -1; taskman.task = null; return; } taskman.task = taskman.tasks[taskman.current]; taskman.task.init(); } setTimeout("taskman.step()", taskman.timeSlice); }; taskman.Nop = function () { if (!(this instanceof taskman.Nop)) return new taskman.Nop(); return true; }; taskman.Nop.prototype.init = function () { return this; }; taskman.Nop.prototype.step = function () { return null; }; taskman.Idle = function (count) { if (!(this instanceof taskman.Idle)) return new taskman.Idle(count); this.count = count; return true; }; taskman.Idle.prototype.init = function () { return this; }; taskman.Idle.prototype.step = function () { if (this.count <= 0) return null; this.count--; return this; }; // end
*1:この図はたまたま日本語を使ってませんが、日本語も使えますよ。http://d.hatena.ne.jp/m-hiyama/20111111/1320987809 を参照。
*2:もちろん、同じ経路が何度も続くこともあります。