Hit-a-Hintでヒント文字を指定できるようにした

時間ができたのでJavaScriptを弄ってる。

この前のHit-a-Hintブックマークレットでは、ヒントに数字しか使えないのでちょっと不便だった。だって数字キーって押しにくいじゃん。

Arc Cosineさんの下のふたつのハックを元に、Hit-a-Hintブックマークレットを好きな文字でできるように改良してみた。

;キー版のみ。

javascript:(function(){var hintkeys='asdfghjkl';var maxdigit=4;function createText(m){var ret='';var l=hintkeys.length;var d=maxdigit*l;while(m>=0){while(m-l*d<0){d--;}ret=hintkeys.charAt(m-l*d)+ret;m=d-1;}return ret;}function retrieveNumber(w){var ret=0;var wlen=w.length;for(var i=0;i<wlen;i++){var fix=(i==0)?0:1;var t=w.charAt(wlen-i-1);ret+=(hintkeys.indexOf(t)+fix)*Math.pow(hintkeys.length,i);}return ret;}var bgcolor = '#FF0';var bghighlight = '#0F0';var color = '#000';var hintlist = new Array();var hintedlinks = new Array();var map = new Array();var choices = new Array();var mapindex = 0;var choice = '';var keycodemapping = {'13':'Enter','27':'Esc','8':'Bkspc','44':','};for(var i=0;i<hintkeys.length;i++){var ithkey=hintkeys[i];keycodemapping[ithkey.charCodeAt(0)]=ithkey;}var originalTitle = document.title;function drawHints(){document.addEventListener('keypress',interpretKeyStroke,true);document.title+=' - ';var allLinks = document.getElementsByTagName('a');var viewportStart = window.pageYOffset - 5;var viewportEnd = viewportStart + window.innerHeight + 10;for (var i = 0;i<allLinks.length;i++){linkYcoord = getAbsoluteY(allLinks[i]);if(linkYcoord > viewportStart && linkYcoord < viewportEnd && allLinks[i].href != '') {hintedlinks.push(allLinks[i]);}}for (var i = 0;i<hintedlinks.length;i++){var hint = document.createElement('span');hintlist.push(hint);hint.style.backgroundColor=bgcolor;hint.style.color=color;hint.style.position='absolute';hint.innerHTML = createText(mapindex);map[createText(mapindex)]=hintedlinks[i].href;mapindex++;hintedlinks[i].appendChild(hint,hintedlinks[i]);}}function getAbsoluteY(element){var y = 0;while (element) {y += element.offsetTop;element = element.offsetParent;}return y;}function removeHints(){for (var i=0;i<hintedlinks.length;i++){hintedlinks[i].removeChild(hintlist[i],hintedlinks[i]);}choice='';document.title=originalTitle;document.removeEventListener('keypress',interpretKeyStroke,true);delete map;delete hintlist;delete hintedlinks;delete choices;}function interpretKeyStroke(e){e.preventDefault();var key=keycodemapping[(typeof event!='undefined')?window.event.keyCode:e.keyCode];if(key=='Enter'){choices.push(choice);for (var i=0;i<choices.length;i++) {if(map[choices[i]]!=undefined){window.open(map[choices[i]]);}}removeHints();}else if(key==','){if(choice!=''){choices.push(choice);hintlist[retrieveNumber(choice)].style.backgroundColor=bghighlight;choice = '';document.title+=',';}}else if(key=='Esc'){removeHints();}else if(key=='Bkspc'){if(choices.length){if(choice!=''){choice='';}else{hintlist[retrieveNumber(choices.pop())].style.backgroundColor=bgcolor;document.title=originalTitle+' - '+choices.join(',');}}}else if(key == undefined){removeHints();}else{choice+=key;var choicestring = (choices.length) ? choices.join(',')+','+choice : choice;document.title=originalTitle+' - '+choicestring;}}drawHints();})();

長いので、Operaで使うときはブックマークに↑を登録してニックネームをhitahintとでもし、キーボードショートカットのApplicationのところに何かキーを作ってGo to page, "hitahint"とすると管理がラクだよ。

Firefoxでは動かなかった。ヒントは表示されるが、キーを押した瞬間に全部消える。ドウシテ?

元の作者さんのページを見たら、7月7日に更新してたのでそれも取り入れた。

解説

  • 一番最初のvar hintkeys='asdfghjkl'というところを変えて自分の好きな文字キーを指定できる。このお手軽さを出したかったので苦労した。
  • 次のvar maxdigit=4;というところを変えると最大桁数を変えられる。ヒントが"asdfghjkl"の9個で最大桁数が4の場合、リンク数は9の4乗で6561つまで処理できるはず。
    • 表示画面にそれ以上リンクがある場合はちゃんと動作しないはず。
    • もしヒントを"abcdefghijklmnopqrstuvwxyz"とフルセットのアルファベットにしている場合は、var maxdigit=2;ぐらいで十分だと思う。
  • その後に定義されている二つの関数createTextretrieveNumberはそれぞれ、ヒントの番号 (表示画面内の最初のリンクから0,1,2,3,...と続く) から文字列 (同様に、この場合だとa,s,d,f,...,aa,as,ad,...と続く)に変換する関数と、その逆。
function createText(m){var ret='';var l=hintkeys.length;var d=maxdigit*l;while(m>=0){while(m-l*d<0){d--;}ret=hintkeys.charAt(m-l*d)+ret;m=d-1;}return ret;}
function retrieveNumber(w){var ret=0;var wlen=w.length;for(var i=0;i<wlen;i++){var fix=(i==0)?0:1;var t=w.charAt(wlen-i-1);ret+=(hintkeys.indexOf(t)+fix)*Math.pow(hintkeys.length,i);}return ret;}
  • 元々var keycodemappingですべてのキーを指定していたところを、var keycodemapping = {'13':'Enter','27':'Esc','8':'Bkspc','44':','};だけにして、その直後の部分で自分指定のヒントキーを追加している。
for(var i=0;i<hintkeys.length;i++){var ithkey=hintkeys[i];keycodemapping[ithkey.charCodeAt(0)]=ithkey;}

それより下は元のスクリプトからほとんど変えてない。

  • ヒント表示中はOperaのキーボードショートカットを殺すためにe.preventDefault();というのを追加。(Arc Cosineさんより)
  • 他、些細な変更3〜4箇所。いづれもcreateText()とretreaveNumber()を挿入したのみで、基本的には同じ。
  • ヒントのリストをhintlinksmapという2つの配列で管理しているのが気持ち悪い。でもちゃんと動いてるから変えたくない。
    • hintlinksを番号 (1,2,3,...)、mapをヒント文字 (a,s,d,...) で統一してみた。
  • ヒントを挿入する要素の横幅が狭いとき?はヒントがちゃんと表示されないことがある。気にしない。
  • Hit-a-Hintが最も有効なページの一つ。