バーレーンGP予選
今年の予選の第1、第2セッションは面白いな。ただ、今回みたいに赤旗とかのトラブルが出たときの影響がちょっと大きすぎる気がするから、その辺はもうちょっと何とかした方がいいかも。
で、それに対して第3セッションは、最初何をやっているのかさっぱり分からなかった。第3セッションには、燃料制限があるってことは知っていたけど、それがどういう意味なのかよく分かってなかったっつーか。
要するに、第3セッションは
- 第3セッション開始時に入れた燃料で、決勝(のスタート)を走らないといけない。だから、第3セッション開始時には決勝スタートのための燃料を入れておく。
- 予選中に使用した分の燃料は(周回タイムが極端に遅くなければ)周回数に応じて、決勝前に補給できる。
- 予選でタイムを出すためには、燃料が少ない方がいい。だから、第3セッションの序盤は、終盤のタイムアタックのために燃料を減らすことに費やされる。
- ある程度燃料が減った段階で、タイムアタックをはじめるチームが出てくる。ただし戦略によって、どの程度燃料が減った段階でタイムアタックをはじめるかの判断は異なるし、第3セッション中もタイヤ交換も可能なので、戦略の幅は非常に大きい。
って感じなのね。で、そういうことだって分かった上で、今回の予選の結果を考えると、フェラーリの1−2ってことの意味は、現状ではまだ何とも判断できない。少なくとも去年よりは速そうではあるけれども、ルノーやホンダとは戦略が全然違うんじゃん?って可能性がものすごく高そうだし。
なんて感じで、今年の予選の第3セッションは、単に見た目(序盤にみんなでだらだら走っている意味)がわかりにくいというだけでなく、その結果から決勝や各チームの実力が読みにくい、という意味でも分かりにくい。
ただ、このわかりにくさは、一応考えればそれなりのパターンに整理することは可能なので、ある程度情報がそろって、その解釈の仕方に慣れてくれば、F1の(見ての)面白さを増す効果があるかもしれない。
それにしても予想を外さなかったのはマクラーレンだな。今年も信頼性に問題がありそうと誰もが思っていたと思うけど、見事にその予想が当たっていることを示して見せた。しかも突然リアサスペンションが折れるって、去年のリアウイングが飛んだシーンとか思い出したな。ああいかん、今回も最初にリアウイングが外れて、それでリアサスペンションを壊したのか。サスペンションが折れた方が先かと思っていたよ。
一応レースに参加できているアグリチームは、ひとまず最初の数戦は走行距離を稼いでデータと(井出は)経験を蓄えつつ、他のチームの邪魔にならないように頑張りましょうって感じか。車が壊れないようならば、琢磨の方は2、3戦で下位チームの争いに参加できるようになるんじゃないかな。
form.onsubmitとEvent.observeに関する実験
<script type="text/javascript" src="prototype.js"></script>
<form id="test" onsubmit="return formOnSubmit();">
<input type="submit">
</form>
<script type="text/javascript">
function formOnSubmit()
{
alert('formOnSubmit');
return false; /* A */
}
Event.observe('test', 'submit', function() {alert('EventObserve'); return false; /* B */}, false);
</script>
上記のようなコードを、Firefox 1.5.0.1、IE 6.0.2900.2180、Opera 8.5でテスト。
イベントハンドラー実行順序はどれも、form.onSubmit→Event.observeの順序。ただし、form.onSubmitの戻り値がfalseであっても、必ずEvent.observeした関数も呼ばれてしまう。
つまり、もしform.onSubmitのコードがバリデーションロジックで、バリデーションでエラーがあったのでfalseを返したとしても、その後に続くEvent.observeした関数=Ajaxリクエストコールは呼ばれてしまうし、Event.observeした関数には、バリデーションロジックの戻り値は伝わってこない。HTML_QuickFormみたいに、alertでエラーメッセージを表示するバリデーションロジックを出力する場合は、単純にEvent.observeしてAjaxリクエストコードを追加するパターンは使えない*1。
ちなみに、A、Bの戻り値をいろいろ変えて試してみたところ、そのあたりの処理にはブラウザ互換性がないことが分かった。
| A:true/B:true | A:true/B:false | A:false/B:true | A:false/B:false | |
| Firefox | 止まらない | 止まる | 止まる | 止まる |
| IE | 止まらない | 止まる | 止まらない | 止まる |
| Opera | 止まらない | 止まらない | 止まる | 止まる |
Firefoxはどちらかがfalseだったら、submit動作が止まる。IEは最後に実行されたイベントハンドラーがfalseを返したときのみ、submit動作が止まる。Operaはform.onsubmitでfalseを返したときのみ、submit動作が止まる。なんだか面倒くさいなー。
ちなみに、
Event.observe('test', 'submit', function(event) {alert('EventObserve'); Event.stop(event);}, false);
なんてしちゃえば、すべてのブラウザでsubmit動作が止まったんで、自分のコードで確実にsubmit動作を止めたければ、Event.stopを使えばいいのかな。
で、トータルでどうすればいいのかいろいろ試行錯誤して、
var currentHandler = $('test').onsubmit;
$('test').onsubmit = undefined;
Event.observe('test', 'submit', function(event) {
if ((typeof currentHandler == 'function') && (!currentHandler())) {Event.stop(event);return;}
alert('EventObserve');
Event.stop(event);
}, false);
なんて感じで、デフォルトのonsubmitを強引に後から追加したイベントハンドラーの中で実行し、その結果によって後から追加したイベントハンドラーのメイン処理を実行するかどうか分岐できるようにしてみたんだけど、FirefoxとOperaではこの方法でいけるんだけど、IEでは思ったように動いてくれないなー。
でも、どうにかしたいんだったら、こういう方向のアプローチくらいしかない気がする。JavaScriptのイベントハンドラー周りの実装っていまいち理解できていないから、その辺をちょっとお勉強してから、もう一度チャレンジしてみるか。
*1 Event.observeした関数内でもう一度バリデーションロジックを呼んだら、エラーがあったときに2回もalertが表示されてうざい
AjaxHandlerにあった方がいい分岐パターン
実際にいくつもアプリケーションを作ってみないと本当のところは分からない*1気がするけど、さすがにテストのためにそれなりの規模のアプリケーションを書く気にもなれないから、ひとまず思考実験してみよう。
まず思いつくのはフォームからの投稿をAjaxで代替処理する際に、JavaScriptバリデータと連係し、バリデーションの結果がfalseだったら、XmlHttpRequestが発動する前に処理を停止する、という流れ。これは非常によく使いそう。
で、ポイントとなるのはJavaScriptバリデーションコードをどうやって書くのか、というところ。
もしも自前でバリデーションコードを書くのならば、AjaxHandlerが生成するJavaScriptコードで、XmlHttpRequestの実行前にリクエストを実行するかどうかを判断するためのコールバックを用意し、そこにバリデーションコードを書けばいい。
なんにしろこういうパターンは絶対にあるだろうから、Ajax.*を生成する直前に呼ぶcheckValidCallbackとかを作って、その結果がfalseだったら処理を中断する、というオプションを作った方がいいだろう。
ただ、バリデーションコードがライブラリ化されている場合、自分でその実行タイミングをハンドリングできない可能性もある。たとえば、AjaxHandlerと同じように、Event.observeでバリデーション処理をハンドリングするようなバリデーションライブラリだったら、その実行タイミングはイベントハンドラーの実行順序に依存する。
また、HTML_QuickFormみたいにform.onsubmitに直接関数が記述されるようなパターンの場合、同じformのsubmitにEvent.observeしたら、どういう処理が行われるのか、という問題がある。
「まずform.onsubmitに直接記述された関数が呼ばれ、その戻り値がfalseでなかった場合にのみEvent.observeした関数が呼ばれる」なんて感じだといいのだが、「Event.observeしたら、form.onsubmitに記述されていた関数呼び出しが無効になる」とか、「Event.observeした関数とform.onsubmitに記述された関数の実行順序は不定」とかだと、話はややこしくなる。
まずはその辺の挙動について調査してみるか。
*1 Ajaxを実用レベルで活用したアプリケーションに関する経験が足りなすぎる
HTML_AjaxHandlerのサンプルその2
一番基本となる、Ajax.Requestを使うパターンを作ってなかったんで、そっちも作ってみた。
デモの内容は、最小値と最大値を入力してフォームをsubmitする(Σボタンをクリック or ENTERキーを押す)と、最小値から最大値までを加算した式と結果を表示する。
HTMLは、
<form id="rangeform"> min: <input type="text" name="min" id="min" /> max: <input type="text" name="max" id="max" /> <input type="submit" value="Σ"> </form> <span id="range"></span>
な感じで、HTML_AjaxHandlerでハンドラーを登録するPHPコードは、
require_once 'HTML/AjaxHandler.php'; $ajax =& new HTML_AjaxHandler(); $ajax->addHandler( 'rangeform', 'submit', array( 'url' => 'api.php', 'method' => 'get', 'parameters' => 'command=range', 'onSuccess' => 'rangeSuccess', ) ); echo $ajax->toJavaScript();
な感じになる。前のデモと違って、containerがセットされていないから、使われるAjax.*はAjax.Requestになる。で、onSuccessのコールバック関数はrangeSuccessね。
わざわざAjax.Requestを使う意味をだすために、サーバーサイド(api.php?command=range&min=[最小値]&max=[最大値])では、
$min = intval($_GET['min']);
$max = intval($_GET['max']);
$range = range($min, $max);
$sum = 0; foreach ($range as $num) {$sum += $num;}
echo '{expression: "' . implode(' + ', $range) . '", sum: ' . $sum . ' }';
なんて感じでJSON形式で結果を返している。で、Ajax.RequestのonSuccessコールバックに登録されたrangeSuccess関数では、
function rangeSuccess(res)
{
eval('var result = ' + res.responseText + ';');
var html = result['expression'] + ' = ' + result['sum'];
$('range').innerHTML = html;
}
なんて感じで、evalして計算式文字列と結果数値を取り出して、その内容から出力を組み立てている。
なんてのが、Ajax.Request+自前のコールバック関数を使った場合の記述パターンになる。