aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--feedSomeKeys_2.js338
-rw-r--r--localkeymode.js336
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();');