root/lang/javascript/vimperator-plugins/branches/1.2/feedSomeKeys_2.js

Revision 22766, 12.5 kB (checked in by retlet, 22 months ago)

動作確認とれたのでtrunkからコピー

Line 
1/**
2 * ==VimperatorPlugin==
3 * @name           feedSomeKeys 2
4 * @description    feed some defined key events into the Web content
5 * @description-ja 定義したkeyイベントをWebページ側へ送ってあげます
6 * @author         teramako teramako@gmail.com
7 * @version        2.0a
8 * ==/VimperatorPlugin==
9 *
10 * 英語での説明を放棄する
11 *
12 * XXX: feedSomeKeys.js の改良版仕様を変更したのでフォーク
13 *
14 * keyイベント(正確にはkepressイベント)をWebコンテンツ側へ送る事を可能にするプラグイン
15 * Gmailとかlivedoor ReaderとかGreasemonkeyでキーを割り当てている場合に活躍するでしょう。
16 * それ以外の場面ではむしろ邪魔になる諸刃の剣
17 *
18 * :f[eed]map lhs [...]             -> lhsのキーマップをそのままWebコンテンツへ
19 * :f[eed]map lhs,[num]rhs [...]    -> lhsのキーマップをrhsへ変換してWebコンテンツへ
20 *                                     [num]はフレームの番号(省略時はトップウィンドウへイベントが送られる)
21 * :f[eed]map -d[epth] {num} ...    -> {num}はフレームの番号で :fmap lhs1,{num}rhs1 lhs2,{num}rhs2 ... と同等
22 *                                     Gmailの例を参照
23 * :f[eed]map -v[key] ....          -> 仮想キーコードでイベントを送るように
24 *
25 * :fmapc
26 * :feedmapclear            -> 全てを無に帰して元に戻す
27 *
28 * :f[eed]map! lhs ...      -> "!" をつけると、一旦すべてのfeedKeysを元に戻しての再定義
29 *
30 * autocmdと組み合わせる場合は
31 * :autocmd LocationChange .* :fmapc
32 * を最初に登録してください。でないと対象外のページに移ったときに設定が前のものを引きずることになります。
33 *
34 * また、下記設定例はvimperator 2.0pre用で1.2の場合は\を一つに削ってください。
35 *
36 * == LDR の場合 ==
37 * :autocmd LocationChange .* :fmapc
38 * :autocmd LocationChange reader\\.livedoor\\.com/reader :fmap j k s a p o v c <Space> <S-Space> z b < >
39 *
40 * とかやると幸せになれるかも。
41 *
42 * == Gmail の場合 ==
43 * :autocmd LocationChange .* :fmapc
44 * :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
45 *
46 * とかやると幸せになれるかもしれません。
47 *
48 * == Google Reader の場合 ==
49 * :autocmd LocationChange .* :fmapc
50 * :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 / ?
51 *
52 * Greasemonkey LDRizeの場合などにも使用可
53 */
54
55liberator.plugins.feedKey = (function(){
56var origMaps = [];
57var feedMaps = [];
58
59// keyTableの再定義...ひどく不毛...
60const keyTable = [
61    [ KeyEvent.DOM_VK_BACK_SPACE, ["BS"] ],
62    [ KeyEvent.DOM_VK_TAB, ["Tab"] ],
63    [ KeyEvent.DOM_VK_RETURN, ["Return", "CR", "Enter"] ],
64    //[ KeyEvent.DOM_VK_ENTER, ["Enter"] ],
65    [ KeyEvent.DOM_VK_ESCAPE, ["Esc", "Escape"] ],
66    [ KeyEvent.DOM_VK_SPACE, ["Spc", "Space"] ],
67    [ KeyEvent.DOM_VK_PAGE_UP, ["PageUp"] ],
68    [ KeyEvent.DOM_VK_PAGE_DOWN, ["PageDown"] ],
69    [ KeyEvent.DOM_VK_END, ["End"] ],
70    [ KeyEvent.DOM_VK_HOME, ["Home"] ],
71    [ KeyEvent.DOM_VK_LEFT, ["Left"] ],
72    [ KeyEvent.DOM_VK_UP, ["Up"] ],
73    [ KeyEvent.DOM_VK_RIGHT, ["Right"] ],
74    [ KeyEvent.DOM_VK_DOWN, ["Down"] ],
75    [ KeyEvent.DOM_VK_INSERT, ["Ins", "Insert"] ],
76    [ KeyEvent.DOM_VK_DELETE, ["Del", "Delete"] ],
77    [ KeyEvent.DOM_VK_F1, ["F1"] ],
78    [ KeyEvent.DOM_VK_F2, ["F2"] ],
79    [ KeyEvent.DOM_VK_F3, ["F3"] ],
80    [ KeyEvent.DOM_VK_F4, ["F4"] ],
81    [ KeyEvent.DOM_VK_F5, ["F5"] ],
82    [ KeyEvent.DOM_VK_F6, ["F6"] ],
83    [ KeyEvent.DOM_VK_F7, ["F7"] ],
84    [ KeyEvent.DOM_VK_F8, ["F8"] ],
85    [ KeyEvent.DOM_VK_F9, ["F9"] ],
86    [ KeyEvent.DOM_VK_F10, ["F10"] ],
87    [ KeyEvent.DOM_VK_F11, ["F11"] ],
88    [ KeyEvent.DOM_VK_F12, ["F12"] ],
89    [ KeyEvent.DOM_VK_F13, ["F13"] ],
90    [ KeyEvent.DOM_VK_F14, ["F14"] ],
91    [ KeyEvent.DOM_VK_F15, ["F15"] ],
92    [ KeyEvent.DOM_VK_F16, ["F16"] ],
93    [ KeyEvent.DOM_VK_F17, ["F17"] ],
94    [ KeyEvent.DOM_VK_F18, ["F18"] ],
95    [ KeyEvent.DOM_VK_F19, ["F19"] ],
96    [ KeyEvent.DOM_VK_F20, ["F20"] ],
97    [ KeyEvent.DOM_VK_F21, ["F21"] ],
98    [ KeyEvent.DOM_VK_F22, ["F22"] ],
99    [ KeyEvent.DOM_VK_F23, ["F23"] ],
100    [ KeyEvent.DOM_VK_F24, ["F24"] ],
101];
102const vkeyTable = [
103    [ KeyEvent.DOM_VK_0, ['0'] ],
104    [ KeyEvent.DOM_VK_1, ['1'] ],
105    [ KeyEvent.DOM_VK_2, ['2'] ],
106    [ KeyEvent.DOM_VK_3, ['3'] ],
107    [ KeyEvent.DOM_VK_4, ['4'] ],
108    [ KeyEvent.DOM_VK_5, ['5'] ],
109    [ KeyEvent.DOM_VK_6, ['6'] ],
110    [ KeyEvent.DOM_VK_7, ['7'] ],
111    [ KeyEvent.DOM_VK_8, ['8'] ],
112    [ KeyEvent.DOM_VK_9, ['9'] ],
113    [ KeyEvent.DOM_VK_SEMICOLON, [';'] ],
114    [ KeyEvent.DOM_VK_EQUALS, ['='] ],
115    [ KeyEvent.DOM_VK_A, ['a'] ],
116    [ KeyEvent.DOM_VK_B, ['b'] ],
117    [ KeyEvent.DOM_VK_C, ['c'] ],
118    [ KeyEvent.DOM_VK_D, ['d'] ],
119    [ KeyEvent.DOM_VK_E, ['e'] ],
120    [ KeyEvent.DOM_VK_F, ['f'] ],
121    [ KeyEvent.DOM_VK_G, ['g'] ],
122    [ KeyEvent.DOM_VK_H, ['h'] ],
123    [ KeyEvent.DOM_VK_I, ['i'] ],
124    [ KeyEvent.DOM_VK_J, ['j'] ],
125    [ KeyEvent.DOM_VK_K, ['k'] ],
126    [ KeyEvent.DOM_VK_L, ['l'] ],
127    [ KeyEvent.DOM_VK_M, ['m'] ],
128    [ KeyEvent.DOM_VK_N, ['n'] ],
129    [ KeyEvent.DOM_VK_O, ['o'] ],
130    [ KeyEvent.DOM_VK_P, ['p'] ],
131    [ KeyEvent.DOM_VK_Q, ['q'] ],
132    [ KeyEvent.DOM_VK_R, ['r'] ],
133    [ KeyEvent.DOM_VK_S, ['s'] ],
134    [ KeyEvent.DOM_VK_T, ['t'] ],
135    [ KeyEvent.DOM_VK_U, ['u'] ],
136    [ KeyEvent.DOM_VK_V, ['v'] ],
137    [ KeyEvent.DOM_VK_W, ['w'] ],
138    [ KeyEvent.DOM_VK_X, ['x'] ],
139    [ KeyEvent.DOM_VK_Y, ['y'] ],
140    [ KeyEvent.DOM_VK_Z, ['z'] ],
141    [ KeyEvent.DOM_VK_MULTIPLY, ['*'] ],
142    [ KeyEvent.DOM_VK_ADD, ['+'] ],
143    [ KeyEvent.DOM_VK_SUBTRACT, ['-'] ],
144    [ KeyEvent.DOM_VK_COMMA, [','] ],
145    [ KeyEvent.DOM_VK_PERIOD, ['.'] ],
146    [ KeyEvent.DOM_VK_SLASH, ['/'] ],
147    [ KeyEvent.DOM_VK_BACK_QUOTE, ['`'] ],
148    [ KeyEvent.DOM_VK_OPEN_BRACKET, ['{'] ],
149    [ KeyEvent.DOM_VK_BACK_SLASH, ['\\'] ],
150    [ KeyEvent.DOM_VK_CLOSE_BRACKET, ['}'] ],
151    [ KeyEvent.DOM_VK_QUOTE, ["'"] ],
152];
153
154function getKeyCode(str, vkey) {
155    str = str.toLowerCase();
156    var ret = 0;
157    (vkey ? vkeyTable : keyTable).some(function(i) i[1].some(function(k) k.toLowerCase() == str && (ret = i[0])));
158    return ret;
159}
160function init(keys, useVkey){
161    destroy();
162    keys.forEach(function(key){
163        var origKey, feedKey;
164        if (key instanceof Array){
165            [origKey, feedKey] = key;
166        } else if (typeof(key) == 'string'){
167            [origKey, feedKey] = [key,key];
168        }
169        replaceUserMap(origKey, feedKey, useVkey);
170    });
171}
172function replaceUserMap(origKey, feedKey, useVkey){
173    if (mappings.hasMap(modes.NORMAL, origKey)){
174        var origMap = mappings.get(modes.NORMAL,origKey);
175        if (origMap.description.indexOf(origKey+' -> ') != 0) {
176            // origMapをそのままpushするとオブジェクト内の参照先を消されてしまう
177            // 仕方なく複製を試みる
178            var clone = new Map(origMap.modes.map(function(m) m),
179                                origMap.names.map(function(n) n),
180                                origMap.description,
181                                origMap.action,
182                                { flags:origMap.flags, rhs:origMap.rhs, noremap:origMap.noremap });
183            origMaps.push(clone);
184        }
185    }
186    var map = new Map([modes.NORMAL], [origKey], origKey + ' -> ' + feedKey,
187        function(count){
188            count = count > 1 ? count : 1;
189            for (var i=0; i<count; i++){
190                feedKeyIntoContent(feedKey, useVkey);
191            }
192        }, { flags:Mappings.flags.COUNT, rhs:feedKey, noremap:true });
193    addUserMap(map);
194    if (feedMaps.some(function(fmap){
195        if (fmap.names[0] != origKey) return false;
196        for (var key in fmap) fmap[key] = map[key];
197        return true;
198    })) return;
199    feedMaps.push(map);
200}
201function destroy(){
202    try{
203        feedMaps.forEach(function(map){
204            mappings.remove(map.modes[0],map.names[0]);
205        });
206    }catch(e){ log(map); }
207    origMaps.forEach(function(map){
208        addUserMap(map);
209    });
210    origMaps = [];
211    feedMaps = [];
212}
213function addUserMap(map){
214    mappings.addUserMap(map.modes, map.names, map.description, map.action, { flags:map.flags,noremap:map.noremap,rhs:map.rhs });
215}
216function parseKeys(keys){
217    var matches = /^\d+(?=\D)/.exec(keys);
218    if (matches){
219        var num = parseInt(matches[0],10);
220        if (!isNaN(num)) return [keys.substr(matches[0].length), num];
221    }
222    return [keys, 0];
223}
224function getDestinationElement(frameNum){
225    var root = document.commandDispatcher.focusedWindow;
226    if (frameNum > 0){
227        var frames = [];
228        (function(frame){// @see liberator.buffer.shiftFrameFocus
229            if (frame.document.body.localName.toLowerCase() == 'body') {
230                frames.push(frame);
231            }
232            for (var i=0; i<frame.frames.length; i++){
233                arguments.callee(frame.frames[i]);
234            }
235        })(window.content);
236        frames = frames.filter(function(frame){
237            frame.focus();
238            if (document.commandDispatcher.focusedWindow == frame) return frame;
239        });
240        if (frames[frameNum]) return frames[frameNum];
241    }
242    return root;
243}
244function feedKeyIntoContent(keys, useVkey){
245    var frameNum = 0;
246    [keys, frameNum] = parseKeys(keys);
247    var destElem = getDestinationElement(frameNum);
248    destElem.focus();
249    modes.passAllKeys = true;
250    modes.passNextKey = false;
251    for (var i=0; i<keys.length; i++){
252        var keyCode;
253        var shift = false, ctrl = false, alt = false, meta = false;
254        if (useVkey && (keyCode = getKeyCode(keys[i], true))) {
255            var charCode = 0;
256        } else {
257            var charCode = keys.charCodeAt(i);
258            keyCode = 0;
259        }
260        if (keys[i] == '<'){
261            var matches = keys.substr(i + 1).match(/^((?:[ACMSacms]-)*)([^>]+)/);
262            if (matches) {
263                if (matches[1]) {
264                    ctrl  = /[cC]-/.test(matches[1]);
265                    alt   = /[aA]-/.test(matches[1]);
266                    shift = /[sS]-/.test(matches[1]);
267                    meta  = /[mM]-/.test(matches[1]);
268                }
269                if (matches[2].length == 1) {
270                    if (!ctrl && !alt && !shift && !meta) return false;
271                    if (useVkey && (keyCode = getKeyCode(matches[2], true))) {
272                        charCode = 0;
273                    } else {
274                        charCode = matches[2].charCodeAt(0);
275                    }
276                } else if (matches[2].toLowerCase() == "space") {
277                    if (useVkey) {
278                        charCode = 0;
279                        keyCode = KeyEvent.DOM_VK_SPACE;
280                    } else {
281                        charCode = KeyEvent.DOM_VK_SPACE;
282                    }
283                } else if (keyCode = getKeyCode(matches[2])) {
284                    charCode = 0;
285                } else  {
286                    return false;
287                }
288                i += matches[0].length + 1;
289            }
290        } else  {
291            shift = (keys[i] >= "A" && keys[i] <= "Z");
292        }
293
294        //liberator.log({ctrl:ctrl, alt:alt, shift:shift, meta:meta, keyCode:keyCode, charCode:charCode, useVkey: useVkey});
295        var evt = content.document.createEvent('KeyEvents');
296        evt.initKeyEvent('keypress', true, true, content, ctrl, alt, shift, meta, keyCode, charCode);
297        destElem.document.dispatchEvent(evt);
298    }
299    modes.passAllKeys = false;
300}
301
302// --------------------------
303// Command
304// --------------------------
305commands.addUserCommand(['feedmap','fmap'],'Feed Map a key sequence',
306    function(args, bang){
307        if(!args.string){
308            liberator.echo(template.table("feedmap list",feedMaps.map(function(map) [map.names[0], map.rhs])), true);
309            return;
310        }
311        if (bang) destroy();
312        var depth = args["-depth"] ? args["-depth"] : "";
313        var useVkey = "-vkey" in args;
314       
315        args.arguments.forEach(function(keypair){
316            var [lhs, rhs] = keypair.split(",");
317            if (!rhs) rhs = lhs;
318            replaceUserMap(lhs, depth + rhs, useVkey);
319        });
320    },{
321        bang: true,
322        argCount: "*",
323        options: [
324            [["-depth","-d"], commands.OPTION_INT],
325            [["-vkey","-v"], commands.OPTION_NOARG]
326        ]
327    }
328);
329commands.addUserCommand(['feedmapclear','fmapc'],'Clear Feed Maps',destroy);
330var converter = {
331    get origMap() origMaps,
332    get feedMap() feedMaps,
333    setup: init,
334    reset: destroy
335};
336return converter;
337})();
338// vim: fdm=marker sw=4 ts=4 et:
Note: See TracBrowser for help on using the browser.