Hit-a-Hintブックマークレット2.0をリリースしたよ

リリースしたよ。

ページは英語なので、使い方はここに書いてあるよ。

開発段階のソースはこのへんで読めるよ。

経緯

前のコードは、自分が JavaScript を本格的に始めて半月ぐらいの時に書いたものだったので、色々な無駄が多かった。そういう部分をずっと整理したいと思っていた。ちょうど良いタイミングで CoolDriver さんからのトラックバック記事↓や、バージョン1.0をリリースしたときの記事にコメントをくれた方がいたので、Firefox でも需要があるのだと知り、書き直しを決定。

整理したのだけど、どちらかと言うと読みにくくなったと思う。まだ変なことしてるところがある。最初の自分の設計が変だったからなのだけど、今変えるのは面倒かなと思って。次バージョンがあるならこのへんもなんとかする。

変更点

多数。一部は後述。

前回からの方針としては、完全な後方互換性と、なおかつ拡張性の追加。

Firefox の完全なサポート。Opera と同じように動くように。

Firefox でも一通りテストはしてあるけど、Opera でしか常用してないので変なところがあったら教えてください。

特徴

ブックマークレットなので、他に必要なものがない

ブックマークに登録するだけで使える。
自分の Firefox には Greasemonkey は入れてないのです。

補助スクリプトで拡張できる

補助スクリプト (UserJS や Greasemonkey など) によって拡張することもできる。(無くてもちゃんと動く)

前回紹介した補助スクリプトはそのまま使える。ただし、CSS を最初に読み込む "HaH-accelerator.js" はちょっと変更したので Generator からアップデートをおすすめします。

拡張の方法は2通り。

下に拡張の書き方の例としてそれぞれを紹介。

その1・はてなスターを押せるように

デフォルトでセミコロンに割り当てられている click key というやつを押せる要素、および、押したときの挙動を追加する。

例えばはてなスターを追加するときに押す画像 (以下便宜的に「スター追加画像」と呼ぶ) の要素には onclick 属性が付いていないので、デフォルトの XPath には含まれない。

ちなみにデフォルトの XPath はこれ。

//a[@href]|//input[not(@type="hidden")]|//textarea|//select|//img[@onclick]|//button|//xhtml:a[@href]|//xhtml:input[not(@type="hidden")]|//xhtml:textarea|//xhtml:select|//xhtml:img[@onclick]|//xhtml:button

そういうものを「疑似クリック」できるようにする。

// ==UserScript==
// @name           HaH-hatenastar
// @namespace      http://d.hatena.ne.jp/edvakf/
// @include        http://*
// ==/UserScript==

(function(){
//とりあえず書いとく。
var w = this.unsafeWindow || window;
if(!w.HitaHint) w.HitaHint = {};
if(!w.HitaHint.types) w.HitaHint.types = [];

//本体
w.HitaHint.types.push({
//新たな XPath を追加したい場合は書く。無くてもよい。
  xpath :
    '//img[@class="hatena-star-add-button"]|//xhtml:img[@class="hatena-star-add-button"]',

//スター追加画像の要素を引数として与えると true を返す関数を書く。必須。
  accessor :
    function(element){
      return (element.className == 'hatena-star-add-button');
    },

/*
//タイプ。スター追加画像要素のヒントに特別な色を付けたい場合のみ書く。無くてもよい。
  type :
    'HaHhatenastar',
//色。ヒントの色。無くてもよい。タイプを指定したときのみ有効。
  color:
    '#0FF',
*/

//アクション。スター追加画像の要素を引数として、疑似クリックを実行したときにする動作を書く。
//これが無い場合は、入力欄などと同じようにフォーカスしてクリックイベントを送るデフォルトの動作が適用される。
  action :
    function(element){
      var e = document.createEvent('MouseEvents');
      e.initMouseEvent('click',true,true,w,1,10,50,10,50,0,0,0,0,1,element);
      element.dispatchEvent(e);
    }
});

})();

こんな感じの UserJS をインストールしておくと、このようにスターを押せるようになる。

スター追加アクションはこちらを参考にした。

Opera な人は疑似キャレットモードでのテキスト選択と合わせて利用すると効果が倍増するかも。

その2・要素に直接空間ナビゲーションでフォーカスできるように

例が Opera 専用でアレなのだが、Firefox でも原理は同じです。

click key は通常は要素にクリックイベントを送ってしまうので、ある要素に hover したいときなどには不便だった。

ヒントを選択して、「'」(クオーテーション。英語キーボードだとセミコロンとエンターの間にあるので右手小指で押しやすい) を押すと、最初に選択したヒントの要素へ空間ナビゲーションできるようにする。

  • HaH-hover.js (こっちはどうせ Firefox では使えないので unsafeWindow を使ってない)
// ==UserScript==
// @name           HaH-hover
// @namespace      http://d.hatena.ne.jp/edvakf/
// @include        http://*
// ==/UserScript==

//とりあえず書いとく。
if(!window.HitaHint) window.HitaHint = {};
if(!window.HitaHint.keys) window.HitaHint.keys = [];

//本体
HitaHint.keys.push({
//空間ナビゲーションする関数必須。引数などは後述。
  func : 
    function(modified,chosenHints){
      if (modified) return;
      var firstHint = chosenHints[0];
      if (firstHint) {
        var ele = firstHint.element;
        if (ele.id) var origId = ele.id;
        else ele.id = 'HaHTempId'
        if (ele.style.navRight) var navRight = ele.style.navRight;
        ele.style.navRight = '#' + ele.id;
        ele.focus();
        document.moveFocusRight();
        document.moveFocusRight();
        ele.style.navRight = navRight ? navRight: '';
        if (origId) ele.id = origId;
        else ele.removeAttribute('id');
      }
   },

//キーの名前 (クオーテーション記号はバックスラッシュエスケープが必要)。必須。
  keyName : '\'',

//キーコード。無くてもよい。これがある場合は、キーの名前ではなくこちらで判断する。
  keyCode : 39
});

これだけ。

関数部分は、modified という引数 (1つでもモディファイアキーが押されていれば true になる) と、chosenHints という配列を引数に取る。

Ctrl キーが押されているかだけを調べたいときなどは、modified を見ずに window.event.ctrlKey などを使えばいい。

chosenHints 配列の各要素はこんなオブジェクトになっているのだが、普通はほとんど気にしなくても上のぐらいの拡張は書けるはず。

hint = {
  element : 要素自身,
  x : 要素の x 座標,
  y : 要素の y 座標,
  action : アクション, //上で書いたように、click キーを押したときの挙動を定義している。
  hintNode : ヒント要素, //ヒント文字が入っている。span。
  type : タイプ, //上で書いた。hintNode のクラス名になる。
  text : ヒント文字
}

ちなみに chosenHints はちゃんと重複を削除したりの操作はしてあるので、気にせず関数を書けばいいはず。

Space や Enter やセミコロンなどの最初から定義してあるキーの動作もこの方法で変えられる。でもその場合はちゃんと動く保証はない。ソースを見れば変えてもいいキーといけないキーが分かるかも。

あれこれ

  • 別に UserJS なんかでグローバル関数を定義しなくても、ブックマークレットの一番最後の部分の (window.HitaHint) というところに上のようなことをべた書きすることもできる。
  • 上の2つの例のように書けば、補助スクリプトを何個も置いても衝突を気にしなくて大丈夫のはず。
  • この1ヶ月でちょっと Vimperator を使うことがあったのだけど、Vimperator のヒントモードより使い易くしたかった。
    • Vimperator のヒントモードでは最初に「f」キーでヒント開始するか「;」キーでするかで、ヒント選択後に出来ることが違ってくる。ヒントを出す前に選択後のことまで考えたりするなんて、自分には面倒で仕方がない。ヒント選択してワンキーでアクション。これ常識。
    • しかし、iframe にも対応してたりする Vimperator のヒントモードはやっぱりすごいなあと思う。
  • Generator も内部でちょっと変更したのだけど、前ほど入念にチェックしてないので、不具合があったらコメントください。


Opera だと aリンクに対してClick Key で開くんだけど Firefox は Open Key でなきゃ開かないと CoolDriver さんに言われたのを修正しました。Firefox の場合は Click Key を普通のリンクの上で実行すると、そのリンクを現在のタブに開くようにした。(target="_blank"なら新しいタブに開かせようと思ったのだけど、そうすると Firefox のポップアップブロックに引っかかった)
それから、HaH-hatenastar が Greasemonkey で動かなかったのを直した。