-
Notifications
You must be signed in to change notification settings - Fork 0
/
localkeymode.js
345 lines (330 loc) · 12.5 KB
/
localkeymode.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
var PLUGIN_INFO =
<VimperatorPlugin>
<name>localkeymode</name>
<description>assign temporary keymap</description>
<description lang="ja">一時的なキーマップの割り当て</description>
<version>0.2.2</version>
<author homepage="http://d.hatena.ne.jp/pekepekesamurai/">pekepeke</author>
<minVersion>2.2pre</minVersion>
<maxVersion>2.2pre</maxVersion>
<updateURL>http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk/localkeymode.js</updateURL>
<detail><![CDATA[
== 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=
[
[ Url_RegExp,
[ key, command/function, {extra} ],
[...]
],
[/www\.hoge\.com/, [
['h l'], // 一時的に削除するキーマップ(スペース区切で指定)
[['1','0'], ':open http://www.google.com'],
['e', '<C-v>?', {noremap:true}],
['q', 'd', {noremap:true}],
],
];
EOM
||<
Url_RegExp:
設定を有効にしたいURL(正規表現での指定)
key:
割り当てたいキー名(Arrayで複数指定可能)
command/function:
キーに割り当てたいコマンド/メソッド。
コマンドは ':hoge' のように先頭に':'を付加してください。
extra:
noremap, count 等の指定。addUserMap にて使用されます。
]]></detail>
</VimperatorPlugin>;
liberator.plugins.LocalKeyMode = (function() {
// アイコン定義
const DISABLE_ICON = 'data:image/png;base64,'
+'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAZ0lEQVR4nGNcvXo1AymAiSTV'
+'WDSEhISEhITg0cDy+PFjTFGsgmQ6ieXz58+YolgFybXh06dPmKJYBcm1gY+PD1MUqyC5NvDy'
+'8mKKYhWEasAqWlhYiCbS398PYTDOmTOHJCdRnPgIAgBfBxpKyax43AAAAABJRU5ErkJg'
+'gg==';
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 feedKeys = liberator.modules ? liberator.modules.events.feedkeys
: liberator.events.feedkeys;
// utility function
function cloneMap(org, key) {
return new Map(
org.modes, key || org.names, org.description, org.action,
cloneExtraInfo(org)
);
}
function cloneExtraInfo(org) {
let result = {};
for (let name in org)
if (/^(rhs|noremap|count|motion|arg|silent|route)$/.test(name))
result[name] = org[name];
return result;
}
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.getElementById('localkeymode-status');
if (panel) {
let parent = panel.parentNode;
parent.removeChild(panel);
}
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() _isEnable,
set isEnable(value) {
this.panel.setAttribute('src', value ? ENABLE_ICON : DISABLE_ICON);
_isEnable = value;
this.loadKeyMap();
},
get isBinding() _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 + ']'];
} );
autocommands.add('LocationChange', '.*', function () liberator.plugins.LocalKeyMode.loadKeyMap());
},
// ローカルキーマップの生成
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, '\\'));
items.forEach( function( [key, command, extra] ) {
if (!key) return;
else if (!command) delkeys = delkeys.concat( key.split(' '));
else {
key = key instanceof Array ? key : [key];
extra = extra || new Object();
if (!extra || !extra.rhs) extra.rhs = (command+'').replace(rhsRegExp, ' ');
if (typeof command != 'function') {
let cmdName = command;
if (command.charAt(0) == ':')
command = extra.noremap ? function () commandline.open("", cmdName, modes.EX)
: function () liberator.execute(cmdName);
else
command = function () feedKeys( command, extra.noremap, true);
}
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, [key]) );
self.helpstring += key+' -> [Delete KeyMap]<br/>\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, [key]) );
else self.delkeychars.push( key );
} );
mappings.addUserMap([modes.NORMAL], m.names, m.description, m.action,
cloneExtraInfo(m));
self.helpstring += m.names+' -> '+m.rhs+'<br/>\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() {
var self = this;
// 暫定処置
//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) {
var msg = "";
for (; 0 < this.storekeymaps.length; ) {
let m = this.storekeymaps.shift();
mappings.addUserMap([modes.NORMAL], m.names, m.description, m.action,
cloneExtraInfo(m));
}
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(context, arg) {
let filter = context.filter;
var names = self.completeNames;
context.title = ['Name','Description'];
if (!filter) {
context.completions = names;
return;
}
filter = filter.toLowerCase();
context.completions = 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();
})();