onclickでごにょごにょする HTML要素があって、そいつに Tabキーでフォーカスをあてたいなと思ったときに最初に思いついたのが
<a href="javascript:void(0)">ほげほげ</a>*
のように Aタグで囲う方法。遅まきながら、この時初めてステータスバーに javascript:void(0)と表示される何もしないリンクの存在意義に気付いた。こいつがあると Tabキーでフォーカスをあてられるし、Enterキーでクリックすることもできる。そしてスクリプト(onclickハンドラ)で処理できる。簡単かつ使える範囲の広い方法。javascriptを切ってるブラウザではそもそも onclickハンドラが動かないから javascriptスキーマを使うことの是非は問われないし。onclickハンドラでキーボードとマウスをまとめて処理できることや、対応するブラウザの多さなど、書けば書くほどこれしかないって思えてくる。
次に考えついたのが、9日に "tabindexはフォーカスの順番を決めるだけのものではないのだなぁ。" と書いたように、IE(>=5.0)や Firefox1.5で、tabindexを付けることでリンクやフォームの部品以外の要素をフォーカス可能にする方法。
IEの場合、
<element>.tabIndex = 0 // 0 => フォーカス可能にはするが、タブオーダーは指定しない <element>.onkeypress = function(){ if(event.keyCode == 13) this.click() } // Enter->Click
で済むが、Firefoxの場合 click()が実装されてるエレメントがフォームの部品などに限られているので
<element>.tabIndex = 0 // 同上 <element>.onkeypress = function(event){ if(event.keyCode == 13) { var event2 = document.createEvent("MouseEvents"); event2.initMouseEvent("click", true, true, event.view, event.detail, event.screenX, event.screenY, event.clientX, event.clientY, event.ctrlKey, event.altKey, event.shiftKey, event.metaKey, 0, null); this.dispatchEvent(event2); } }
と、かなり冗長。
* href="" だとそのページ自身へ移動(=リロード)してしまう。void(0)以外に href="#" を使う流派もある様子(でもイマイチ)。
あるページを読んでいる。読み進んでいく。「次のページへ」というリンクがある。タブキーを押す。ページがギュンッと巻き戻って一番最初のリンク*にフォーカスが移動する。さらにタブを押す、押し続ける。サイドバーのリンクを順にたどってフォーカスがページ下方へ移動していく。やがてまたもページが巻き戻る。今度は本文中のリンクを順に下方にたどっていく。やっと「次のページへ」にたどり着く。
今見ているリンクにフォーカスを合わそうとしているのに、フォーカスはとんでもないところに飛んでいく。タブキーを押すことによってスクロール位置が変わるのが非常にストレス。タブキーでフォーカスが移動する先は現在表示されてる(フレームに収まってる)部分だけで良い。
HTML作成者が tabindexをリンクやフォームに割り振っていた場合 tabindexの小さい順にフォーカスが移動するのだった。tabindexが一つでも指定されていればそちらを優先すべきだな。(といっても、tabindexが指定されていて欲しいのは自分がよく使うページだけだけど。不特定の人に見せるのが目的のページで押し付けは良くない。)
tabindexはフォーカスの順番を決めるだけのものではないのだなぁ。
ありものは↓。
Mouseless Browsingを使っているが Find As You Typeと競合するのでテンキーを Mouseless Browsing専用にしている。そうすると、テキストフィールドに数字を入力するのが不便(テンキーが使えないので)。
自前でやる場合、次にフォーカスを得るエレメントがどれなのかを自前で探すのは避けたいところ。それをするんだったら greasemonkeyで onblurを捕まえて、次にフォーカスを得るエレメントを順に辿っていって、最初に見つかったブラウザのフレーム内に収まってる(=見えている)エレメントを focus()すればいいはず。
* 普通はソーステキスト中に一番最初に登場したもの。tabindexが指定されていれば最小の tabindexを与えられたもの。
知らない。知らないよこんな便利なコマンド。
EXPLAIN sql-statement;
ならドキュメントに載ってるけど、これが返すのは
0|Integer|5|0| 1|MustBeInt|0|0| 2|MemStore|0|0| 3|IfMemZero|0|25| 4|IfMemPos|0|8| 5|Pop|1|0| ……
みたいな SQLがコンパイルされた結果の、VMが逐一実行する命令のリストだから腰を据えないと解読できない。
それに対して
EXPLAIN QUERY PLAN sql-statement;
が返すのは
0|0|TABLE MyBooks USING PRIMARY KEY
みたいな、テーブルに対する問い合わせのリスト。インデックスが使われるのかどうかもわかる。
Amazonで「七竈」「七竃」「桜庭 一樹」「可愛そうな大人」、いずれを検索しても『少女七竈と七人の可愛そうな大人』は見つからない。ISBNで検索してやっと見つかる。4048737007。難儀なタイトルを付けたものだ。
と、そうではなくて、近所の本屋がだらしなくて 4軒まわっても本を見つけられなかった。Gyaoでドラマが配信されてる『少女には向かない職業』はその内の 3軒で平積みだったというのに。何故に新刊を隣に並べてまとめて売ろうとしない。
Rubyist Magazine - RubyKaigi2006特別号(6月10日 午前の部)より。
Ruby オンリーのカンファレンスを Ruby ゆかりの地・日本で開催できる喜びを寿ぎ*ました。
難易度高すぎ(笑) (゜Д゜;)ハッ!俺だけ?
* 再変換(ATOKの場合、文字入力が可能な場所で選択して Shift+変換)すると正解がわかる。
/ItemLookupResponse /ItemLookupResponse/OperationRequest /ItemLookupResponse/OperationRequest/HTTPHeaders /ItemLookupResponse/OperationRequest/RequestId /ItemLookupResponse/OperationRequest/Arguments /ItemLookupResponse/OperationRequest/RequestProcessingTime /ItemLookupResponse/OperationRequest/HTTPHeaders/Header /ItemLookupResponse/OperationRequest/Arguments/Argument[1] /ItemLookupResponse/OperationRequest/Arguments/Argument[1] /ItemLookupResponse/OperationRequest/Arguments/Argument[1] /ItemLookupResponse/OperationRequest/Arguments/Argument[1] /ItemLookupResponse/OperationRequest/Arguments/Argument[1] /ItemLookupResponse/OperationRequest/Arguments/Argument[1] /ItemLookupResponse/OperationRequest/Arguments/Argument
pathが重複している。
def xpath path_elements = [] cur = self path_elements << __to_xpath_helper( self ) while cur.parent cur = cur.parent path_elements << __to_xpath_helper( cur ) end return path_elements.reverse.join( "/" ) end
pathの各要素は __to_xpath_helperで取ってきている。
def __to_xpath_helper node rv = node.expanded_name if node.parent results = node.parent.find_all {|n| n.kind_of?(REXML::Element) and n.expanded_name == node.expanded_name } if results.length > 1 idx = results.index( node ) rv << "[#{idx+1}]" end end rv end
node.expanded_nameを破壊的に変更している。("Argument" -> "Argument[1]")
D:\ruby\lib\ruby\1.8\rexml>diff -u element.rb~ element.rb --- element.rb~ 2005-08-12 21:08:47.000000000 +0900 +++ element.rb 2006-06-27 00:36:58.546875000 +0900 @@ -720,7 +720,8 @@ } if results.length > 1 idx = results.index( node ) - rv << "[#{idx+1}]" + rv += "[#{idx+1}]" end end rv
/ItemLookupResponse /ItemLookupResponse/OperationRequest /ItemLookupResponse/OperationRequest/HTTPHeaders /ItemLookupResponse/OperationRequest/RequestId /ItemLookupResponse/OperationRequest/Arguments /ItemLookupResponse/OperationRequest/RequestProcessingTime /ItemLookupResponse/OperationRequest/HTTPHeaders/Header /ItemLookupResponse/OperationRequest/Arguments/Argument[1] /ItemLookupResponse/OperationRequest/Arguments/Argument[2] /ItemLookupResponse/OperationRequest/Arguments/Argument[3] /ItemLookupResponse/OperationRequest/Arguments/Argument[4] /ItemLookupResponse/OperationRequest/Arguments/Argument[5] /ItemLookupResponse/OperationRequest/Arguments/Argument[6] /ItemLookupResponse/OperationRequest/Arguments/Argument[7]
をを、直っている。というわけで上記は ruby-1.8.4に付随する REXML 3.1.3限定の話でした。
def __to_xpath_helper node rv = node.expanded_name.clone if node.parent results = node.parent.find_all {|n| n.kind_of?(REXML::Element) and n.expanded_name == node.expanded_name } if results.length > 1 idx = results.index( node ) rv << "[#{idx+1}]" end end rv end
3.1.3 からの ChangeLog。(そのうち 3.1.4からの ChangeLogになりそう)
間に合わせにこんなのを紛れ込ませる。
if(::REXML::Version < '3.1.4') module ::REXML class Element def __to_xpath_helper node rv = node.expanded_name.clone if node.parent results = node.parent.find_all {|n| n.kind_of?(REXML::Element) and n.expanded_name == node.expanded_name } if results.length > 1 idx = results.index( node ) rv << "[#{idx+1}]" end end rv end end end end
Apacheと opensslは Windowsでも親切なのに Subversionの気難しいこと。
python gen-make.py -t vcproj --with-http=D:\apache2 --with-apr=〜 〜略〜
で書き出された subversion_vcnet.slnを VC++ 2005 Expressで開いて、mod_authz_svnと mod_dav_svnプロジェクトだけエラーを潰して*ビルドして、出来上がった Release\subversion\*\*.soを D:\apache2\modulesにコピー。
Apacheと svnモジュールの準備が整ったところで D:\apache2\bin\httpd.exeを起動しようとすると MSVCR80.dllがないとか言われる。Apacheのコンパイル時に *.exeや *.dllや *.soと一緒に作成されてた *.*.manifestを手動インストール。
httpd -k install
でサービスを登録して、D:\apache2\conf\httpd.confで
LoadModule authz_svn_module modules/mod_authz_svn.so LoadModule dav_svn_module modules/mod_dav_svn.so <Location /svn> DAV svn SVNPath "D:/svn/rep" </Location>
みたいな設定を追加してリポジトリが見えるのを確認した。
python gen-make.py -t vcproj --with-httpd=D:\apache2 --with-apr=〜 〜略〜
ここにあった気がする。apacheが用意してくれた Makefile.winとか *.makを使ってだと apr, apr-util, apr-iconvはコンパイルできるけど Subversion経由だとこれらがコンパイルできない。だから Subversionには既に出来上がってる libapr.libを使ってほしいのに、その希望を伝える方法がわからない。
--with-apr=DIR --with-apr-util=DIR --with-apr-iconv=DIR
がそれだと思うのに。
* intl3_svn.libは subversion.tigris.orgから DLできる。
みっくるんるん♪はネタとしてよくできてたけど、ハルヒが第12話の学園祭で歌った 2曲は普通にとても良かった。
今日発売のスクールランブル 13巻と、3年前の 5月発売の 1巻を比べてみる。
<ItemAttributes> <Author>小林 尽</Author> <Binding>コミック</Binding> <Creator Role="著">小林 尽</Creator> <Label>講談社</Label> <ListPrice> <Amount>410</Amount> <CurrencyCode>JPY</CurrencyCode> <FormattedPrice>¥ 410</FormattedPrice> </ListPrice> <Manufacturer>講談社</Manufacturer> <ProductGroup>Book</ProductGroup> <PublicationDate>2006-06-16</PublicationDate> <Publisher>講談社</Publisher> <Studio>講談社</Studio> <Title>School Rumble Vol.13 (13)</Title> </ItemAttributes>
<ItemAttributes> <Author>小林 尽</Author> <Binding>コミック</Binding> <Creator Role="著">小林 尽</Creator> <ListPrice> <Amount>410</Amount> <CurrencyCode>JPY</CurrencyCode> <FormattedPrice>¥ 410</FormattedPrice> </ListPrice> <NumberOfPages>161</NumberOfPages> <PackageDimensions> <Length Units="cm">18</Length> </PackageDimensions> <ProductGroup>Book</ProductGroup> <PublicationDate>2003-05-16</PublicationDate> <Publisher>講談社</Publisher> <Title>School Rumble Vol.1 (1)</Title> </ItemAttributes>
ページ数がわからなくなるのは残念だけど、アマゾンの登録するページ数って多分ページ数の書いてある最後のページのこと。だから半端になる。
大抵の本は先頭から数えた紙の枚数をページとしている。最後まで数えたらキリのいい数字になる。
スクールランブルの 13巻を見てみたら奥付に 161Pと書いてある。あれ? 半端。161Pだと巻頭のカラー2Pが含まれていて奥付が含まれていない。純粋な紙の枚数は 161-2+1=160P。OK。この場合は 160+2(カラー)=162Pということにしよう。
この要素からは「講談社」でなく「少年マガジンKC」という値を取得したい。
アマゾンのサイトを見る限り、スクールランブル 13巻には
ジャンル別 > コミック・アニメ・BL > 出版社別 > 講談社 > 少年マガジンKC
というブラウズノードが存在している。何で取得できなくなったのだろう。
ブラウズノードからはレーベルを取得したいだけなので、<Label>に期待した値を入れてくれればそれで済むし、しかも楽なんだけど〜。
月面で一体の死体が発見された。人間と同じ姿ながら、5万年前に死んでいる彼は地球人ではありえない。だが別々に進化したにしては両者は似すぎている。彼は宇宙人なのか地球人なのか。
世紀の発見に沸く地球。様々な分野の科学者集団が一つの死体にアリのように群がり、たちどころに、とはいかないけれど少しずつ、時には意見を対立させながら謎を明らかにしていく過程の興奮すること!
ところが小骨がひとつ引っ掛かっていて、チャーリー(件の死体)の手記にこうある。
「ミネルヴァはどうなったろうか。この禍いの後、果たしてわれわれの子孫はより恵まれた場所で生き延びるだろうか。もし生き延びたとしたら、彼らはわれわれが何をしたか、いくらかでも理解するだろうか。」(206ページ)
これを読んだとき「星を継ぐもの」というタイトルの意味がわかった気がしたし、実際に結末ではそういう結論だったのだけど、その事実と結論された事柄はこの手記を書き残してすぐに死んでしまったチャーリーには知り得ないことのはずなのだが。
「彼らはわれわれが何をしたか、いくらかでも理解するだろうか。」という部分から、チャーリーたちルナリアンが「より恵まれた場所で生き延びる」ために何らかの行動を起こしたのだと思っていたし、実際にルナリアンは死にゆく自分たちの星から脱出するために惑星間の移住計画を推進していたのだからチャーリーの手記に間違いはなく思える。
だが、チャーリーがこの手記を書いている状況を考えると違ってくる。 チャーリーがこれを書いているのは自分が死ぬ寸前のことであり、その原因は対立する二国間の戦争(星の危機に際して戦争!)による破壊だとしたら? チャーリーの敵国はチャーリーの居た月面の基地を破壊したが、チャーリーたちもまた自らの星に存在する敵基地を攻撃して破壊している。
移住計画実現の見通しも立たないうちに始まった、母星を小惑星帯に変え、自らを絶滅に至らしめる戦争のさなかにあっては、あまりにも希望と空想に満ちたメッセージじゃないか。