diff options
author | retlet | 2008-11-05 08:39:33 +0000 |
---|---|---|
committer | retlet | 2008-11-05 08:39:33 +0000 |
commit | 2bfa73504c735e0b2df2d27ee0567adecc3510ff (patch) | |
tree | 5ac21c2051694c638f59b2901de5ddc1702f4aae | |
parent | c4489d32e21b8548976fec780eff4df613697a53 (diff) | |
download | vimperator-plugins-2bfa73504c735e0b2df2d27ee0567adecc3510ff.tar.bz2 |
動作確認とれたのでtrunkからコピー
git-svn-id: http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/branches/1.2@22766 d0d07461-0603-4401-acd4-de1884942a52
-rw-r--r-- | feedSomeKeys_2.js | 338 | ||||
-rw-r--r-- | localkeymode.js | 336 |
2 files changed, 674 insertions, 0 deletions
diff --git a/feedSomeKeys_2.js b/feedSomeKeys_2.js new file mode 100644 index 0000000..f5ac206 --- /dev/null +++ b/feedSomeKeys_2.js @@ -0,0 +1,338 @@ +/** + * ==VimperatorPlugin== + * @name feedSomeKeys 2 + * @description feed some defined key events into the Web content + * @description-ja 定義したkeyイベントをWebページ側へ送ってあげます + * @author teramako teramako@gmail.com + * @version 2.0a + * ==/VimperatorPlugin== + * + * 英語での説明を放棄する + * + * XXX: feedSomeKeys.js の改良版仕様を変更したのでフォーク + * + * keyイベント(正確にはkepressイベント)をWebコンテンツ側へ送る事を可能にするプラグイン + * Gmailとかlivedoor ReaderとかGreasemonkeyでキーを割り当てている場合に活躍するでしょう。 + * それ以外の場面ではむしろ邪魔になる諸刃の剣 + * + * :f[eed]map lhs [...] -> lhsのキーマップをそのままWebコンテンツへ + * :f[eed]map lhs,[num]rhs [...] -> lhsのキーマップをrhsへ変換してWebコンテンツへ + * [num]はフレームの番号(省略時はトップウィンドウへイベントが送られる) + * :f[eed]map -d[epth] {num} ... -> {num}はフレームの番号で :fmap lhs1,{num}rhs1 lhs2,{num}rhs2 ... と同等 + * Gmailの例を参照 + * :f[eed]map -v[key] .... -> 仮想キーコードでイベントを送るように + * + * :fmapc + * :feedmapclear -> 全てを無に帰して元に戻す + * + * :f[eed]map! lhs ... -> "!" をつけると、一旦すべてのfeedKeysを元に戻しての再定義 + * + * autocmdと組み合わせる場合は + * :autocmd LocationChange .* :fmapc + * を最初に登録してください。でないと対象外のページに移ったときに設定が前のものを引きずることになります。 + * + * また、下記設定例はvimperator 2.0pre用で1.2の場合は\を一つに削ってください。 + * + * == LDR の場合 == + * :autocmd LocationChange .* :fmapc + * :autocmd LocationChange reader\\.livedoor\\.com/reader :fmap j k s a p o v c <Space> <S-Space> z b < > + * + * とかやると幸せになれるかも。 + * + * == Gmail の場合 == + * :autocmd LocationChange .* :fmapc + * :autocmd LocationChange mail\\.google\\.com/mail :fmap -depth 4 c / j k n p o u e x s r a # [ ] z ? gi gs gt gd ga gc + * + * とかやると幸せになれるかもしれません。 + * + * == Google Reader の場合 == + * :autocmd LocationChange .* :fmapc + * :autocmd LocationChange www\\.google\\.co\\.jp/reader :fmap! -vkey j k n p m s t v A r S N P X O gh ga gs gt gu u / ? + * + * Greasemonkey LDRizeの場合などにも使用可 + */ + +liberator.plugins.feedKey = (function(){ +var origMaps = []; +var feedMaps = []; + +// keyTableの再定義...ひどく不毛... +const keyTable = [ + [ KeyEvent.DOM_VK_BACK_SPACE, ["BS"] ], + [ KeyEvent.DOM_VK_TAB, ["Tab"] ], + [ KeyEvent.DOM_VK_RETURN, ["Return", "CR", "Enter"] ], + //[ KeyEvent.DOM_VK_ENTER, ["Enter"] ], + [ KeyEvent.DOM_VK_ESCAPE, ["Esc", "Escape"] ], + [ KeyEvent.DOM_VK_SPACE, ["Spc", "Space"] ], + [ KeyEvent.DOM_VK_PAGE_UP, ["PageUp"] ], + [ KeyEvent.DOM_VK_PAGE_DOWN, ["PageDown"] ], + [ KeyEvent.DOM_VK_END, ["End"] ], + [ KeyEvent.DOM_VK_HOME, ["Home"] ], + [ KeyEvent.DOM_VK_LEFT, ["Left"] ], + [ KeyEvent.DOM_VK_UP, ["Up"] ], + [ KeyEvent.DOM_VK_RIGHT, ["Right"] ], + [ KeyEvent.DOM_VK_DOWN, ["Down"] ], + [ KeyEvent.DOM_VK_INSERT, ["Ins", "Insert"] ], + [ KeyEvent.DOM_VK_DELETE, ["Del", "Delete"] ], + [ KeyEvent.DOM_VK_F1, ["F1"] ], + [ KeyEvent.DOM_VK_F2, ["F2"] ], + [ KeyEvent.DOM_VK_F3, ["F3"] ], + [ KeyEvent.DOM_VK_F4, ["F4"] ], + [ KeyEvent.DOM_VK_F5, ["F5"] ], + [ KeyEvent.DOM_VK_F6, ["F6"] ], + [ KeyEvent.DOM_VK_F7, ["F7"] ], + [ KeyEvent.DOM_VK_F8, ["F8"] ], + [ KeyEvent.DOM_VK_F9, ["F9"] ], + [ KeyEvent.DOM_VK_F10, ["F10"] ], + [ KeyEvent.DOM_VK_F11, ["F11"] ], + [ KeyEvent.DOM_VK_F12, ["F12"] ], + [ KeyEvent.DOM_VK_F13, ["F13"] ], + [ KeyEvent.DOM_VK_F14, ["F14"] ], + [ KeyEvent.DOM_VK_F15, ["F15"] ], + [ KeyEvent.DOM_VK_F16, ["F16"] ], + [ KeyEvent.DOM_VK_F17, ["F17"] ], + [ KeyEvent.DOM_VK_F18, ["F18"] ], + [ KeyEvent.DOM_VK_F19, ["F19"] ], + [ KeyEvent.DOM_VK_F20, ["F20"] ], + [ KeyEvent.DOM_VK_F21, ["F21"] ], + [ KeyEvent.DOM_VK_F22, ["F22"] ], + [ KeyEvent.DOM_VK_F23, ["F23"] ], + [ KeyEvent.DOM_VK_F24, ["F24"] ], +]; +const vkeyTable = [ + [ KeyEvent.DOM_VK_0, ['0'] ], + [ KeyEvent.DOM_VK_1, ['1'] ], + [ KeyEvent.DOM_VK_2, ['2'] ], + [ KeyEvent.DOM_VK_3, ['3'] ], + [ KeyEvent.DOM_VK_4, ['4'] ], + [ KeyEvent.DOM_VK_5, ['5'] ], + [ KeyEvent.DOM_VK_6, ['6'] ], + [ KeyEvent.DOM_VK_7, ['7'] ], + [ KeyEvent.DOM_VK_8, ['8'] ], + [ KeyEvent.DOM_VK_9, ['9'] ], + [ KeyEvent.DOM_VK_SEMICOLON, [';'] ], + [ KeyEvent.DOM_VK_EQUALS, ['='] ], + [ KeyEvent.DOM_VK_A, ['a'] ], + [ KeyEvent.DOM_VK_B, ['b'] ], + [ KeyEvent.DOM_VK_C, ['c'] ], + [ KeyEvent.DOM_VK_D, ['d'] ], + [ KeyEvent.DOM_VK_E, ['e'] ], + [ KeyEvent.DOM_VK_F, ['f'] ], + [ KeyEvent.DOM_VK_G, ['g'] ], + [ KeyEvent.DOM_VK_H, ['h'] ], + [ KeyEvent.DOM_VK_I, ['i'] ], + [ KeyEvent.DOM_VK_J, ['j'] ], + [ KeyEvent.DOM_VK_K, ['k'] ], + [ KeyEvent.DOM_VK_L, ['l'] ], + [ KeyEvent.DOM_VK_M, ['m'] ], + [ KeyEvent.DOM_VK_N, ['n'] ], + [ KeyEvent.DOM_VK_O, ['o'] ], + [ KeyEvent.DOM_VK_P, ['p'] ], + [ KeyEvent.DOM_VK_Q, ['q'] ], + [ KeyEvent.DOM_VK_R, ['r'] ], + [ KeyEvent.DOM_VK_S, ['s'] ], + [ KeyEvent.DOM_VK_T, ['t'] ], + [ KeyEvent.DOM_VK_U, ['u'] ], + [ KeyEvent.DOM_VK_V, ['v'] ], + [ KeyEvent.DOM_VK_W, ['w'] ], + [ KeyEvent.DOM_VK_X, ['x'] ], + [ KeyEvent.DOM_VK_Y, ['y'] ], + [ KeyEvent.DOM_VK_Z, ['z'] ], + [ KeyEvent.DOM_VK_MULTIPLY, ['*'] ], + [ KeyEvent.DOM_VK_ADD, ['+'] ], + [ KeyEvent.DOM_VK_SUBTRACT, ['-'] ], + [ KeyEvent.DOM_VK_COMMA, [','] ], + [ KeyEvent.DOM_VK_PERIOD, ['.'] ], + [ KeyEvent.DOM_VK_SLASH, ['/'] ], + [ KeyEvent.DOM_VK_BACK_QUOTE, ['`'] ], + [ KeyEvent.DOM_VK_OPEN_BRACKET, ['{'] ], + [ KeyEvent.DOM_VK_BACK_SLASH, ['\\'] ], + [ KeyEvent.DOM_VK_CLOSE_BRACKET, ['}'] ], + [ KeyEvent.DOM_VK_QUOTE, ["'"] ], +]; + +function getKeyCode(str, vkey) { + str = str.toLowerCase(); + var ret = 0; + (vkey ? vkeyTable : keyTable).some(function(i) i[1].some(function(k) k.toLowerCase() == str && (ret = i[0]))); + return ret; +} +function init(keys, useVkey){ + destroy(); + keys.forEach(function(key){ + var origKey, feedKey; + if (key instanceof Array){ + [origKey, feedKey] = key; + } else if (typeof(key) == 'string'){ + [origKey, feedKey] = [key,key]; + } + replaceUserMap(origKey, feedKey, useVkey); + }); +} +function replaceUserMap(origKey, feedKey, useVkey){ + if (mappings.hasMap(modes.NORMAL, origKey)){ + var origMap = mappings.get(modes.NORMAL,origKey); + if (origMap.description.indexOf(origKey+' -> ') != 0) { + // origMapをそのままpushするとオブジェクト内の参照先を消されてしまう + // 仕方なく複製を試みる + var clone = new Map(origMap.modes.map(function(m) m), + origMap.names.map(function(n) n), + origMap.description, + origMap.action, + { flags:origMap.flags, rhs:origMap.rhs, noremap:origMap.noremap }); + origMaps.push(clone); + } + } + var map = new Map([modes.NORMAL], [origKey], origKey + ' -> ' + feedKey, + function(count){ + count = count > 1 ? count : 1; + for (var i=0; i<count; i++){ + feedKeyIntoContent(feedKey, useVkey); + } + }, { flags:Mappings.flags.COUNT, rhs:feedKey, noremap:true }); + addUserMap(map); + if (feedMaps.some(function(fmap){ + if (fmap.names[0] != origKey) return false; + for (var key in fmap) fmap[key] = map[key]; + return true; + })) return; + feedMaps.push(map); +} +function destroy(){ + try{ + feedMaps.forEach(function(map){ + mappings.remove(map.modes[0],map.names[0]); + }); + }catch(e){ log(map); } + origMaps.forEach(function(map){ + addUserMap(map); + }); + origMaps = []; + feedMaps = []; +} +function addUserMap(map){ + mappings.addUserMap(map.modes, map.names, map.description, map.action, { flags:map.flags,noremap:map.noremap,rhs:map.rhs }); +} +function parseKeys(keys){ + var matches = /^\d+(?=\D)/.exec(keys); + if (matches){ + var num = parseInt(matches[0],10); + if (!isNaN(num)) return [keys.substr(matches[0].length), num]; + } + return [keys, 0]; +} +function getDestinationElement(frameNum){ + var root = document.commandDispatcher.focusedWindow; + if (frameNum > 0){ + var frames = []; + (function(frame){// @see liberator.buffer.shiftFrameFocus + if (frame.document.body.localName.toLowerCase() == 'body') { + frames.push(frame); + } + for (var i=0; i<frame.frames.length; i++){ + arguments.callee(frame.frames[i]); + } + })(window.content); + frames = frames.filter(function(frame){ + frame.focus(); + if (document.commandDispatcher.focusedWindow == frame) return frame; + }); + if (frames[frameNum]) return frames[frameNum]; + } + return root; +} +function feedKeyIntoContent(keys, useVkey){ + var frameNum = 0; + [keys, frameNum] = parseKeys(keys); + var destElem = getDestinationElement(frameNum); + destElem.focus(); + modes.passAllKeys = true; + modes.passNextKey = false; + for (var i=0; i<keys.length; i++){ + var keyCode; + var shift = false, ctrl = false, alt = false, meta = false; + if (useVkey && (keyCode = getKeyCode(keys[i], true))) { + var charCode = 0; + } else { + var charCode = keys.charCodeAt(i); + keyCode = 0; + } + if (keys[i] == '<'){ + var matches = keys.substr(i + 1).match(/^((?:[ACMSacms]-)*)([^>]+)/); + if (matches) { + if (matches[1]) { + ctrl = /[cC]-/.test(matches[1]); + alt = /[aA]-/.test(matches[1]); + shift = /[sS]-/.test(matches[1]); + meta = /[mM]-/.test(matches[1]); + } + if (matches[2].length == 1) { + if (!ctrl && !alt && !shift && !meta) return false; + if (useVkey && (keyCode = getKeyCode(matches[2], true))) { + charCode = 0; + } else { + charCode = matches[2].charCodeAt(0); + } + } else if (matches[2].toLowerCase() == "space") { + if (useVkey) { + charCode = 0; + keyCode = KeyEvent.DOM_VK_SPACE; + } else { + charCode = KeyEvent.DOM_VK_SPACE; + } + } else if (keyCode = getKeyCode(matches[2])) { + charCode = 0; + } else { + return false; + } + i += matches[0].length + 1; + } + } else { + shift = (keys[i] >= "A" && keys[i] <= "Z"); + } + + //liberator.log({ctrl:ctrl, alt:alt, shift:shift, meta:meta, keyCode:keyCode, charCode:charCode, useVkey: useVkey}); + var evt = content.document.createEvent('KeyEvents'); + evt.initKeyEvent('keypress', true, true, content, ctrl, alt, shift, meta, keyCode, charCode); + destElem.document.dispatchEvent(evt); + } + modes.passAllKeys = false; +} + +// -------------------------- +// Command +// -------------------------- +commands.addUserCommand(['feedmap','fmap'],'Feed Map a key sequence', + function(args, bang){ + if(!args.string){ + liberator.echo(template.table("feedmap list",feedMaps.map(function(map) [map.names[0], map.rhs])), true); + return; + } + if (bang) destroy(); + var depth = args["-depth"] ? args["-depth"] : ""; + var useVkey = "-vkey" in args; + + args.arguments.forEach(function(keypair){ + var [lhs, rhs] = keypair.split(","); + if (!rhs) rhs = lhs; + replaceUserMap(lhs, depth + rhs, useVkey); + }); + },{ + bang: true, + argCount: "*", + options: [ + [["-depth","-d"], commands.OPTION_INT], + [["-vkey","-v"], commands.OPTION_NOARG] + ] + } +); +commands.addUserCommand(['feedmapclear','fmapc'],'Clear Feed Maps',destroy); +var converter = { + get origMap() origMaps, + get feedMap() feedMaps, + setup: init, + reset: destroy +}; +return converter; +})(); +// vim: fdm=marker sw=4 ts=4 et: diff --git a/localkeymode.js b/localkeymode.js new file mode 100644 index 0000000..bc93ac1 --- /dev/null +++ b/localkeymode.js @@ -0,0 +1,336 @@ +/** + * ==VimperatorPlugin== + * @name local key mode + * @description assign temporary usermodemap + * @description-ja 一時的なキーマップの割り当てを行います。 + * @version 0.2.1a + * ==/VimperatorPlugin== + * + * Usage: + * + * :togglelocalkeymode - 有効/無効のトグルです。(ステータスバーのアイコンクリックでも切り替え可能) + * :loadkeymaps - 任意のキーマップの読み込みを行う + * :clearkeymaps - loadkeymaps の読み込みを無効にする + * + * .vimperatorrc + * g:localkeymode_enable : [true=有効/false=無効(デフォルト)] + * + * .vimperatorrrc 設定例: + * let g:localkeymode_enable = "true" + * javascript <<EOM + * //[ [url regexp, [ [removekeys], [key, command/function, {noremap:true, count: true, ...}], ... ]], ... ] + * liberator.globalVariables.localKeyMappings= + * [ + * [/www\.nicovideo\.jp\/watch/, [ + * ['p', ':nicopause'], + * ['m', ':nicomute'], + * ['v', ':nicommentvisible'], + * ['s', ':nicoseek! +10'], + * ['S', ':nicoseek! -10'], + * ['z', ':nicosize ', true], + * ['c', ':nicomment ', true], + * ['C', ':nicommand ', true], + * ['t', function() {alert('test');}], + * ]], + * [/www\.hoge\.com/, [ + * ['h l'], // 一時的に削除するキーマップ(スペース区切) + * [['1','0'], ':open http://www.google.com'], + * ['e', '<C-v>?', {noremap:true}], + * ['q', 'd', {noremap:true}], + * ], + * ]; + * EOM + * + * 備考: + * * + */ +liberator.plugins.LocalKeyMode = (function() { + + // アイコン定義 + const DISABLE_ICON = 'data:image/png;base64,' + +'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAADAFBMVEUAAAABAQECAgIDAwMEBAQF' + +'BQUGBgYHBwcICAgJCQkKCgoLCwsMDAwNDQ0ODg4PDw8QEBARERESEhITExMUFBQVFRUWFhYXFxcY' + +'GBgZGRkaGhobGxscHBwdHR0eHh4fHx8gICAhISEiIiIjIyMkJCQlJSUmJiYnJycoKCgpKSkqKior' + +'KyssLCwtLS0uLi4vLy8wMDAxMTEyMjIzMzM0NDQ1NTU2NjY3Nzc4ODg5OTk6Ojo7Ozs8PDw9PT0+' + +'Pj4/Pz9AQEBBQUFCQkJDQ0NERERFRUVGRkZHR0dISEhJSUlKSkpLS0tMTExNTU1OTk5PT09QUFBR' + +'UVFSUlJTU1NUVFRVVVVWVlZXV1dYWFhZWVlaWlpbW1tcXFxdXV1eXl5fX19gYGBhYWFiYmJjY2Nk' + +'ZGRlZWVmZmZnZ2doaGhpaWlqampra2tsbGxtbW1ubm5vb29wcHBxcXFycnJzc3N0dHR1dXV2dnZ3' + +'d3d4eHh5eXl6enp7e3t8fHx9fX1+fn5/f3+AgICBgYGCgoKDg4OEhISFhYWGhoaHh4eIiIiJiYmK' + +'ioqLi4uMjIyNjY2Ojo6Pj4+QkJCRkZGSkpKTk5OUlJSVlZWWlpaXl5eYmJiZmZmampqbm5ucnJyd' + +'nZ2enp6fn5+goKChoaGioqKjo6OkpKSlpaWmpqanp6eoqKipqamqqqqrq6usrKytra2urq6vr6+w' + +'sLCxsbGysrKzs7O0tLS1tbW2tra3t7e4uLi5ubm6urq7u7u8vLy9vb2+vr6/v7/AwMDBwcHCwsLD' + +'w8PExMTFxcXGxsbHx8fIyMjJycnKysrLy8vMzMzNzc3Ozs7Pz8/Q0NDR0dHS0tLT09PU1NTV1dXW' + +'1tbX19fY2NjZ2dna2trb29vc3Nzd3d3e3t7f39/g4ODh4eHi4uLj4+Pk5OTl5eXm5ubn5+fo6Ojp' + +'6enq6urr6+vs7Ozt7e3u7u7v7+/w8PDx8fHy8vLz8/P09PT19fX29vb39/f4+Pj5+fn6+vr7+/v8' + +'/Pz9/f3+/v7////isF19AAAAPElEQVR4nGNYgwYYsAv8/48Q6AeB///7YQBToAkE/v9vggFMgRIQ' + +'+P+/BAYwBQibgcsdEAASmIsGCAsAAE8ZnUuRMbA8AAAAAElFTkSuQmCC'; + const ENABLE_ICON = 'data:image/png;base64,' + +'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAa0lEQVR4nGP0+OzAQApgIkk1Fg3b' + +'efZv59mPRwMjwycU/n/e/wwMDIyfGanmJBaG16gCvAwMDAzogpTZ8AJVQImBgYEBXZAyGySwCWMV' + +'JNcGUWzCWAWhGrABSPQhA3hUMvo9Js1JFCc+ggAAYtsQ+fmaz5UAAAAASUVORK5CYII='; + const BINDING_ICON = 'data:image/png;base64,' + + 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAVElEQVR4nGP8v5eBJMBEmnIs' + + 'Gpz+Mzj9x6OBheEZNmGsguQ5iYXhHjZhrILk2vAVmzBWQXJt4MYmjFWQXBuUsAljFYRqwApi' + + 'MCJ7CSOEZqR/4iMEAOh5DfER9lQKAAAAAElFTkSuQmCC'; + const rhsRegExp = /[ \r\n]+/g; + + var _isEnable; + var _isBindLocalKey = false; + + var _enableTabs = []; + var _names; + + var feedkeysfuncName = typeof liberator.modules != 'undefined' ? + 'liberator.modules.events.feedkeys' : 'liberator.events'; + // utility function + function cloneMap(org) { + return new Map( + org.modes, org.names, org.description, org.action, + {flags:org.flags, rhs:org.rhs, noremap:org.noremap } + ); + } + + var Class = function() function() {this.initialize.apply(this, arguments);}; + + var LocalKeyMode = new Class(); + LocalKeyMode.prototype = { + // 初期化メソッド + initialize: function() { + this.storekeymaps = []; //キー待避用(戻し) + this.delkeychars = []; //キー待避用(削除) + this.keymapnames = []; // 対応URI保持 + this.localkeymaps = []; // キーマップ保持用 + this.completeNames; // 補完用 + this.tabinfo = []; // タブごとの状態保持用 + this.helpstring = ''; + + var global = liberator.globalVariables; + this.panel = this.setupStatusBar(); + this.isEnable = global.localkeymode_enable != undefined ? + window.eval(global.localkeymode_enable) : false; + this.setupEnvironment(); + this.initLocalKeyMap(); + }, + // ステータスバーにアイコンを生成 + setupStatusBar: function() { + var self = this; + var panel = document.createElement('statusbarpanel'); + panel.setAttribute('id', 'localkeymode-status'); + panel.setAttribute('class', 'statusbarpanel-iconic'); + panel.setAttribute('src', self.isEnable ? ENABLE_ICON : DISABLE_ICON); + panel.addEventListener('click', function(e) { self.isEnable = !self.isEnable; }, false); + document.getElementById('status-bar').insertBefore( + panel, document.getElementById('security-button').nextSibling); + return panel; + }, + get isEnable() { + return _isEnable; + }, + set isEnable(value) { + this.panel.setAttribute('src', value ? ENABLE_ICON : DISABLE_ICON); + _isEnable = value; + this.loadKeyMap(); + }, + get isBinding() { + return _isBindLocalKey; + }, + set isBinding(value) { + this.panel.setAttribute('src', value ? BINDING_ICON : + this.isEnable ? ENABLE_ICON : DISABLE_ICON ); + _isBindLocalKey = value; + }, + // 初期処理 + initLocalKeyMap: function() { + if (liberator.globalVariables.localKeyMappings == undefined ) return; + var list = liberator.globalVariables.localKeyMappings; + if (!list) return; + var self = this; + list.forEach( function( items ) { + if ( !(items instanceof Array) || items.length < 2 || !(items[1] instanceof Array) ) return; + self.addLocalKeyMap( items[0], items[1] ); + } ); + this.completeNames = this.keymapnames.map( function(m) { + m = (m+'').replace(/[\/\\]+/g, ''); + return [m+'', 'maps for [' + m + ']']; + } ); + }, + // ローカルキーマップの生成 + addLocalKeyMap: function( uri, items ) { + if (!uri) return; + var keymaps = []; + var delkeys = []; + if (!(uri instanceof RegExp) ) uri = new RegExp(uri.replace(/(?=[^-0-9A-Za-z_@])/g, '\\')); + + for (let i=0; i<items.length; i++) { + var item = items[i]; + if (item.length < 1 || !item[0]) continue; + if (item.length < 2) { + delkeys = delkeys.concat( item[0].split(' ') ); + continue; + } + var key = item[0] instanceof Array ? item[0] : [ item[0] ]; + var command = item[1]; + var extra = item[2] ? item[2]:new Object(); + if (!extra || !extra.rhs) extra.rhs = (item[1]+'').replace(rhsRegExp, ' '); + + if (typeof command != 'function') { + if (command.charAt(0) == ':') + command = new Function( extra.noremap ? + 'commandline.open("", "'+command+'", modes.EX);' : + 'liberator.execute("'+command+'");' ); + else + command = new Function([ feedkeysfuncName, '("', command, '", ', + (extra.noremap ? true : false), ', true)'].join('') ); + } + keymaps.push(new Map([modes.NORMAL], key, 'localkeymap', command, extra) ); + } + this.keymapnames.push( uri ); + this.localkeymaps.push( { keys:keymaps, removekeys:delkeys } ); + }, + releaseClosedTabCache: function() { + var tabs = getBrowser().mTabs; + var tabIds = []; + var tabinfo = this.tabinfo; + for (let i=0, l=tabs.length; i<l; i++) { + tabIds.push( tabs[i].linkedPanel ); + } + for (let i=0; i<tabinfo.length; i++) { + let isExist = false; + for (let j=0, l=tabs.length; j<l; j++) { + if (tabinfo[i].tabId == tabs[j]) { + isExist = true; + break; + } + } + if (!isExist) tabinfo.splice(i, 1); + } + }, + setupKeyMaps: function( keymaps ) { + var self = this; + keymaps.removekeys.forEach( function( key ) { + var org = mappings.get( modes.NORMAL, key); + if (org) self.storekeymaps.push( cloneMap(org) ); + self.helpstring += key+' -> [Delete KeyMap]\n'; + mappings.remove( modes.NORMAL, key); + } ); + keymaps.keys.forEach( function( m ) { + m.names.forEach( function( key ) { + var org = mappings.get(modes.NORMAL, key); + if (org) self.storekeymaps.push( cloneMap(org) ); + else self.delkeychars.push( key ); + } ); + mappings.addUserMap([modes.NORMAL], m.names, m.description, m.action, + {flags:m.flags, rhs:m.rhs, noremap:m.noremap }); + self.helpstring += m.names+' -> '+m.rhs+'\n'; + } ); + this.isBinding = true; + }, + deleteCurrentTabCache: function() { + var tabId = getBrowser().selectedTab.linkedPanel; + var tabinfo = this.tabinfo; + for (let i=0; i<tabinfo.length; i++) { + if (tabinfo[i].tabId == tabId) { + tabinfo.splice(i, 1); + break; + } + } + }, + // ローカルキーマップセット処理 + loadKeyMap: function() { + // 暫定処置 + if (liberator.plugins.feedKey && liberator.plugins.feedKey.origMap.length >0) return; + this.helpstring = ''; + if (this.isBinding) this.restoreKeyMap(); + if (!this.isEnable) { + this.clearTabCache(); + return; + } + var tabinfo = this.tabinfo; + var settings = this.localkeymaps; + var tabId = getBrowser().selectedTab.linkedPanel; + for (let i=0, l=tabinfo.length; i<l; i++) { + if (tabId == tabinfo[i].tabId) { + this.setEnable = true; + this.setupKeyMaps( settings[ tabinfo[i].keyMapIndex ] ); + return; + } + } + + for (let i=0, l=settings.length; i<settings.length; i++) { + if ( this.keymapnames[i].test(content.location.href) ) { + this.setupKeyMaps( settings[i] ); + break; + } + } + }, + clearTabCache: function() { + for (;0 < this.tabinfo.length;) { + this.tabinfo.shift(); + } + }, + // 割り当てていたローカルキーの削除処理 + restoreKeyMap: function() { + if (this.isBinding) { + for (; 0 < this.storekeymaps.length; ) { + let m = this.storekeymaps.shift(); + mappings.addUserMap([modes.NORMAL], m.names, m.description, m.action, + {flags:m.flags, rhs:m.rhs, noremap:m.noremap}); + } + for (; 0 < this.delkeychars.length; ) { + let keys = this.delkeychars.shift(); + mappings.remove( modes.NORMAL, keys ); + } + this.isBinding = false; + } + }, + // その他処理(ユーザコマンド追加等) + setupEnvironment: function() { + var self = this; + commands.addUserCommand(['togglelocalkeymode'], 'Toggle local/global key mapping', + function() { + self.isEnable = !self.isEnable; + }, {} ); + commands.addUserCommand(['loadkeymaps', 'loadlocalkeymaps'], 'Load local key mapping', + function(args) { + if (!self.isEnable) { + liberator.echoerr('localkeymode is disabled'); + return; + } + var arg = (typeof args.string == undefined ? args : args.string); + if (!arg) { + liberator.echo(self.helpstring); + return; + } + self.releaseClosedTabCache(); + self.deleteCurrentTabCache(); + var tabId = getBrowser().selectedTab.linkedPanel; + var names = self.completeNames; + for (let i=0, l=names.length; i<l; i++) { + if (names[i][0] == arg) { + self.tabinfo.push( {tabId: tabId, keyMapIndex: i} ); + self.loadKeyMap(); + return; + } + } + }, { + completer: function(filter) { + var names = self.completeNames; + if (!filter) return [0, names]; + filter = filter.toLowerCase(); + return [0, names.filter( function(el) + el[0].toLowerCase().indexOf(filter) == 0) ]; + } + } ); + commands.addUserCommand(['clearkeymaps', 'clearlocalkeymaps'], 'Clear local key mapping', + function() { + self.clearTabCache(); + self.loadKeyMap(); + }, { + }); + }, + }; + + return new LocalKeyMode(); +})(); + +autocommands.add('LocationChange', '.*', 'js liberator.plugins.LocalKeyMode.loadKeyMap();'); |