.
(↓)のVimperatorでサイトごとにキーバインドを指定するコードの改良版。
ニコニコ動画を快適化するvimperator設定まとめ
http://anond.hatelabo.jp/20080803202321
これには致命的なバグがあり、グローバルなkey mappingを上書きした場合removeされたままになってしまう!
そこでスクラッチから書き直した。
_vimperatorrc内でaddSetting(array);という呼び出しをする。
すると、arrayがliberator.mappings.localMapSettingsという配列にpushされる。
ここでarrayは、
[
regex, // urlを表すregex
ar_1,
ar_2,
...
ar_n,
]
という形の配列。
ただし、ar_iは、
1. [key1, action1], // keyは押すキー(文字列でも文字列からなる配列でも良い)、actionは関数 2. [key2, cmd1, 1], // cmdはコマンド 3. [modes, keys, desc, action, extra],
のどれかのタイプの配列。
2のタイプの配列の第3引数は、:cmdname<CR>のように、引数無しにそのままコマンドを実行するかどうかを指定する。
:cmdname<Space>のように、引数を必要とするので<CR>はユーザに入力させる場合は0を指定する。
LocationChangeするたび以下のように動く。
※以下の12行目の&は&に変換してください。増田だとスーパーpre記法内でなぜか&を書けない件。
javascript <<EOF (function(){ liberator.mappings.localMapSettings = []; function addSetting(se){ liberator.mappings.localMapSettings.push(se); } function $(id){ return content.document.getElementById(id); } function $X(expr, index){ var result; if(expr != undefined && index != undefined){ try{ result = liberator.buffer.evaluateXPath(expr).snapshotItem(index); }catch(e){ liberator.echoerr('$X: ' + e) } }else if(index == undefined){ try{ result = liberator.buffer.evaluateXPath(expr); }catch(e){ liberator.echoerr('$X: ' + e) } }else{ liberator.echoerr('invalid aruguments: $X'); } if(result == null){ liberator.echo('$X: none'); return 0; }else{ return result; } } function url(url){ if(url){ content.location.href = url } return content.location.href; } // nicovideo var nico_general = [ /^http:\/\/www\.nicovideo\.jp/, [["h"], "History", function(){ url('http://www.nicovideo.jp/history') } ], ["cm", "MyPage", function(){ url('http://www.nicovideo.jp/my') } ], ["cn", "New Arrival", function(){ url('http://www.nicovideo.jp/newarrival') } ], ["cf", "Focus Search Input", function(){ $X('//*[@class="search_input"]',0).focus() } ], ]; addSetting(nico_general); var nico_top = [ /^http:\/\/www\.nicovideo\.jp(|\?g=\w+)/, [["Cm","CM"], "Music Category", function(){ url('http://www.nicovideo.jp/?g=music') } ], [["Cg","CG"], "Game Category", function(){ url( 'http://www.nicovideo.jp/?g=game') } ], [["Cs","CS"], "Science Category", function(){ url('http://www.nicovideo.jp/?g=science') } ], [["Cr","CR"], "R-18 Category", function(){ url('http://www.nicovideo.jp/?g=r_18') } ], [["Ca","CA"], "Animal Category", function(){ url('http://www.nicovideo.jp/?g=animal') } ], [["Cn","CN"], "Nature Category", function(){ url('http://www.nicovideo.jp/?g=nature') } ], [["Cl","CL"], "Lecture Category", function(){ url('http://www.nicovideo.jp/?g=lecture') } ], ]; addSetting(nico_top); var nico_search = [ /^http:\/\/www\.nicovideo\.jp\/(search|tag)/, [["cs"], "Sort Search Results", function(){ var select = $X('//form[@name="sort"]/select',0); select.focus(); } ], ]; addSetting(nico_search); // cでコメント入力、Cでコマンド入力、sでシーク、lでボリューム調整、 // pで停止/再生、mでミュートのon/off、vでコメの表示トグル、zでズーム、Ctrl-t<e3><81><a7>"test"とアラート var nico_player = [ /^http:\/\/www\.nicovideo\.jp\/watch/, [ [1], ["p", "q"], "Pause", function(){ liberator.execute(":nicopause") }, {rhs: ':nicopause<CR>', noremap: true} ], [ "m", "Mute audio", "nicomute", 1], [ "v", "Toggle Visibility of Comments", "nicommentvisible", 1], [ "z", "Zoom", "nicosize", 1], [ "c", "Comment", "nicomment", 0], [ "C", "Command", "nicommand", 0], [ "l", "Volume", "nicovolume", 0], [ "s", "Seek", "nicoseek", 0], [ "<C-t>", "Test", function(){ alert('test') } ], ]; addSetting(nico_player); })(); EOF
このように、一つのURLに複数の設定を被せることも出来る。
※以下の5行目の&は&に変換してください。増田だとスーパーpre記法内でなぜか&を書けない件。
liberator.modules.options.add(["localmaploglevel","lmll"], "Define which loglevel local map logs are at", "number", 3, { validator: function(value){ return (value >= 0 && value <= 9) } }); liberator.modules.mappings.storedGlobalMaps = []; liberator.modules.mappings.curLocalMaps = []; liberator.modules.mappings.localMapLog = function(txt){ if(!txt)return; liberator.log(txt, liberator.modules.options["localmaploglevel"]); } // arg: 0 -> error console, 1 -> multiline output, 2 -> multiline output(detailed) liberator.modules.mappings.listCurLocalMaps = function(arg){ var lmaps = liberator.modules.mappings.curLocalMaps; var lmapstx = "現在適用されているローカルマップは\n"; if(arg == 2){ for(var i=0, L=lmaps.length; i<L; i++){ let names = lmaps[i].names; names.forEach(function(name){ lmapstx += name + "\t" + lmaps[i].rhs + "\n"; }); } lmapstx = lmapstx.replace(/</g,'<').replace(/>/g,'>'); liberator.modules.commandline.echo(lmapstx, 'hl-Nomal', 0); return; } for(var i=0, L=lmaps.length; i<L; i++){ let names = lmaps[i].names; names.forEach(function(name){ lmapstx += '「'+ name + '」'; }); } if(arg == 0){ liberator.modules.mappings.localMapLog(lmapstx); }else if(arg == 1){ lmapstx = lmapstx.replace(/</g,'<').replace(/>/g,'>'); liberator.echo(lmapstx); }else{liberator.echoerr('Invalid argument : listCurLocalMaps');} }; liberator.modules.mappings.listStoredGlobalMaps = function(){ var sgmtx = "現在退避されているグローバルマップは\n"; var sgm = liberator.modules.mappings.storedGlobalMaps; for(var i=0; i<sgm.length; i++){ sgmtx += sgm[i].names[0]+"\n" + sgm[i].action+"\n"; } liberator.modules.mappings.localMapLog(sgmtx); }; liberator.modules.mappings.clearLocalMaps = function(){ var logtx = ''; for(var i=0; i<liberator.modules.mappings.curLocalMaps.length; i++){ let names = liberator.modules.mappings.curLocalMaps[i].names; names.forEach(function(name){ logtx += '「'+name+'」'; liberator.modules.mappings.remove(1, name); }); } liberator.modules.mappings.curLocalMaps = [] liberator.modules.mappings.localMapLog('前のロケーションでのローカルマップ'+logtx+'が除去された'); }; liberator.modules.mappings.restoreGlobalMaps = function(){ var logtx = ''; for(var i=0; i<liberator.modules.mappings.storedGlobalMaps.length; i++){ let m = liberator.modules.mappings.storedGlobalMaps[i]; logtx += '「'+m.names[0]+'」'; liberator.modules.mappings.addUserMap([1], m.names, m.description, m.action, {flags: m.flags, rhs: m.rhs, noremap: m.noremap}); } liberator.modules.mappings.storedGlobalMaps = []; if(logtx)liberator.modules.mappings.localMapLog('上書きされていたグローバルマップ'+logtx+'が復帰された'); }; liberator.modules.mappings.storeGlobalMaps = function(se){ var logtx = ''; for(var i=1; i<se.length; i++){ if(liberator.modules.mappings.get(1, se[i][1]) != null){ var orig = liberator.modules.mappings.get(1, se[i][1]); var clone = new Map(orig.modes, orig.names, orig.description, orig.action, {flags: orig.flags, rhs: orig.rhs, noremap: orig.noremap}); liberator.modules.mappings.storedGlobalMaps.push( clone ); logtx += '「'+se[i][1]+'」'; } } if(logtx)liberator.modules.mappings.localMapLog('上書きされようとしているグローバルマップ'+logtx+'が退避された'); }; liberator.modules.mappings.addLocalMaps = function(se){ var logtx = ''; for(var i=1, L=se.length; i<L; i++){ let ar = [], args = [], rhs, extra; if(se[i].length == 3){ // ar : [ key, description, action ] ar = se[i]; if(ar[0].constructor == String) ar[0] = [ ar[0] ]; if(typeof ar[2] != 'function'){ let logtx = 'type error @ ' + se[0] + ', ' + ar[0]; liberator.echoerr(logtx);liberator.log(logtx, 1);continue; } extra = { rhs: ar[1] + "\n" + ar[2].toSource().replace(/^/m, "\t"), noremap: true }; args = [ [1], ar[0], ar[1], ar[2], extra ]; }else if(se[i].length == 4){ // ar : [ key, description, command_name, whether_no_args ] ar = se[i]; if(ar[0].constructor == String) ar[0] = [ ar[0] ]; if(typeof ar[2] != 'string'){ let logtx = 'type error @ ' + se[0] + ', ' + ar[0]; liberator.echoerr(logtx);continue; } let action; if(ar[3]){ action = function(){ liberator.execute( ':'+ar[2] ) }; extra = { rhs: ':'+ar[2]+'<CR>'+"\t"+ar[1], noremap: true }; args = [ [1], ar[0], ar[1], action, extra ]; }else{ action = function () liberator.modules.commandline.open(':', ar[2]+' ', liberator.modules.modes.EX); extra = { rhs: ':'+ar[2]+'<Space>'+"\t"+ar[1], noremap: true }; args = [ [1], ar[0], ar[1], action, extra ]; } }else if(se[i].length == 5){ // ar : [ modes, key, description, action, extra ] ar = se[i]; args = [ ar[0], ar[1], ar[2], ar[3], { rhs: ar[4].rhs, flags: ar[4].flags, noremap: ar[4].noremap } ]; if(args[1].constructor == String) args[1] = [ args[1] ]; args[4].rhs += "\t" + args[2]; }else{ liberator.echoerr('Invalid argument : ' + se[i] ); continue } liberator.modules.mappings.addUserMap( args[0], args[1], args[2], args[3], args[4] ); logtx += '「'+args[1]+'」'; liberator.modules.mappings.curLocalMaps.push(new Map( args[0], args[1], args[2], args[3], args[4] )); } if(logtx)liberator.modules.mappings.localMapLog('ローカルマップ'+logtx+'が付与された');logtx = ''; }; liberator.modules.mappings.applyLocalMapSetting = function(se){ if( !se[0].test(content.location.href) )return; liberator.modules.mappings.localMapLog('次の設定を開始します : ' + se[0], liberator.modules.options["localmaploglevel"]); // 上書きされようとしているグローバルマップを退避する liberator.modules.mappings.storeGlobalMaps(se); // ローカルマップを付与する liberator.modules.mappings.addLocalMaps(se); }; liberator.modules.mappings.setLocalUserMaps = function(){ var logtx = ''; liberator.modules.mappings.localMapLog('ローカルマップの設定開始 @ '+ content.location.href); liberator.modules.mappings.listCurLocalMaps(0); liberator.modules.mappings.listStoredGlobalMaps(); // ## マッピングを初期状態に戻す ## // ローカルマップを除去する liberator.modules.mappings.clearLocalMaps(); // 上書きされていたグローバルマップを復帰する liberator.modules.mappings.restoreGlobalMaps(); // ## ローカルマップをセットする ## liberator.modules.mappings.localMapSettings.forEach(function(se){ liberator.modules.mappings.applyLocalMapSetting(se) }); liberator.modules.mappings.listCurLocalMaps(0); liberator.modules.mappings.listStoredGlobalMaps(); }; autocommands.add('LocationChange', '.*', 'js liberator.modules.mappings.setLocalUserMaps();');
08/10/21 nightlyバージョンのvimperator仕様変更に合わせてliberator.*** -> liberator.modules.*** とリネーム。nightlyバージョンのvimperatorを使ってない人はliberator.modulesのmodulesの部分を削除すれば動きます。
08/09/06 コマンド補完を出なくするパッチを適用(thanks to id:nokturnalmortum。local_mappings.js.diff - 地獄の猫日記 http://d.hatena.ne.jp/nokturnalmortum/20080906#1220643244)
08/08/29 liberator.mappings.listCurLocalMaps(2)で現在のkey mappingをエコー出来るようにした、addSetting()のインターフェースを柔軟化、サンプルコード改善
08/08/27 mなどextraInfo(addUserMap()の最後の引数)のあるマッピングが使えなくなるのを修正、ネーミング改善、addSetting()のインターフェースを柔軟化
08/08/26 リリース
気づいた事や要望やアイデアあったらなんでも書いてちょうだい。
・同じURL内で共有される変数を定義できるようにする。例えば、Googleイメージ検索でj/kでtable row単位で移動できるようにするには、現在何行目にいるかを示す変数がkey mapping間で共有できると便利。同一URLでもタブが違えば異なる変数にする。どう書けばいいのか試行錯誤中。もしかして技術的に難しい?
→2008/9/18追記。どうやれば良いか分からないので放置中。誰か書いてくれると嬉しいです。
local_mappings.jsのせいで_vimperatorrcが600行近くになった。
vimperatorプラグイン書いた - pekeblog!
http://d.hatena.ne.jp/pekepekesamurai/20080908/1220894045
で似たプラグイン作った人がいます。
.
. 8/27追記 本記事末尾のローカルなkey mappingを実現するコードを改良してプラグインにしました。 ↓ Vimperatorでローカルなkey mappingを実現するプラグイン local_mappings.js を書いた。 http://anond.h...