| 2 | | * Vimperator Plugin For "Split Browser" |
| 3 | | * For vimperator 0.5.3 |
| 4 | | * @author teramako |
| | 2 | * ==VimperatorPlugin== |
| | 3 | * @name Split Browser for Vimperator |
| | 4 | * @author teramako <teramako@gmail.com> |
| | 5 | * @version 1.1a |
| | 6 | * @depend "Split Browser" {29c4afe1-db19-4298-8785-fcc94d1d6c1d} |
| | 7 | * ==/VimperatorPlugin== |
| | 8 | * |
| | 9 | * Usage: |
| | 10 | * |
| | 11 | * ---------------------------- |
| | 12 | * Commands |
| | 13 | * ---------------------------- |
| | 14 | * :sp[lit] [arg1], [arg2], ... [destination] |
| | 15 | * split horizontal and open [arg1] |
| | 16 | * opens [arg2] ... to background tab, if [arg2] ... is applied |
| | 17 | * [destination] is -top, if omitted |
| | 18 | * |
| | 19 | * :vs[plit] [arg1], [arg2], ... [destination] |
| | 20 | * like sp[lit] command |
| | 21 | * but [destination] is -right, if omitted |
| | 22 | * |
| | 23 | * available [destination] |
| | 24 | * -l or -left |
| | 25 | * -r or -right |
| | 26 | * -t or -top |
| | 27 | * -b or -bottom |
| | 28 | * |
| | 29 | * :on[ly] [-g] |
| | 30 | * Close all subbrowsers |
| | 31 | * or |
| | 32 | * Gather all subbrowsers to main browser if `-g' option is applied |
| | 33 | * |
| | 34 | * -------------------------- |
| | 35 | * Mappings |
| | 36 | * -------------------------- |
| | 37 | * <C-w><C-w> -> focus next browser |
| | 38 | * <C-w>0 -> focus main browser |
| | 39 | * <C-w>$ -> focus the last opened subbrowser |
| | 40 | * <C-w><C-v> -> virtical split |
| | 41 | * <C-w><C-s> -> horizontal split |
| | 42 | * <C-w>d -> close tab or subbrowser |
| | 43 | * |
| | 44 | * s[maps] -> [maps] is some nomral map names |
| | 45 | * e.g.) |
| | 46 | * sd -> remove current tab |
| | 47 | * sgt -> switch focus to the next tab |
| | 48 | * sgT -> switch focus to the previous tab |
| 9 | | vimperator.commands.add(new vimperator.Command(['sp[lit]'], |
| | 53 | |
| | 54 | const SplitBrowserAppID = '{29c4afe1-db19-4298-8785-fcc94d1d6c1d}'; |
| | 55 | if (!Application.extensions.get(SplitBrowserAppID).enabled) return; |
| | 56 | |
| | 57 | liberator.plugins.splitBrowser = (function(){ |
| | 58 | |
| | 59 | /* |
| | 60 | var origGetBrowser = getBrowser; |
| | 61 | |
| | 62 | getBrowser = function(){ |
| | 63 | return SplitBrowser.activeBrowser; |
| | 64 | }; |
| | 65 | */ |
| | 66 | function getPositionForOpen(args){ |
| | 67 | var p = null; |
| | 68 | if (!args || args.length == 0) return p; |
| | 69 | for (var i=0; i<args.length; i++){ |
| | 70 | switch (args[i][0]){ |
| | 71 | case '-l': p = SplitBrowser.POSITION_LEFT; break; |
| | 72 | case '-r': p = SplitBrowser.POSITION_RIGHT; break; |
| | 73 | case '-t': p = SplitBrowser.POSITION_TOP; break; |
| | 74 | case '-b': p = SplitBrowser.POSITION_BOTTOM; break; |
| | 75 | } |
| | 76 | } |
| | 77 | return p; |
| | 78 | } |
| | 79 | |
| | 80 | function focusSwitch(where, isAbsolute){ //{{{ |
| | 81 | if (SplitBrowser.browsers.length == 0) return; |
| | 82 | var bs = SplitBrowser.browsers; |
| | 83 | if (isAbsolute && bs[key-1]){ |
| | 84 | bs[key-1].browser.contentWindow.focus(); |
| | 85 | } else if (where == 0){ |
| | 86 | getBrowser().contentWindow.focus(); |
| | 87 | } else if (where == '$'){ |
| | 88 | bs[bs.length-1].browser.contentWindow.focus(); |
| | 89 | } else if (/^[-+]?[0-9]+$/.test(where)){ |
| | 90 | var length = bs.length; |
| | 91 | var count = parseInt(where,10) % (length + 1); |
| | 92 | if (SplitBrowser.activeBrowser == getBrowser()){ |
| | 93 | if (count > 0){ |
| | 94 | bs[count-1].browser.contentWindow.focus(); |
| | 95 | } else if (count < 0){ |
| | 96 | bs[length + count].browser.contentWindow.focus(); |
| | 97 | } else { |
| | 98 | return; |
| | 99 | } |
| | 100 | } else { |
| | 101 | var id = SplitBrowser.activeSubBrowser.id; |
| | 102 | for (var i=0; i<length; i++){ |
| | 103 | if (bs[i].id == id){ |
| | 104 | count = (count + i + 1) % (length + 1); |
| | 105 | if (count > 0){ |
| | 106 | bs[count-1].browser.contentWindow.focus(); |
| | 107 | } else if (count < 0){ |
| | 108 | bs[length + count].browser.contentWindow.focus(); |
| | 109 | } else { |
| | 110 | getBrowser().contentWindow.focus(); |
| | 111 | } |
| | 112 | return true; |
| | 113 | } |
| | 114 | } |
| | 115 | } |
| | 116 | } |
| | 117 | } //}}} |
| | 118 | |
| | 119 | /* ---------------------------------------------- |
| | 120 | * Commands |
| | 121 | * --------------------------------------------*/ |
| | 122 | liberator.commands.addUserCommand(['sp[lit]'], 'split browser', //{{{ |
| | 123 | function(args){ liberator.plugins.splitBrowser.openSubBrowser(args, SplitBrowser.POSITION_TOP); }, |
| | 124 | { completer: function(filter) liberator.completion.url(filter) } |
| | 125 | ); //}}} |
| | 126 | liberator.commands.addUserCommand(['vs[plit]'], 'split browser', //{{{ |
| | 127 | function(args){ liberator.plugins.splitBrowser.openSubBrowser(args, SplitBrowser.POSITION_RIGHT); }, |
| | 128 | { completer: function(filter) liberator.completion.url(filter) } |
| | 129 | ); //}}} |
| | 130 | liberator.commands.addUserCommand(['on[ly]'], 'Close or gather all subbrowsers', //{{{ |
| 11 | | var position = null; |
| 12 | | if (args) { |
| 13 | | args = args.split(/ -/); |
| 14 | | switch (args[args.length-1].toLowerCase()) { |
| 15 | | case 'l': |
| 16 | | case 'left': position = SplitBrowser.POSITION_LEFT; break; |
| 17 | | case 'r': |
| 18 | | case 'right': position = SplitBrowser.POSITION_RIGHT; break; |
| 19 | | case 't': |
| 20 | | case 'top': position = SplitBrowser.POSITION_TOP; break; |
| 21 | | case 'bottom': |
| 22 | | case 'b': position = SplitBrowser.POSITION_BOTTOM; break; |
| 23 | | } |
| 24 | | if (position) { |
| 25 | | args.pop(); |
| 26 | | } |
| 27 | | var urls = args.join(' -'); |
| 28 | | if (urls){ |
| 29 | | urls = vimperator.util.stringToURLArray(urls); |
| 30 | | var url = typeof urls[0] == 'string' ? urls[0] : urls[0][0]; |
| 31 | | openSubBrowser(url, position); |
| 32 | | return; |
| 33 | | } |
| 34 | | } |
| 35 | | vimperator.echo('Split and load current URL.'); |
| 36 | | openSubBrowser(vimperator.buffer.URL, position); |
| 37 | | },{ |
| 38 | | usage: ['sp[lit] [URL] [-POSITION]'], |
| 39 | | shortHelp: 'split browser', |
| 40 | | help: 'Split browser and load the <code class="argument">[URL]</code>.<br/>' + |
| 41 | | 'If you don\'t specify the <code class="argument">[URL]</code> argument, browser loads the current URL.<br/>' + |
| 42 | | '<br/>You need to install <a href="https://addons.mozilla.org/firefox/addon/4287">Split Browser</a>.', |
| 43 | | completer: function(filter) vimperator.completion.get_url_completions(filter) |
| 44 | | } |
| 45 | | )); |
| 46 | | vimperator.commands.add(new vimperator.Command(['on[ly]'], |
| 47 | | function(){ |
| 48 | | if (SplitBrowser.browsers.length > 0) { |
| 49 | | SplitBrowser.removeAllSubBrowsers(); // 分割したブラウザをすべて閉じる |
| 50 | | // 好みで↑↓選ぼう |
| 51 | | // SplitBrowser.gatherSubBrowsers(); // すべての分割したブラウザをタブに集める |
| 52 | | } else { |
| 53 | | vimperator.echoerr('SubBrowser is none'); |
| 54 | | } |
| 55 | | },{ |
| 56 | | usage: ['on[ly]'], |
| 57 | | shortHelp: 'close split browsers', |
| 58 | | help: 'Close all split browsers' |
| 59 | | } |
| 60 | | )); |
| | 132 | if (SplitBrowser.browsers.length == 0) { |
| | 133 | liberator.echoerr('SubBrowser is none'); |
| | 134 | return; |
| | 135 | } |
| | 136 | if (args == '-g') { |
| | 137 | SplitBrowser.gatherSubBrowsers(); |
| | 138 | } else { |
| | 139 | SplitBrowser.removeAllSubBrowsers(); |
| | 140 | } |
| | 141 | } |
| | 142 | ); //}}} |
| | 143 | |
| | 144 | /* ---------------------------------------------- |
| | 145 | * Mappings |
| | 146 | * --------------------------------------------*/ |
| | 147 | liberator.mappings.addUserMap([liberator.modes.NORMAL],['s'], 'SplitBrowser motion Map', //{{{ |
| | 148 | function(key, count){ |
| | 149 | gBrowser = SplitBrowser.activeBrowser; |
| | 150 | try { |
| | 151 | var map = liberator.mappings.get(liberator.modes.NORMAL, key) |
| | 152 | map.execute(null, count); |
| | 153 | } catch(e) { |
| | 154 | liberator.log(e); |
| | 155 | } finally { |
| | 156 | gBrowser = document.getElementById('content'); |
| | 157 | } |
| | 158 | },{ |
| | 159 | flags: liberator.Mappings.flags.MOTION + liberator.Mappings.flags.COUNT |
| | 160 | } |
| | 161 | ); |
| | 162 | //}}} |
| | 163 | liberator.mappings.addUserMap([liberator.modes.NORMAL], ['<C-w>'], 'select subbrowser', //{{{ |
| | 164 | function(count, key){ |
| | 165 | if (/[1-9]/.test(key)){ |
| | 166 | focusSwitch(parseInt(key), true); |
| | 167 | return; |
| | 168 | } |
| | 169 | switch (key){ |
| | 170 | case '0': |
| | 171 | case '$': |
| | 172 | focusSwitch(key); |
| | 173 | break; |
| | 174 | case 'h': //FIXME: How to get subbrowser of relative position ? |
| | 175 | case 'j': |
| | 176 | case '<C-w>': |
| | 177 | case 'w': |
| | 178 | focusSwitch(count > 0 ? count : 1); |
| | 179 | break; |
| | 180 | case 'k': |
| | 181 | case 'l': |
| | 182 | case 'W': |
| | 183 | forcusSwitch('-' + (count > 0 ? count : 1)); |
| | 184 | break; |
| | 185 | case 'd': |
| | 186 | liberator.plugins.splitBrowser.closeSubBrowser(); |
| | 187 | break; |
| | 188 | case '<C-v>': |
| | 189 | liberator.plugins.splitBrowser.openSubBrowser(liberator.buffer.URL,SplitBrowser.POSITION_RIGHT); |
| | 190 | break; |
| | 191 | case '<C-s>': |
| | 192 | liberator.plugins.splitBrowser.openSubBrowser(liberator.buffer.URL,SplitBrowser.POSITION_TOP); |
| | 193 | break; |
| | 194 | } |
| | 195 | },{ flags: liberator.Mappings.flags.COUNT + liberator.Mappings.flags.ARGUMENT } |
| | 196 | ); //}}} |
| 65 | | vimperator.mappings.add(new vimperator.Map(vimperator.modes.NORMAL, ['sd'], //{{{ |
| 66 | | function(){ |
| 67 | | var b = SplitBrowser.activeBrowser; |
| 68 | | if (b.mTabs.length > 1){ |
| 69 | | b.removeTab(b.mCurrentTab); |
| 70 | | } else { |
| 71 | | if (b === gBrowser){ |
| 72 | | vimperator.open('about:blank', vimperator.NEW_BACKGROUND_TAB); |
| 73 | | gBrowser.removeTab(gBrowser.mCurrentTab); |
| 74 | | } else { |
| 75 | | SplitBrowser.activeBrowserCloseWindow(); |
| 76 | | } |
| 77 | | } |
| 78 | | },{ |
| 79 | | shortHelp: 'Close active browser' |
| 80 | | } |
| 81 | | )); //}}} |
| 82 | | /** |
| 83 | | * Switch browser focus |
| 84 | | */ |
| 85 | | vimperator.mappings.add(new vimperator.Map(vimperator.modes.NORMAL, ['<C-w><C-w>'], //{{{ |
| 86 | | function(){ |
| 87 | | var browsers = SplitBrowser.browsers; |
| 88 | | if (SplitBrowser.activeBrowser === gBrowser) { |
| 89 | | SplitBrowser.getSubBrowserById(browsers[0].id).browser.contentWindow.focus(); |
| 90 | | } else { |
| 91 | | var id = SplitBrowser.activeSubBrowser.id; |
| 92 | | for (var i=0; i<browsers.length; i++){ |
| 93 | | if (browsers[i].id == id) { |
| 94 | | if (browsers[i+1]){ |
| 95 | | SplitBrowser.getSubBrowserById(browsers[i+1].id).browser.contentWindow.focus(); |
| 96 | | } else { |
| 97 | | gBrowser.contentWindow.focus(); |
| 98 | | } |
| 99 | | return true; |
| 100 | | } |
| 101 | | } |
| 102 | | } |
| 103 | | },{ |
| 104 | | shortHelp: 'Switch focus', |
| 105 | | help: 'Switch focus to splitted browser' |
| 106 | | } |
| 107 | | )); //}}} |
| 108 | | |
| 109 | | /** |
| 110 | | * Overwrite save hint `s' to `S' |
| 111 | | */ |
| 112 | | vimperator.mappings.add(new vimperator.Map(vimperator.modes.HINTS, ['S'], //{{{ |
| 113 | | function(){ vimperator.hints.saveHints(true); }, |
| 114 | | { |
| 115 | | cancelMode: true, |
| 116 | | alwaysActive: false |
| 117 | | } |
| 118 | | )); //}}} |
| 119 | | /** |
| 120 | | * ExtendedHint mode key mapping `s' |
| 121 | | * This key mapping is prefix for open the urls to split browser |
| 122 | | */ |
| 123 | | vimperator.mappings.add(new vimperator.Map(vimperator.modes.HINTS, ['s'], //{{{ |
| 124 | | function(){ |
| 125 | | vimperator.input.buffer += 's'; |
| 126 | | vimperator.echo("-- HINTS (extended) -- Split: `t': TOP, `b': BOTTOM, `l':LEFT, `r':RIGHT"); |
| 127 | | },{ |
| 128 | | cancelMode:false, |
| 129 | | alwaysActive:true |
| 130 | | } |
| 131 | | )); // }}} |
| 132 | | /** |
| 133 | | * ExtendedHint mode key mapping `t' |
| 134 | | * Split to TOP |
| 135 | | */ |
| 136 | | vimperator.mappings.add(new vimperator.Map(vimperator.modes.HINTS, ['t'], //{{{ |
| 137 | | function(){ |
| 138 | | var input = vimperator.input.buffer; |
| 139 | | if (input.charAt(input.length-1) == 's'){ |
| 140 | | openSubBrowserExtendedMode(SplitBrowser.POSITION_TOP); |
| 141 | | } else { |
| 142 | | vimperator.hints.openHints(true, false); |
| 143 | | } |
| 144 | | vimperator.hints.disableHahMode(null,true); |
| 145 | | },{ |
| 146 | | cancelMode:true, |
| 147 | | alwaysActive:false |
| 148 | | } |
| 149 | | )); //}}} |
| 150 | | /** |
| 151 | | * ExtendedHint mode key mapping `b' |
| 152 | | * Split to BOTTOM |
| 153 | | */ |
| 154 | | vimperator.mappings.add(new vimperator.Map(vimperator.modes.HINTS, ['b'], //{{{ |
| 155 | | function(){ |
| 156 | | var input = vimperator.input.buffer; |
| 157 | | if (input.charAt(input.length-1) == 's'){ |
| 158 | | openSubBrowserExtendedMode(SplitBrowser.POSITION_BOTTOM); |
| 159 | | } |
| 160 | | vimperator.hints.disableHahMode(null,true); |
| 161 | | },{ |
| 162 | | cancelMode:true, |
| 163 | | alwaysActive:false |
| 164 | | } |
| 165 | | )); //}}} |
| 166 | | /** |
| 167 | | * ExtendedHint mode key mapping `l' |
| 168 | | * Split to LEFT |
| 169 | | */ |
| 170 | | vimperator.mappings.add(new vimperator.Map(vimperator.modes.HINTS, ['l'], //{{{ |
| 171 | | function(){ |
| 172 | | var input = vimperator.input.buffer; |
| 173 | | if (input.charAt(input.length-1) == 's'){ |
| 174 | | openSubBrowserExtendedMode(SplitBrowser.POSITION_LEFT); |
| 175 | | } |
| 176 | | vimperator.hints.disableHahMode(null,true); |
| 177 | | },{ |
| 178 | | cancelMode:true, |
| 179 | | alwaysActive:false |
| 180 | | } |
| 181 | | )); //}}} |
| 182 | | /** |
| 183 | | * ExtendedHint mode key mapping `r' |
| 184 | | * Split to RIGHT |
| 185 | | */ |
| 186 | | vimperator.mappings.add(new vimperator.Map(vimperator.modes.HINTS, ['r'], //{{{ |
| 187 | | function(){ |
| 188 | | var input = vimperator.input.buffer; |
| 189 | | if (input.charAt(input.length-1) == 's'){ |
| 190 | | openSubBrowserExtendedMode(SplitBrowser.POSITION_RIGHT); |
| 191 | | } |
| 192 | | vimperator.hints.disableHahMode(null,true); |
| 193 | | },{ |
| 194 | | cancelMode:true, |
| 195 | | alwaysActive:false |
| 196 | | } |
| 197 | | )); //}}} |
| 198 | | /** |
| 199 | | * openSubBrowser at ExtendedHint mode |
| 200 | | * @param {Number} aPosition split direction |
| 201 | | */ |
| 202 | | function openSubBrowserExtendedMode(aPosition){ //{{{ |
| 203 | | var elms = vimperator.hints.hintedElements(); |
| 204 | | var urls = []; |
| 205 | | for (var i=0; i<elms.length; i++){ |
| 206 | | var url = elms[i].refElem.href; |
| 207 | | if (typeof(url) == 'undefined' || !url.length){ |
| 208 | | continue; |
| 209 | | } |
| 210 | | urls.push(url); |
| 211 | | } |
| 212 | | if (urls.length == 0) { return; } |
| 213 | | var subBrowser = openSubBrowser(urls[0], aPosition); |
| 214 | | if (urls.length > 1){ |
| 215 | | for (var i=1, l=urls.length; i < l; subBrowser.browser.addTab(urls[i++],null,null,null)) |
| 216 | | ; |
| 217 | | } |
| 218 | | } //}}} |
| 219 | | /** |
| 220 | | * create new subBrowser and load url |
| 221 | | * @param {String} url |
| 222 | | * @param {Number} aPosition split direction |
| 223 | | */ |
| 224 | | function openSubBrowser(url, aPosition){ // {{{ |
| 225 | | var subBrowser = SplitBrowser.addSubBrowser(url, null, aPosition || SplitBrowser.POSITION_TOP); |
| 226 | | subBrowser.addEventListener('load',function(){ |
| 227 | | subBrowser.removeEventListener('load',arguments.callee,true); |
| 228 | | subBrowser.browser.contentWindow.focus(); |
| 229 | | },true); |
| 230 | | return subBrowser; |
| 231 | | } //}}} |
| 232 | | |
| 233 | | /** |
| 234 | | * Overwrite vimperator.open for SplitBrowser |
| 235 | | * @see vimperator.js::vimperaotr.open |
| 236 | | */ |
| 237 | | vimperator.open = function(urls, where, callback){ //{{{ |
| 238 | | if (typeof urls == 'string') { urls = vimperator.util.stringToURLArray(urls); } |
| 239 | | if (urls.length == 0) { return false; } |
| 240 | | if (!where) { where = vimperator.CURRENT_TAB; } |
| 241 | | |
| | 202 | liberator.open = function(urls, where){ //{{{ |
| | 203 | if (typeof urls == 'string') urls = liberator.util.stringToURLArray(urls); |
| | 204 | if (urls.length == 0) return false; |
| | 205 | if (liberator.forceNewTab && liberator.has("tabs")){ |
| | 206 | where = liberator.NEW_TAB; |
| | 207 | } else if (!where || !liberator.has("tabs")){ |
| | 208 | where = liberator.CURRENT_TAB; |
| | 209 | } |
| | 246 | |
| | 247 | var manager = { |
| | 248 | splitBrowserId: SplitBrowserAppID, |
| | 249 | args: [ [['-l','-left'], liberator.commands.OPTION_NOARG], |
| | 250 | [['-r','-right'], liberator.commands.OPTION_NOARG], |
| | 251 | [['-t','-top'], liberator.commands.OPTION_NOARG], |
| | 252 | [['-b','-bottom'], liberator.commands.OPTION_NOARG] ], |
| | 253 | get gBrowser(){ |
| | 254 | return origGetBrowser(); |
| | 255 | }, |
| | 256 | /** |
| | 257 | * create new subBrowser and load url |
| | 258 | * @param {String} args command aruguments |
| | 259 | * @param {Number} defPosition default split direction |
| | 260 | */ |
| | 261 | openSubBrowser: function(args, defPosition){ |
| | 262 | var url; |
| | 263 | var urls = []; |
| | 264 | var position = defPosition || SplitBrowser.POSITION_TOP; |
| | 265 | if (args){ |
| | 266 | var res = liberator.commands.parseArgs(args, this.args); |
| | 267 | position = getPositionForOpen(res.opts) || position; |
| | 268 | if (res.args.length > 0){ |
| | 269 | liberator.log(res.args); |
| | 270 | urls = liberator.util.stringToURLArray(res.args.join(' ')); |
| | 271 | liberator.log(urls); |
| | 272 | if (urls.length == 0) { |
| | 273 | url = liberator.buffer.URL; |
| | 274 | } else { |
| | 275 | url = urls[0]; |
| | 276 | urls.shift(); |
| | 277 | } |
| | 278 | } else { |
| | 279 | url = liberator.buffer.URL; |
| | 280 | } |
| | 281 | } else { |
| | 282 | url = liberator.buffer.URL; |
| | 283 | } |
| | 284 | var subBrowser = SplitBrowser.addSubBrowser(url, null, position); |
| | 285 | subBrowser.addEventListener('load',function(){ |
| | 286 | subBrowser.removeEventListener('load',arguments.callee,true); |
| | 287 | subBrowser.browser.contentWindow.focus(); |
| | 288 | },true); |
| | 289 | urls.forEach(function(url){ |
| | 290 | subBrowser.browser.addTab(url, null, null, null); |
| | 291 | }); |
| | 292 | return subBrowser; |
| | 293 | }, |
| | 294 | closeSubBrowser: function(){ |
| | 295 | var b = SplitBrowser.activeBrowser; |
| | 296 | if (b.mTabs.length > 1){ |
| | 297 | b.removeTab(b.mCurrentTab); |
| | 298 | } else { |
| | 299 | if (b === getBrowser()){ |
| | 300 | liberator.open('about:blank', liberator.NEW_BACKGROUND_TAB); |
| | 301 | getBrowser().removeTab(gBrowser.mCurrentTab); |
| | 302 | } else { |
| | 303 | SplitBrowser.activeBrowserCloseWindow(); |
| | 304 | } |
| | 305 | } |
| | 306 | } |
| | 307 | }; |
| | 308 | return manager; |