| 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 | |
|---|
| 55 | liberator.plugins.feedKey = (function(){ |
|---|
| 56 | var origMaps = []; |
|---|
| 57 | var feedMaps = []; |
|---|
| 58 | |
|---|
| 59 | // keyTableの再定義...ひどく不毛... |
|---|
| 60 | const 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 | ]; |
|---|
| 102 | const 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 | |
|---|
| 154 | function 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 | } |
|---|
| 160 | function 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 | } |
|---|
| 172 | function 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 | } |
|---|
| 201 | function 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 | } |
|---|
| 213 | function addUserMap(map){ |
|---|
| 214 | mappings.addUserMap(map.modes, map.names, map.description, map.action, { flags:map.flags,noremap:map.noremap,rhs:map.rhs }); |
|---|
| 215 | } |
|---|
| 216 | function 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 | } |
|---|
| 224 | function 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 | } |
|---|
| 244 | function 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 | // -------------------------- |
|---|
| 305 | commands.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 | ); |
|---|
| 329 | commands.addUserCommand(['feedmapclear','fmapc'],'Clear Feed Maps',destroy); |
|---|
| 330 | var converter = { |
|---|
| 331 | get origMap() origMaps, |
|---|
| 332 | get feedMap() feedMaps, |
|---|
| 333 | setup: init, |
|---|
| 334 | reset: destroy |
|---|
| 335 | }; |
|---|
| 336 | return converter; |
|---|
| 337 | })(); |
|---|
| 338 | // vim: fdm=marker sw=4 ts=4 et: |
|---|