├── .extension-reloader ├── CHANGELOG.adoc ├── cmdline_frame.js ├── .gitignore ├── icons ├── 16.png ├── 19.png ├── 38.png ├── 48.png ├── 128.png ├── disabled.png └── disabled-38.png ├── scripts ├── release.sh ├── generate-docs.sh ├── watch.sh ├── codelength.sh ├── build.sh ├── update_usercss.js └── create_pages.js ├── docs ├── Gemfile ├── thanks.adoc ├── installation.adoc ├── index.adoc ├── Gemfile.lock ├── contribute.adoc ├── tips.adoc └── settings.adoc ├── Makefile ├── background_scripts ├── links.js ├── sites.js ├── extension-enabler.js ├── files.js ├── clipboard.js ├── extension-reloader.js ├── tab_creation_order.js ├── update.js ├── frames.js ├── bookmarks.js ├── sessions.js ├── history.js ├── user.css ├── popup.js ├── main.js └── options.js ├── cvimrc_parser ├── Makefile ├── test │ ├── test.js │ └── test.vim └── parser.peg ├── content_scripts ├── session.js ├── newtab.js ├── clipboard.js ├── status.js ├── cursor.js ├── frames.js ├── hud.js ├── bookmarks.js ├── search.js ├── dom.js ├── main.css ├── find.js ├── visual.js ├── messenger.js └── scroll.js ├── README.md ├── package.json ├── pages ├── hljs.css ├── codemirror │ ├── LICENSE.txt │ └── codemirror.css ├── blank.html ├── popup.html ├── popup.js ├── options.css ├── options.html ├── options.js └── markdown.css ├── LICENSE.txt ├── .eslintrc ├── cmdline_frame.html ├── vendor ├── linkify-element.min.js └── linkify-jquery.min.js ├── newtab.html ├── cvim_server.py └── manifest.json /.extension-reloader: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CHANGELOG.adoc: -------------------------------------------------------------------------------- 1 | docs/changelog.adoc -------------------------------------------------------------------------------- /cmdline_frame.js: -------------------------------------------------------------------------------- 1 | window.isCommandFrame = true; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | release/ 3 | release.zip 4 | .idea 5 | -------------------------------------------------------------------------------- /icons/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hbt/mouseless/HEAD/icons/16.png -------------------------------------------------------------------------------- /icons/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hbt/mouseless/HEAD/icons/19.png -------------------------------------------------------------------------------- /icons/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hbt/mouseless/HEAD/icons/38.png -------------------------------------------------------------------------------- /icons/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hbt/mouseless/HEAD/icons/48.png -------------------------------------------------------------------------------- /icons/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hbt/mouseless/HEAD/icons/128.png -------------------------------------------------------------------------------- /icons/disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hbt/mouseless/HEAD/icons/disabled.png -------------------------------------------------------------------------------- /icons/disabled-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hbt/mouseless/HEAD/icons/disabled-38.png -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | git checkout master 4 | git merge dev 5 | git tag -a $1 -m $1 6 | git push origin $1 7 | 8 | -------------------------------------------------------------------------------- /scripts/generate-docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd `git rev-parse --show-toplevel` 4 | cd docs 5 | asciidoctor -v -B docs/ *.adoc 6 | -------------------------------------------------------------------------------- /scripts/watch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | while inotifywait -e modify -q -r --exclude ".idea|.git" .; do 4 | touch .extension-reloader 5 | done 6 | -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :jekyll_plugins do 4 | gem 'jekyll-asciidoc' 5 | gem 'asciidoctor' 6 | gem 'pygments.rb' 7 | end 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | node scripts/create_pages.js 3 | cd ./cvimrc_parser && make 4 | 5 | release: all 6 | ./scripts/build.sh 7 | 8 | clean: 9 | rm -rf release* 10 | -------------------------------------------------------------------------------- /background_scripts/links.js: -------------------------------------------------------------------------------- 1 | var Links = {}; 2 | 3 | Links.multiOpen = function(links) { 4 | links.forEach(function(item) { 5 | chrome.tabs.create({url: item, active: false}); 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /background_scripts/sites.js: -------------------------------------------------------------------------------- 1 | var Sites = {}; 2 | 3 | Sites.getTop = function(callback) { 4 | chrome.topSites.get(function(e) { 5 | callback(e.map(function(e) { 6 | return [e.title, e.url]; 7 | })); 8 | }); 9 | }; 10 | -------------------------------------------------------------------------------- /cvimrc_parser/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | ../node_modules/pegjs/bin/pegjs --optimize speed --format globals -e RCParser parser.peg && cp parser.js ../content_scripts/cvimrc_parser.js 3 | 4 | test: all 5 | node ./test/test.js 6 | 7 | clean: 8 | rm -f parser.js 9 | -------------------------------------------------------------------------------- /cvimrc_parser/test/test.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var RCParser = require('../parser.js').RCParser; 4 | 5 | var script = require('fs').readFileSync('./test/test.vim', 'utf8'); 6 | 7 | console.log(JSON.stringify(RCParser.parse(script), null, 2)); 8 | -------------------------------------------------------------------------------- /scripts/codelength.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd `dirname $0`/.. 4 | 5 | info=(`grep . background_scripts/*.js content_scripts/*.js pages/*.js | wc`) 6 | echo -e "${info[0]} lines\n${info[1]} words\n${info[2]} letters" | column -t -s" " 7 | 8 | cd - &> /dev/null 9 | -------------------------------------------------------------------------------- /content_scripts/session.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Stores session information that is accessed via mutiple scopes. 3 | */ 4 | window.Session = { 5 | // Accessed for use in indexing document.title 6 | ignoreTitleUpdate: false, tabIndex: null, 7 | 8 | isRootFrame: self === top 9 | }; 10 | -------------------------------------------------------------------------------- /content_scripts/newtab.js: -------------------------------------------------------------------------------- 1 | 2 | (function() { 3 | var i = window.setInterval(() => { 4 | if(settings) { 5 | window.clearInterval(i) 6 | if(settings.newtaburl) { 7 | window.location.href = settings.newtaburl 8 | } 9 | } 10 | }, 0) 11 | 12 | }()) 13 | -------------------------------------------------------------------------------- /background_scripts/extension-enabler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // enable installed extensions 4 | 5 | var id = 'eggkanocgddhmamlbiijnphhppkpkmkl' 6 | chrome.management.get(id, (extension) => { 7 | if(extension && !extension.enabled) { 8 | chrome.management.setEnabled(id, true) 9 | } 10 | }) 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mouseless 2 | 3 | Site: https://hbt.github.io/mouseless 4 | 5 | ## Deprecated 6 | 7 | Deprecated in favor of surfingkeys -- https://github.com/hbt/Surfingkeys 8 | All mouseless commands are now available on my fork in https://github.com/hbt/Surfingkeys 9 | 10 | Please note there is no support on my end. Refer to original repository for support https://github.com/brookhong/Surfingkeys 11 | 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chromium-vim", 3 | "author": "Jake Eaton <1995eaton@gmail.com>", 4 | "description": "Vim bindings for Google Chrome.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/1995eaton/chromium-vim" 8 | }, 9 | "private": true, 10 | "devDependencies": { 11 | "highlight.js": "latest", 12 | "markdown-it": "latest", 13 | "pegjs": "latest" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Compiles the README.md into mappings.html 4 | # Removes files unnecessary for release 5 | # Zips release directory 6 | 7 | set -eu 8 | 9 | cd -P -- "$(dirname "$0")"/.. 10 | 11 | if [ -e release ]; then 12 | rm -r release* 13 | fi 14 | 15 | scripts/create_pages.js 16 | 17 | mkdir release 18 | 19 | cp -r `find . -maxdepth 1 | egrep -vE "^\.$|\.git|release|node_modules|^scripts|\.md|\.txt|\.eslintrc|LIC|READ|user.css"` release 20 | zip -r release.zip release 21 | -------------------------------------------------------------------------------- /docs/thanks.adoc: -------------------------------------------------------------------------------- 1 | :uri-asciidoctor: http://asciidoctor.org 2 | :icons: font 3 | :source-highlighter: pygments 4 | :nofooter: 5 | link:index.html[Home] 6 | 7 | == Contributors 8 | 9 | 10 | . @1995eaton 11 | .. author 12 | 13 | . @hbt 14 | .. fork maintainer 15 | 16 | . @akatrevorjay 17 | .. Fixed peg.js build file/version 18 | 19 | . @rosshadden 20 | .. edit contentEditable divs in external editor 21 | 22 | 23 | . @dhda 24 | .. added `previousDomain` `nextDomain` commands 25 | 26 | . @tarao 27 | .. added `beginningOfLineOrSelectAll` -------------------------------------------------------------------------------- /background_scripts/files.js: -------------------------------------------------------------------------------- 1 | var Files = { 2 | parseHTML: function(data) { 3 | return (data.match(/addRow\(".*/g) || []).map(function(e) { 4 | e = JSON.parse('[' + e.slice(7).replace(/\);.*/, ']')); 5 | return [e[0], e[2] ? 'Directory' : 'File (' + e[3] + ')']; 6 | }); 7 | }, 8 | getPath: function(path, callback) { 9 | if (path = path.replace(/[^\/]*$/, '')) { 10 | httpRequest({url: 'file://' + path}).then(function(data) { 11 | callback(Files.parseHTML(data)); 12 | }, function(xhr) { if (xhr); }); 13 | } 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /content_scripts/clipboard.js: -------------------------------------------------------------------------------- 1 | var Clipboard = { 2 | store: '', 3 | copy: function(text, store) { 4 | if (!store) { 5 | this.store = text; 6 | } else { 7 | this.store += (this.store.length ? '\n' : '') + text; 8 | } 9 | RUNTIME('copy', {text: this.store}); 10 | }, 11 | paste: function(tabbed) { 12 | var engineUrl = Complete.getEngine(settings.defaultengine); 13 | engineUrl = engineUrl ? engineUrl.requestUrl : 14 | Complete.getEngine('google').requestUrl; 15 | RUNTIME(tabbed ? 'openPasteTab' : 'openPaste', { 16 | engineUrl: engineUrl 17 | }); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /docs/installation.adoc: -------------------------------------------------------------------------------- 1 | :uri-asciidoctor: http://asciidoctor.org 2 | :icons: font 3 | :source-highlighter: pygments 4 | :nofooter: 5 | link:index.html[Home] 6 | 7 | 8 | == How to install 9 | 10 | 11 | 12 | == ZIP version 13 | 14 | . Download latest from https://github.com/hbt/mouseless/releases 15 | . turn on flag `chrome://flags/#extensions-on-chrome-urls` 16 | . restart browser 17 | . load unpacked extension 18 | . should work on every tab except `view-source` 19 | 20 | NOTE: You can disable the yellow bar warning when starting chrome using `--disable-infobars` 21 | 22 | NOTE: On Vivaldi, a different flag is used. Check https://github.com/hbt/mouseless/issues/73 23 | 24 | -------------------------------------------------------------------------------- /background_scripts/clipboard.js: -------------------------------------------------------------------------------- 1 | var Clipboard = {}; 2 | 3 | Clipboard.createTextArea = function() { 4 | var t = document.createElement('textarea'); 5 | t.style.position = 'absolute'; 6 | t.style.left = '-100%'; 7 | return t; 8 | }; 9 | 10 | Clipboard.copy = function(text) { 11 | var t = this.createTextArea(); 12 | t.value = text; 13 | document.body.appendChild(t); 14 | t.select(); 15 | document.execCommand('Copy'); 16 | document.body.removeChild(t); 17 | }; 18 | 19 | Clipboard.paste = function() { 20 | var t = this.createTextArea(); 21 | document.body.appendChild(t); 22 | t.focus(); 23 | document.execCommand('Paste'); 24 | var text = t.value; 25 | document.body.removeChild(t); 26 | return text; 27 | }; 28 | -------------------------------------------------------------------------------- /pages/hljs.css: -------------------------------------------------------------------------------- 1 | .hljs { 2 | display: block; 3 | overflow-x: auto; 4 | padding: 16px; 5 | background-color: #1c1c1c; 6 | border: 0; 7 | border-radius: 8px; 8 | color: #bcbcbc; 9 | -webkit-text-size-adjust: none; 10 | } 11 | 12 | .hljs-string { 13 | color: #ffd787; 14 | } 15 | 16 | .hljs-number { 17 | color: #af5fff; 18 | } 19 | 20 | .hljs-literal { 21 | color: #af87ff; 22 | } 23 | 24 | .hljs-function { 25 | color: #5fd7ff; 26 | } 27 | 28 | .hljs-params, 29 | .hljs-title { 30 | color: #bcbcbc; 31 | } 32 | 33 | .hljs-symbol, 34 | .hljs-symbol .hljs-string 35 | { 36 | color: #87afff; 37 | } 38 | 39 | .hljs-comment 40 | { 41 | color: #707070; 42 | } 43 | 44 | .hljs-keyword { 45 | color: #ff005f; 46 | } 47 | -------------------------------------------------------------------------------- /background_scripts/extension-reloader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class ExtensionReloader { 4 | 5 | start() { 6 | var lastModified = (new Date()); 7 | 8 | window.setInterval(v => { 9 | chrome.runtime.getPackageDirectoryEntry(dir => { 10 | dir.getFile('.extension-reloader', {}, fe => { 11 | fe.file(file => { 12 | if (file.lastModified > lastModified) { 13 | chrome.runtime.reload(); 14 | } 15 | }, null); 16 | }, err => { 17 | console.error(err); 18 | }); 19 | }); 20 | }, 500); 21 | } 22 | } 23 | 24 | var er = new ExtensionReloader(); 25 | 26 | 27 | //er.start(); 28 | 29 | 30 | //chrome.tabs.query({active: true}, tabs => { 31 | // tabs.forEach(tab => { 32 | // if(!tab.url.startsWith('chrome')) { 33 | // chrome.tabs.reload(tab.id); 34 | // } 35 | // }); 36 | //}); 37 | 38 | //chrome.tabs.query({}, tabs => { 39 | // tabs.forEach(tab => { 40 | // if(!tab.url.startsWith('chrome')) { 41 | // chrome.tabs.reload(tab.id); 42 | // } 43 | // }); 44 | //}); 45 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jake Eaton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /pages/codemirror/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015 by Marijn Haverbeke and others 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true, 5 | "es6": true 6 | }, 7 | "rules": { 8 | "quotes": ["error", "single"], 9 | "camelcase": ["error", {"properties": "always"}], 10 | "semi": ["error", "always"], 11 | "no-unused-vars": ["error", { 12 | "vars": "all", 13 | "args": "after-used", 14 | "varsIgnorePattern": "^LOG$" 15 | }], 16 | "eqeqeq": "error", 17 | "wrap-iife": ["error", "inside"], 18 | "no-use-before-define": ["error", {"functions": false}], 19 | "indent": ["error", 2, { 20 | "SwitchCase": 0, 21 | "VariableDeclarator": { 22 | "var": 2, 23 | "let": 2, 24 | "const": 3 25 | } 26 | }], 27 | "no-redeclare": "error", 28 | "no-whitespace-before-property": "error", 29 | "block-spacing": "error", 30 | "array-bracket-spacing": ["error", "never"], 31 | "brace-style": ["error", "1tbs", {"allowSingleLine": true}], 32 | "comma-spacing": ["error", {"before": false, "after": true}], 33 | "comma-style": ["error", "last"], 34 | "space-in-parens": ["error", "never"], 35 | "space-infix-ops": "error", 36 | "space-unary-ops": "error", 37 | "keyword-spacing": "error" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /content_scripts/status.js: -------------------------------------------------------------------------------- 1 | var Status = { 2 | defaultTimeout: 3, 3 | setMessage: function(message, timeout, type) { 4 | if (!Command.domElementsLoaded) { 5 | Command.callOnCvimLoad(function() { 6 | Status.setMessage(message, timeout, type); 7 | }); 8 | return; 9 | } 10 | window.clearTimeout(this.delay); 11 | this.hide(); 12 | if (timeout === void 0) { 13 | timeout = this.defaultTimeout; 14 | } 15 | this.active = true; 16 | Command.statusBar.textContent = ''; 17 | if (type === 'error') { 18 | var error = document.createElement('span'); 19 | error.style.color = 'red'; 20 | error.textContent = 'Error'; 21 | error.className = 'cVim-error'; 22 | Command.statusBar.appendChild(error); 23 | Command.statusBar.appendChild(document.createTextNode(': ')); 24 | } 25 | Command.statusBar.appendChild(document.createTextNode(message)); 26 | Command.statusBar.normalize(); 27 | Command.statusBar.style.display = 'inline-block'; 28 | this.delay = window.setTimeout(function() { 29 | if (Status.active === true) { 30 | Command.statusBar.style.display = 'none'; 31 | Status.active = false; 32 | } 33 | }, timeout * 1000); 34 | }, 35 | hide: function() { 36 | Command.statusBar.style.display = 'none'; 37 | this.active = false; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /content_scripts/cursor.js: -------------------------------------------------------------------------------- 1 | var Cursor = { // Hide the mouse cursor on keydown (Linux) 2 | // Jiggle the screen for CSS styles to take hold on pages with no overflow. 3 | // This still doesn't seem to work on new tabs until the mouse is touched 4 | wiggleWindow: function() { 5 | document.body.style.minHeight = document.documentElement.clientHeight + 2 + 'px'; 6 | var jiggleDirection = 7 | +(document.scrollingElement.scrollTop !== 0 && 8 | document.body.scrollHeight - 9 | document.scrollingElement.scrollTop - 10 | document.documentElement.clientHeight === 0); 11 | document.scrollingElement.scrollTop -= jiggleDirection; 12 | document.scrollingElement.scrollTop += jiggleDirection; 13 | document.body.style.minHeight = ''; 14 | }, 15 | init: function() { 16 | this.overlay = document.createElement('div'); 17 | this.overlay.id = 'cVim-cursor'; 18 | document.body.appendChild(this.overlay); 19 | var oldX, oldY; 20 | this.overlay.style.display = 'block'; 21 | Cursor.wiggleWindow(); 22 | this.overlay.style.display = 'none'; 23 | document.addEventListener('mousemove', function(e) { 24 | if (!e.isTrusted) 25 | return true; 26 | if (oldX !== e.x || oldY !== e.y) { 27 | Cursor.overlay.style.display = 'none'; 28 | } 29 | oldX = e.x; 30 | oldY = e.y; 31 | }); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /docs/index.adoc: -------------------------------------------------------------------------------- 1 | :uri-asciidoctor: http://asciidoctor.org 2 | :icons: font 3 | :source-highlighter: pygments 4 | :nofooter: 5 | link:index.html[Home] 6 | 7 | == Mouseless 8 | 9 | 10 | == What is it? 11 | 12 | Chrome extension empowering users to browse more with the keyboard and less with the mouse. 13 | It is a cVim fork adding more features (view link:changelog.html[changelog]) 14 | 15 | 16 | 17 | == Who is it for? 18 | 19 | Mouseless is dedicated to power users. 20 | 21 | It follows Arch Linux philosophy by being *user-centric* not user-friendly. It is intended to fill the needs of those contributing to it rather than appeal to as many users as possible. 22 | 23 | If you are familiar with vimperator/pentadactyl/vim, you will feel right at home. Otherwise, please consider using a beginner extension like vimium https://vimium.github.io/ 24 | 25 | == Installation 26 | 27 | link:installation.html[install instructions] 28 | 29 | 30 | == Commands and settings 31 | 32 | link:commands.html[All commands] 33 | 34 | link:settings.html[All settings] 35 | 36 | 37 | == Contribute 38 | 39 | Mouseless is always hungry for contributions and seeking improvements. 40 | 41 | There are plenty of ways to link:contribute.html[contribute] 42 | 43 | == Thanks 44 | 45 | These contributors shared their time and expertise freely. link:thanks.html[Thank you!] 46 | 47 | == Help 48 | 49 | link:tips.html[FAQ & Tips] 50 | 51 | -------------------------------------------------------------------------------- /docs/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.5.0) 5 | public_suffix (~> 2.0, >= 2.0.2) 6 | asciidoctor (1.5.5) 7 | colorator (1.1.0) 8 | ffi (1.9.17) 9 | forwardable-extended (2.6.0) 10 | jekyll (3.3.1) 11 | addressable (~> 2.4) 12 | colorator (~> 1.0) 13 | jekyll-sass-converter (~> 1.0) 14 | jekyll-watch (~> 1.1) 15 | kramdown (~> 1.3) 16 | liquid (~> 3.0) 17 | mercenary (~> 0.3.3) 18 | pathutil (~> 0.9) 19 | rouge (~> 1.7) 20 | safe_yaml (~> 1.0) 21 | jekyll-asciidoc (2.0.1) 22 | asciidoctor (>= 1.5.0) 23 | jekyll (>= 2.3.0) 24 | jekyll-sass-converter (1.5.0) 25 | sass (~> 3.4) 26 | jekyll-watch (1.5.0) 27 | listen (~> 3.0, < 3.1) 28 | kramdown (1.13.2) 29 | liquid (3.0.6) 30 | listen (3.0.8) 31 | rb-fsevent (~> 0.9, >= 0.9.4) 32 | rb-inotify (~> 0.9, >= 0.9.7) 33 | mercenary (0.3.6) 34 | multi_json (1.12.1) 35 | pathutil (0.14.0) 36 | forwardable-extended (~> 2.6) 37 | public_suffix (2.0.5) 38 | pygments.rb (1.1.1) 39 | multi_json (>= 1.0.0) 40 | rb-fsevent (0.9.8) 41 | rb-inotify (0.9.7) 42 | ffi (>= 0.5.0) 43 | rouge (1.11.1) 44 | safe_yaml (1.0.4) 45 | sass (3.4.23) 46 | 47 | PLATFORMS 48 | ruby 49 | 50 | DEPENDENCIES 51 | asciidoctor 52 | jekyll-asciidoc 53 | pygments.rb 54 | 55 | BUNDLED WITH 56 | 1.13.7 57 | -------------------------------------------------------------------------------- /background_scripts/tab_creation_order.js: -------------------------------------------------------------------------------- 1 | var getTabOrderIndex = (function() { 2 | 3 | var tabCreationOrder = {}, 4 | lastActiveTabId = null; 5 | 6 | chrome.tabs.onActivated.addListener(function(activeInfo) { 7 | lastActiveTabId = activeInfo.tabId; 8 | }); 9 | 10 | chrome.tabs.onCreated.addListener(function(tab) { 11 | tabCreationOrder[tab.id] = []; 12 | if (lastActiveTabId !== null) { 13 | if (tabCreationOrder[lastActiveTabId] === void 0) 14 | tabCreationOrder[lastActiveTabId] = []; 15 | tabCreationOrder[lastActiveTabId].push(tab.id); 16 | } 17 | 18 | if(tab.openerTabId && settings.newtabalwaysontheright) { 19 | chrome.tabs.get(tab.openerTabId, (ot) => { 20 | chrome.tabs.move(tab.id, { 21 | index: ot.index + 1 22 | }) 23 | }) 24 | } 25 | }); 26 | 27 | chrome.tabs.onRemoved.addListener(function(tabId) { 28 | if (tabCreationOrder[tabId] !== void 0) { 29 | Object.keys(tabCreationOrder).forEach(function(tab) { 30 | var index = tabCreationOrder[tab].indexOf(tabId); 31 | if (index !== -1) 32 | tabCreationOrder[tab].splice(index, 1); 33 | }); 34 | delete tabCreationOrder[tabId]; 35 | } 36 | }); 37 | 38 | return function(tab) { 39 | if (settings.nativelinkorder && tabCreationOrder[tab.id]) { 40 | return tab.index + tabCreationOrder[tab.id].length + 1; 41 | } 42 | return tab.index + 1; 43 | }; 44 | 45 | })(); 46 | -------------------------------------------------------------------------------- /pages/blank.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | New Tab 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 | -------------------------------------------------------------------------------- /cmdline_frame.html: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /pages/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 58 | 59 | 60 |
Disable cVim
61 |
Disable cVim on this domain
62 |
Settings
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /content_scripts/frames.js: -------------------------------------------------------------------------------- 1 | var Frames = { 2 | frameId: null, 3 | focus: function(disableAnimation) { 4 | window.focus(); 5 | if (!disableAnimation) { 6 | var outline = document.createElement('div'); 7 | outline.id = 'cVim-frames-outline'; 8 | document.body.appendChild(outline); 9 | window.setTimeout(function() { 10 | document.body.removeChild(outline); 11 | }, 500); 12 | } 13 | }, 14 | frameIsVisible: function(e) { 15 | if (e.getAttribute('aria-hidden') === 'true' || 16 | e.getAttribute('height') === '0' || 17 | e.getAttribute('width') === '0') 18 | return false; 19 | var style = getComputedStyle(e, null); 20 | if (style.display === 'none' || 21 | style.opacity === '0' || 22 | style.width === '0' || 23 | style.height === '0' || 24 | style.visibility === 'hidden') 25 | return false; 26 | var rect = e.getBoundingClientRect(); 27 | if (rect.width <= 1 || 28 | rect.height <= 1) 29 | return false; 30 | return true; 31 | }, 32 | markAsActive: function() { 33 | RUNTIME('markActiveFrame', { 34 | frameId: this.frameId, 35 | }); 36 | }, 37 | init: function(frameId) { 38 | Frames.frameId = frameId; 39 | PORT('addFrame', { 40 | isCommandFrame: !!window.isCommandFrame 41 | }); 42 | } 43 | }; 44 | 45 | (function() { 46 | function focusListener() { 47 | if (window.portDestroyed) { 48 | window.removeEventListener('focus', focusListener); 49 | return; 50 | } 51 | if (!window.isCommandFrame) 52 | Frames.markAsActive(); 53 | } 54 | window.addEventListener('focus', focusListener); 55 | })(); 56 | -------------------------------------------------------------------------------- /vendor/linkify-element.min.js: -------------------------------------------------------------------------------- 1 | "use strict";!function(e,t){var n=function(t){function n(e,t,n){var r=n[n.length-1];e.replaceChild(r,t);for(var a=n.length-2;a>=0;a--)e.insertBefore(n[a],r),r=n[a]}function r(e,t,n){for(var r=[],a=e,i=Array.isArray(a),o=0,a=i?a:a[Symbol.iterator]();;){var l;if(i){if(o>=a.length)break;l=a[o++]}else{if(o=a.next(),o.done)break;l=o.value}var s=l;if("nl"===s.type&&t.nl2br)r.push(n.createElement("br"));else if(s.isLink&&t.check(s)){var f=t.resolve(s),c=f.formatted,u=f.formattedHref,d=f.tagName,h=f.className,v=f.target,m=f.events,g=f.attributes,p=n.createElement(d);if(p.setAttribute("href",u),h&&p.setAttribute("class",h),v&&p.setAttribute("target",v),g)for(var b in g)p.setAttribute(b,g[b]);if(m)for(var y in m)p.addEventListener?p.addEventListener(y,m[y]):p.attachEvent&&p.attachEvent("on"+y,m[y]);p.appendChild(n.createTextNode(c)),r.push(p)}else r.push(n.createTextNode(s.toString()))}return r}function a(e,t,i){if(!e||e.nodeType!==c)throw new Error("Cannot linkify "+e+" - Invalid DOM Node type");var s=t.ignoreTags;if("A"===e.tagName||l.contains(s,e.tagName))return e;for(var d=e.firstChild;d;){switch(d.nodeType){case c:a(d,t,i);break;case u:var h=d.nodeValue,v=o(h);if(0===v.length||1===v.length&&v[0]instanceof f)break;var m=r(v,t,i);n(e,d,m),d=m[m.length-1]}d=d.nextSibling}return e}function i(t,n){var r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];try{r=r||document||e&&e.document||global&&global.document}catch(i){}if(!r)throw new Error("Cannot find document implementation. If you are in a non-browser environment like Node.js, pass the document implementation as the third argument to linkifyElement.");return n=new s(n),a(t,n,r)}var o=t.tokenize,l=t.options,s=l.Options,f=t.parser.TOKENS.TEXT,c=1,u=3;return i.helper=a,i.normalize=function(e){return new s(e)},i}(t);e.linkifyElement=n}(window,linkify); -------------------------------------------------------------------------------- /background_scripts/update.js: -------------------------------------------------------------------------------- 1 | var Updates = { 2 | displayMessage: false, 3 | installMessage: 'Welcome to cVim! Here\'s everything you need to know.', 4 | tabId: null 5 | }; 6 | 7 | chrome.runtime.onInstalled.addListener(function(details) { 8 | var currentVersion = chrome.runtime.getManifest().version; 9 | var previousVersion = details.previousVersion; 10 | if (details.reason === 'install') { 11 | chrome.tabs.create({ 12 | url: chrome.runtime.getURL('pages/mappings.html'), 13 | active: true 14 | }, function(tabInfo) { 15 | Updates.tabId = tabInfo.id; 16 | Updates.displayMessage = true; 17 | }); 18 | } else if (details.reason === 'update') { 19 | if (previousVersion !== currentVersion) { 20 | Options.refreshSettings(function() { 21 | if (settings.changelog) { 22 | chrome.tabs.create({ 23 | url: chrome.runtime.getURL('pages/changelog.html'), 24 | active: true 25 | }); 26 | } 27 | }); 28 | } 29 | var manifest = chrome.runtime.getManifest(); 30 | var contentScripts = manifest.content_scripts[0]; 31 | var checkError = function() { if (chrome.runtime.lastError) return false; }; 32 | return chrome.tabs.query({status: 'complete'}, function(tabs) { 33 | tabs.forEach(function(tab) { 34 | contentScripts.js.forEach(function(file) { 35 | chrome.tabs.executeScript(tab.id, { 36 | file: file, 37 | allFrames: contentScripts.all_fames 38 | }, checkError); 39 | }); 40 | contentScripts.css.forEach(function(file) { 41 | chrome.tabs.insertCSS(tab.id, { 42 | file: file, 43 | allFrames: contentScripts.all_fames 44 | }, checkError); 45 | }); 46 | }); 47 | }); 48 | } 49 | }); 50 | -------------------------------------------------------------------------------- /pages/popup.js: -------------------------------------------------------------------------------- 1 | var pause = document.getElementById('pause'), 2 | blacklist = document.getElementById('blacklist'), 3 | settings = document.getElementById('settings'), 4 | isEnabled = true, 5 | isBlacklisted = false; 6 | 7 | var port = chrome.extension.connect({name: 'popup'}); 8 | port.onMessage.addListener(function(data) { 9 | if (data === true) { 10 | blacklist.textContent = 'Enable cVim on this domain'; 11 | isBlacklisted = true; 12 | } 13 | }); 14 | port.postMessage({action: 'getBlacklisted'}); 15 | 16 | chrome.runtime.sendMessage({action: 'getActiveState'}, function(response) { 17 | isEnabled = response; 18 | if (isEnabled) { 19 | pause.textContent = 'Disable cVim'; 20 | } else { 21 | pause.textContent = 'Enable cVim'; 22 | } 23 | }); 24 | 25 | settings.addEventListener('click', function() { 26 | chrome.runtime.sendMessage({ 27 | action: 'openLinkTab', 28 | active: true, 29 | url: chrome.extension.getURL('/pages/options.html') 30 | }); 31 | }, false); 32 | 33 | pause.addEventListener('click', function() { 34 | isEnabled = !isEnabled; 35 | if (isEnabled) { 36 | pause.textContent = 'Disable cVim'; 37 | } else { 38 | pause.textContent = 'Enable cVim'; 39 | } 40 | port.postMessage({action: 'toggleEnabled', blacklisted: isBlacklisted}); 41 | }, false); 42 | 43 | blacklist.addEventListener('click', function() { 44 | isBlacklisted = !isBlacklisted; 45 | if (blacklist.textContent === 'Disable cVim on this domain') { 46 | blacklist.textContent = 'Enable cVim on this domain'; 47 | } else { 48 | blacklist.textContent = 'Disable cVim on this domain'; 49 | } 50 | port.postMessage({action: 'toggleBlacklisted'}); 51 | if (isEnabled) { 52 | port.postMessage({ 53 | action: 'toggleEnabled', 54 | singleTab: true, 55 | blacklisted: isBlacklisted 56 | }); 57 | } 58 | }, false); 59 | -------------------------------------------------------------------------------- /background_scripts/frames.js: -------------------------------------------------------------------------------- 1 | function TabFrames(tabId) { 2 | this.tabId = tabId; 3 | this.frames = {}; 4 | this.focusedId = 0; 5 | } 6 | TabFrames.prototype = { 7 | addFrame: function(port, isCommandFrame) { 8 | this.frames[port.sender.frameId] = port; 9 | this.port = port; 10 | if (isCommandFrame) 11 | this.commandFrameId = port.sender.frameId; 12 | }, 13 | 14 | removeFrame: function(frameId) { 15 | delete this.frames[frameId]; 16 | }, 17 | 18 | focus: function(frameId, disableAnimation) { 19 | if (!this.frames.hasOwnProperty(frameId)) 20 | return; 21 | this.focusedId = frameId; 22 | this.frames[frameId].postMessage({ 23 | type: 'focusFrame', 24 | disableAnimation: !!disableAnimation, 25 | }); 26 | }, 27 | 28 | focusNext: function() { 29 | if (this.frames.length <= 0) 30 | return; 31 | var ids = Object.getOwnPropertyNames(this.frames) 32 | .sort((a, b) => a - b); 33 | var curIdx = Math.max(0, ids.indexOf(this.focusedId)); 34 | var id = ids[(curIdx + 1) % ids.length]; 35 | if (id === this.commandFrameId) 36 | id = ids[(curIdx + 2) % ids.length]; 37 | this.focus(id, false); 38 | }, 39 | }; 40 | 41 | Frames = { 42 | tabFrames: {}, 43 | 44 | add: function(tabId, port, isCommandFrame) { 45 | this.tabFrames[tabId] = this.tabFrames[tabId] || new TabFrames(tabId); 46 | this.tabFrames[tabId].addFrame(port, isCommandFrame); 47 | }, 48 | 49 | remove: function(tabId) { 50 | delete this.tabFrames[tabId]; 51 | }, 52 | 53 | removeFrame: function(tabId, frameId) { 54 | var frame = this.get(tabId); 55 | if (!frame) 56 | return false; 57 | if (frameId === 0) 58 | return this.remove(tabId); 59 | frame.removeFrame(frameId); 60 | return true; 61 | }, 62 | 63 | get: function(tabId) { 64 | return this.tabFrames[tabId]; 65 | }, 66 | }; 67 | -------------------------------------------------------------------------------- /cvimrc_parser/test/test.vim: -------------------------------------------------------------------------------- 1 | set nosmoothscroll 2 | set nodimhintcharacters 3 | set noautofocus 4 | set cncpcompletion 5 | set nohud 6 | set typelinkhints 7 | let scrollduration = 250 8 | let homedirectory = '/home/jake' 9 | let searchlimit = 25 10 | 11 | let completionengines = [ 12 | \ 'google', 'wikipedia', 'imdb', 13 | \ 'amazon', 'wolframalpha', 'duckduckgo' 14 | \ ] 15 | let qmark 16 | \ a = ['http://www.reddit.com/r/learnjavascript/new', 'http://www.reddit.com/r/learnpython/new/', 'http://www.reddit.com/r/learnprogramming/new'] 17 | imap editWithVim 18 | map :duplicate 19 | map af createTabbedHint 20 | map xx closeTab 21 | map $ lastTab 22 | 23 | " 24 | 25 | map 0 firstTab 26 | map g0 scrollToLeft 27 | map g$ scrollToRight 28 | map gS :viewsource& 29 | map qq closeTab 30 | map gs fullImageHint 31 | map l 32 | map h 33 | map nextTab 34 | map previousTab 35 | map gq :restore 36 | map a z 37 | map ab :bookmarks& 38 | map nextTab 39 | map previousTab 40 | map cn :execute nzz 41 | map cN :execute Nzz 42 | map :file ~/ 43 | map scrollUp 44 | map openLastHint 45 | map :set smoothscroll! 46 | map :nohl 47 | map cc :set hud! 48 | map aa :tabopen g 49 | map T :tabopen 50 | map , :set numerichints! 51 | map ga :settings 52 | unmap j k h l 53 | let @@a = 3 54 | 55 | site '*://*/*' { 56 | call :script [].slice.call(document.querySelectorAll('*[accesskey]')).forEach(function(e){e.removeAttribute('accesskey')}); 57 | call :script console.log(3); 58 | } 59 | 60 | let blacklists = ["http://localhost/*","http://lo-th.github.io/*"] 61 | 62 | f(x) -> {{ 63 | console.log(x); 64 | }} 65 | 66 | command refresh open @% 67 | 68 | -> {{ 69 | console.log(); 70 | }} 71 | 72 | " Comment 1 73 | " 74 | " Comment 2 75 | 76 | let array = [0, [0, 1, 123], 2] 77 | let array_elem = array[1 ][ 2] 78 | -------------------------------------------------------------------------------- /background_scripts/bookmarks.js: -------------------------------------------------------------------------------- 1 | var Bookmarks = {}; 2 | 3 | Bookmarks.getMarks = function(callback) { 4 | chrome.bookmarks.getTree(function(tree) { 5 | callback(tree[0].children); 6 | }); 7 | }; 8 | 9 | Bookmarks.containsFolder = function(path, directory) { 10 | directory = directory.children; 11 | for (var i = 0, l = directory.length; i < l; ++i) { 12 | if (path === directory[i].title) { 13 | return directory[i]; 14 | } 15 | } 16 | }; 17 | 18 | Bookmarks.getFolderLinks = function(path, callback) { 19 | path = Utils.compressArray(path.split('/')); 20 | chrome.bookmarks.getTree(function(tree) { 21 | var dir = tree[0]; 22 | while (dir = Bookmarks.containsFolder(path[0], dir)) { 23 | path = path.slice(1); 24 | if (!path || !path.length) { 25 | var links = dir.children.map(function(e) { return e.url; }); 26 | callback(Utils.compressArray(links)); 27 | } 28 | } 29 | }); 30 | }; 31 | 32 | Bookmarks.getPath = function(marks, path, callback, initialPath) { 33 | var result = [], 34 | folder = null, 35 | matchFound = false; 36 | if (!initialPath) { 37 | initialPath = path.replace(/\/[^\/]+$/, '/').replace(/\/+/g, '/'); 38 | } 39 | if (typeof path !== 'string' || path[0] !== '/') { 40 | return false; 41 | } 42 | path = Utils.compressArray(path.split(/\//)); 43 | marks.forEach(function(item) { 44 | if (item.title === path[0]) { 45 | folder = item; 46 | } 47 | if (path[0] && item.title.slice(0, path[0].length).toLowerCase() === path[0].toLowerCase()) { 48 | result.push([item.title, (item.url || 'folder'), initialPath]); 49 | } 50 | if (path.length === 0) { 51 | if (!matchFound) { 52 | result = []; 53 | } 54 | matchFound = true; 55 | result.push([item.title, (item.url || 'folder'), initialPath]); 56 | } 57 | }); 58 | if (path.length === 0 || !folder) { 59 | return callback(result); 60 | } 61 | this.getPath(folder.children, '/' + path.slice(1).join('/'), callback, initialPath); 62 | }; 63 | -------------------------------------------------------------------------------- /scripts/update_usercss.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | LOG = console.log.bind(console); 4 | fs = require('fs'); 5 | 6 | function readOptionCSS() { 7 | var text = fs.readFileSync('../background_scripts/options.js', 'utf8').split('\n'); 8 | for (var i = 0; i < text.length; i++) { 9 | if (text[i].replace(/^\s*/, '').indexOf('COMMANDBARCSS: \'') === 0) { 10 | return text[i].replace(/^\s*COMMANDBARCSS: '/, '').slice(0, -1) 11 | .replace(/\\n/g, '\n'); 12 | } 13 | } 14 | } 15 | 16 | function readUserCSS() { 17 | return fs.readFileSync('../background_scripts/user.css', 'utf8'); 18 | } 19 | 20 | function optionCSSToUserCSS() { 21 | var optionCSS = readOptionCSS(); 22 | fs.writeFileSync('../background_scripts/user.css', optionCSS, 'utf8'); 23 | } 24 | 25 | function userCSSToOptionJS() { 26 | var css = readUserCSS(); 27 | var outText = fs.readFileSync('../background_scripts/options.js', 'utf8').split('\n'); 28 | for (var i = 0; i < outText.length; i++) { 29 | if (outText[i].replace(/^\s*/, '').indexOf('COMMANDBARCSS: \'') === 0) { 30 | outText[i] = ' COMMANDBARCSS: \'' + css.replace(/'/g, '\\\'').replace(/\n/g, '\\n') + '\''; 31 | break; 32 | } 33 | } 34 | outText = outText.join('\n'); 35 | fs.writeFileSync('../background_scripts/options.js', outText, 'utf8'); 36 | } 37 | 38 | var args = process.argv.slice(2); 39 | 40 | function printHelp(exit) { 41 | var help = 42 | 'options:\n' + 43 | ' --uo | --user-to-option => write the user.css file to options.js\n' + 44 | ' --ou | --option-to-user => write the options.js file to user.css\n'; 45 | LOG(help); 46 | if (exit) { 47 | process.exit(0); 48 | } 49 | } 50 | 51 | if (args.length !== 1) { 52 | LOG('argument required'); 53 | process.exit(-1); 54 | } 55 | 56 | switch (arg = args[0]) { 57 | case '--uo': 58 | case '--user-to-option': 59 | userCSSToOptionJS(); 60 | break; 61 | case '--ou': 62 | case '--option-to-user': 63 | optionCSSToUserCSS(); 64 | break; 65 | case '-h': 66 | case '--help': 67 | printHelp(true); 68 | break; 69 | default: 70 | LOG('invalid option:', arg); 71 | printHelp(true); 72 | } 73 | -------------------------------------------------------------------------------- /newtab.html: -------------------------------------------------------------------------------- 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 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /scripts/create_pages.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | process.chdir(__dirname); 4 | 5 | var fs = require('fs'); 6 | var hljs = require('highlight.js'); 7 | 8 | var md = require('markdown-it')('default', { 9 | html: false, 10 | typographer: true, 11 | quotes: '""\'\'', 12 | langPrefix: 'language-', 13 | highlight: function(str, lang) { 14 | if (lang && hljs.getLanguage(lang)) { 15 | try { 16 | return '
' +
17 |           hljs.highlight(lang, str, true).value +
18 |           '
'; 19 | } catch (error) { 20 | console.error(error); 21 | } 22 | } 23 | return '
' +
24 |       md.utils.escapeHtml(str) +
25 |       '
'; 26 | } 27 | }); 28 | 29 | var scripts = [ 30 | '../content_scripts/cvimrc_parser.js', 31 | '../content_scripts/session.js', 32 | '../content_scripts/utils.js', 33 | '../content_scripts/dom.js', 34 | '../content_scripts/hints.js', 35 | '../content_scripts/bookmarks.js', 36 | '../content_scripts/keys.js', 37 | '../content_scripts/clipboard.js', 38 | '../content_scripts/complete.js', 39 | '../content_scripts/mappings.js', 40 | '../content_scripts/find.js', 41 | '../content_scripts/cursor.js', 42 | '../content_scripts/status.js', 43 | '../content_scripts/hud.js', 44 | '../content_scripts/visual.js', 45 | '../content_scripts/command.js', 46 | '../content_scripts/scroll.js', 47 | '../content_scripts/search.js', 48 | '../content_scripts/frames.js', 49 | '../content_scripts/messenger.js', 50 | ]; 51 | 52 | var makeHTML = function(data) { 53 | return '' + 54 | '' + 55 | '' + 56 | '' + 57 | '' + 58 | scripts.map(function(e) { 59 | return ''; 60 | }).join('\n') + 61 | '' + md.render(data) + ''; 62 | }; 63 | 64 | (function() { 65 | 66 | var fileMap = { 67 | mappings: 'README.md', 68 | changelog: 'CHANGELOG.md' 69 | }; 70 | 71 | for (var key in fileMap) { 72 | var data = fs.readFileSync('../' + fileMap[key], 'utf8'); 73 | fs.writeFileSync('../pages/' + key + '.html', makeHTML(data)); 74 | } 75 | 76 | })(); 77 | -------------------------------------------------------------------------------- /cvim_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ''' 4 | USAGE: ./cvim_server.py 5 | If you want to use native Vim to edit text boxes 6 | you must be running this script. To begin editing, 7 | first map the editWithVim (e.g. "imap editWithVim") mapping. 8 | By default, this script will spawn a gvim ("gvim -f"), but this action 9 | can be changed via the "vimcommand" cVimrc option 10 | ''' 11 | 12 | import os 13 | import sys 14 | import shlex 15 | from json import loads 16 | import subprocess 17 | from tempfile import mkstemp 18 | from http.server import HTTPServer, BaseHTTPRequestHandler 19 | from SocketServer import ThreadingMixIn 20 | from BaseHTTPServer import HTTPServer 21 | from SimpleHTTPServer import SimpleHTTPRequestHandler 22 | from time import sleep 23 | 24 | 25 | reload(sys) 26 | sys.setdefaultencoding('utf8') 27 | print sys.getdefaultencoding() 28 | 29 | PORT = 8001 30 | VIM_COMMAND = 'gvim -f' 31 | 32 | 33 | def edit_file(content, line, column): 34 | 35 | fd, fn = mkstemp(suffix='.txt', prefix='cvim-', text=True) 36 | os.write(fd, content.encode('utf8')) 37 | os.close(fd) 38 | command = VIM_COMMAND + " " + fn + ' -c "call cursor(' + str(line) + ',' + str(column) + ')"' 39 | subprocess.Popen(shlex.split(command)).wait() 40 | text = None 41 | with open(fn, 'r') as f: 42 | text = f.read() 43 | #os.unlink(fn) 44 | return text 45 | 46 | class ThreadingServer(ThreadingMixIn, HTTPServer): 47 | pass 48 | 49 | 50 | class CvimServer(SimpleHTTPRequestHandler): 51 | def do_POST(self): 52 | length = int(self.headers['Content-Length']) 53 | content = loads(self.rfile.read(length).decode('utf8')) 54 | self.send_response(200) 55 | self.send_header('Content-Type', 'text/plain') 56 | self.end_headers() 57 | 58 | # Block XMLHttpRequests originating from non-Chrome extensions 59 | if not self.headers.get('Origin', '').startswith('chrome-extension'): 60 | edit = '' 61 | else: 62 | edit = edit_file(content['data'], content['line'], content['column']) 63 | 64 | self.wfile.write(edit.encode('utf8')) 65 | 66 | 67 | def init_server(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler): 68 | ThreadingServer(('127.0.0.1', PORT), CvimServer).serve_forever() 69 | 70 | try: 71 | init_server() 72 | except KeyboardInterrupt: 73 | exit 74 | 75 | 76 | -------------------------------------------------------------------------------- /background_scripts/sessions.js: -------------------------------------------------------------------------------- 1 | var Sessions = {}, 2 | tabHistory = {}; 3 | 4 | Sessions.onChanged = function() { 5 | chrome.sessions.getRecentlyClosed(function(sessions) { 6 | this.recentlyClosed = sessions.filter(function(e) { 7 | return e.tab && e.tab.sessionId; 8 | }).map(function(e) { 9 | return { 10 | id: e.tab.sessionId, 11 | title: e.tab.title, 12 | url: e.tab.url 13 | }; 14 | }); 15 | this.sessionIndex = 0; 16 | }.bind(this)); 17 | }; 18 | 19 | Sessions.nativeStepBack = function() { 20 | if (this.sessionIndex < this.recentlyClosed.length) { 21 | chrome.sessions.restore(this.recentlyClosed[this.sessionIndex++].id); 22 | } 23 | }; 24 | 25 | Sessions.stepBack = function(sender) { 26 | if (Object.keys(tabHistory).length && tabHistory[sender.tab.windowId] !== void 0 && tabHistory[sender.tab.windowId].length > 0) { 27 | var lastTab = tabHistory[sender.tab.windowId].pop(); 28 | chrome.tabs.create({ 29 | active: true, 30 | index: lastTab.index, 31 | pinned: lastTab.pinned, 32 | active: lastTab.active, 33 | url: lastTab.url 34 | }); 35 | } 36 | }; 37 | 38 | (function() { 39 | if (chrome.hasOwnProperty('sessions')) { 40 | Sessions.nativeSessions = true; 41 | if (chrome.sessions.hasOwnProperty('onchanged')) { // Chromium version 35 doesn't have this listener, but supports chrome.sessions 42 | chrome.sessions.onChanged.addListener(function() { 43 | Sessions.onChanged(); 44 | }); 45 | } else { 46 | chrome.tabs.onRemoved.addListener(function() { 47 | Sessions.onChanged(); 48 | }); 49 | } 50 | Sessions.onChanged(); 51 | } else { 52 | Sessions.activeTabs = {}; 53 | chrome.tabs.onRemoved.addListener(function(id) { 54 | for (var key in Sessions.activeTabs) { 55 | if (Sessions.activeTabs[key].hasOwnProperty(id)) { 56 | if (tabHistory[Sessions.activeTabs[key][id].windowId] === void 0) { 57 | tabHistory[Sessions.activeTabs[key][id].windowId] = []; 58 | } 59 | tabHistory[Sessions.activeTabs[key][id].windowId].push(Sessions.activeTabs[key][id]); 60 | delete Sessions.activeTabs[key][id]; 61 | break; 62 | } 63 | } 64 | }); 65 | chrome.tabs.onUpdated.addListener(function(tab) { 66 | try { 67 | chrome.tabs.get(tab, function(updatedTab) { 68 | if (Sessions.activeTabs[updatedTab.windowId] === void 0) { 69 | Sessions.activeTabs[updatedTab.windowId] = {}; 70 | } 71 | Sessions.activeTabs[updatedTab.windowId][updatedTab.id] = updatedTab; 72 | }); 73 | } catch (e) { } // Ignore tabs that have already been removed 74 | }); 75 | } 76 | })(); 77 | -------------------------------------------------------------------------------- /vendor/linkify-jquery.min.js: -------------------------------------------------------------------------------- 1 | "use strict";!function(e,t,n){var i=function(t,n){function i(e,t,n){var i=n[n.length-1];e.replaceChild(i,t);for(var r=n.length-2;r>=0;r--)e.insertBefore(n[r],i),i=n[r]}function r(e,t,n){for(var i=[],r=e,a=Array.isArray(r),o=0,r=a?r:r[Symbol.iterator]();;){var l;if(a){if(o>=r.length)break;l=r[o++]}else{if(o=r.next(),o.done)break;l=o.value}var f=l;if("nl"===f.type&&t.nl2br)i.push(n.createElement("br"));else if(f.isLink&&t.check(f)){var s=t.resolve(f),c=s.formatted,u=s.formattedHref,d=s.tagName,m=s.className,y=s.target,h=s.events,k=s.attributes,v=n.createElement(d);if(v.setAttribute("href",u),m&&v.setAttribute("class",m),y&&v.setAttribute("target",y),k)for(var g in k)v.setAttribute(g,k[g]);if(h)for(var b in h)v.addEventListener?v.addEventListener(b,h[b]):v.attachEvent&&v.attachEvent("on"+b,h[b]);v.appendChild(n.createTextNode(c)),i.push(v)}else i.push(n.createTextNode(f.toString()))}return i}function a(e,t,n){if(!e||e.nodeType!==d)throw new Error("Cannot linkify "+e+" - Invalid DOM Node type");var o=t.ignoreTags;if("A"===e.tagName||s.contains(o,e.tagName))return e;for(var l=e.firstChild;l;){switch(l.nodeType){case d:a(l,t,n);break;case m:var c=l.nodeValue,y=f(c);if(0===y.length||1===y.length&&y[0]instanceof u)break;var h=r(y,t,n);i(e,l,h),l=h[h.length-1]}l=l.nextSibling}return e}function o(t,n){var i=arguments.length>2&&void 0!==arguments[2]&&arguments[2];try{i=i||document||e&&e.document||global&&global.document}catch(r){}if(!i)throw new Error("Cannot find document implementation. If you are in a non-browser environment like Node.js, pass the document implementation as the third argument to linkifyElement.");return n=new c(n),a(t,n,i)}function l(t){function n(e){return e=o.normalize(e),this.each(function(){o.helper(this,e,i)})}var i=arguments.length>1&&void 0!==arguments[1]&&arguments[1];t.fn=t.fn||{};try{i=i||document||e&&e.document||global&&global.document}catch(r){}if(!i)throw new Error("Cannot find document implementation. If you are in a non-browser environment like Node.js, pass the document implementation as the second argument to linkify/jquery");"function"!=typeof t.fn.linkify&&(t.fn.linkify=n,t(i).ready(function(){t("[data-linkify]").each(function(){var e=t(this),n=e.data(),i=n.linkify,r=n.linkifyNlbr,a={attributes:n.linkifyAttributes,defaultProtocol:n.linkifyDefaultProtocol,events:n.linkifyEvents,format:n.linkifyFormat,formatHref:n.linkifyFormatHref,nl2br:!!r&&0!==r&&"false"!==r,tagName:n.linkifyTagname,target:n.linkifyTarget,className:n.linkifyClassName||n.linkifyLinkclass,validate:n.linkifyValidate,ignoreTags:n.linkifyIgnoreTags},o="this"===i?e:e.find(i);o.linkify(a)})}))}t="default"in t?t["default"]:t;var f=n.tokenize,s=n.options,c=s.Options,u=n.parser.TOKENS.TEXT,d=1,m=3;o.helper=a,o.normalize=function(e){return new c(e)};try{!define&&(e.linkifyElement=o)}catch(y){}return l}(n,t);"function"!=typeof n.fn.linkify&&i(n)}(window,linkify,jQuery); -------------------------------------------------------------------------------- /pages/options.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'DejaVu Sans', 'Helvetica Neue', 'Helvetica', 'Noto Sans UI', 'Segoe UI', 'Chrome Droid Sans', 'Droid Sans Fallback', 'Lucida Grande', 'Tahoma', sans-serif; 3 | -webkit-font-smoothing: antialiased; 4 | font-size: 12pt; 5 | margin: 0; 6 | padding: 0; 7 | overflow-x: auto; 8 | min-width: 800px; 9 | margin-bottom: 25pt; 10 | background-color: #eee; 11 | } 12 | 13 | * { box-sizing: inline-block; } 14 | 15 | #container_main {} 16 | 17 | #container_title { 18 | padding: 3px; 19 | line-height: 50px; 20 | display: block; 21 | vertical-align: middle; 22 | box-sizing: border-box; 23 | color: #222; 24 | font-size: 24pt; 25 | } 26 | 27 | td { 28 | padding: 0; 29 | margin: 0; 30 | position: relative; 31 | } 32 | 33 | tr { 34 | border: 1px solid #aaa; 35 | } 36 | 37 | .options-left { 38 | width: 20%; 39 | top: 0; 40 | padding: 5px; 41 | color: #222; 42 | background-color: #eee; 43 | border-right: 1px solid #aaa; 44 | text-align: center; 45 | } 46 | 47 | .options-right { 48 | width: 100%; 49 | } 50 | 51 | table { 52 | width: 100%; 53 | table-layout: fixed; 54 | border-collapse: collapse; 55 | border-spacing: 0 0; 56 | } 57 | 58 | #settings { 59 | display: block; 60 | } 61 | 62 | #edit_mode { 63 | font-size: 8pt; 64 | background-color: #eee; 65 | border: none; 66 | outline-style: none; 67 | } 68 | 69 | #cssSetting { 70 | position: absolute; 71 | } 72 | 73 | 74 | select { 75 | display: block; 76 | margin: 0 auto; 77 | } 78 | 79 | #mappings { 80 | width: 100%; 81 | max-width: 100%; 82 | height: 200px; 83 | resize: none; 84 | font-family: monospace; 85 | font-size: 10pt; 86 | } 87 | 88 | #title { 89 | display: inline-block; 90 | } 91 | 92 | #save_container { 93 | position: fixed; 94 | bottom: 0; 95 | left: 0; 96 | background-color: rgba(34,34,34,0.8); 97 | box-shadow: 0 -3px 3px rgba(0,0,0,0.5); 98 | width: 100%; 99 | box-sizing: border-box; 100 | text-align: left; 101 | padding: 5px; 102 | z-index: 999; 103 | } 104 | 105 | #save_button, #reset_button { 106 | font-size: 10pt; 107 | border: 1px solid #777; 108 | border-radius: 2px; 109 | height: 35px; 110 | } 111 | #clearHistory, #gistSync { 112 | border: 1px solid #777; 113 | border-radius: 2px; 114 | height: 25px; 115 | margin-left: 10px; 116 | } 117 | #save_button, #reset_button, #clearHistory, #gistSync { 118 | background-color: #bbb; 119 | } 120 | 121 | #save_button:hover, #reset_button:hover { 122 | opacity: 1; 123 | } 124 | 125 | a { 126 | color: darkcyan; 127 | } 128 | 129 | #gistSync { 130 | float: right; 131 | width: 55px; 132 | margin: 5px; 133 | } 134 | 135 | #gistUrl { 136 | height: 100%; 137 | top: 0; 138 | left: 0; 139 | padding-left: 3px; 140 | width: calc(100% - 65px); 141 | position: absolute; 142 | } 143 | 144 | textarea, input { 145 | opacity: 0.8; 146 | color: #000; 147 | border: none; 148 | transition: opacity 0.5s; 149 | box-sizing: border-box; 150 | } 151 | 152 | textarea:hover, input:hover, textarea:focus, input:focus { 153 | opacity: 1; 154 | } 155 | -------------------------------------------------------------------------------- /background_scripts/history.js: -------------------------------------------------------------------------------- 1 | var History = { 2 | 3 | historyTypes: ['action', 'url', 'search'], 4 | searchResults: null, 5 | historyStore: [], 6 | commandHistory: {}, 7 | shouldRefresh: false, 8 | 9 | saveCommandHistory: function() { 10 | Object.keys(this.commandHistory).forEach(function(e) { 11 | localStorage[e] = JSON.stringify(this.commandHistory[e]); 12 | }.bind(this)); 13 | }, 14 | 15 | clear: function() { 16 | this.commandHistory = {}; 17 | this.historyTypes.forEach(function(type) { 18 | this.commandHistory[type] = []; 19 | }.bind(this)); 20 | }, 21 | 22 | sendToTabs: function() { 23 | chrome.tabs.query({}, function(tabs) { 24 | tabs.forEach(function(tab) { 25 | chrome.tabs.sendMessage(tab.id, { 26 | action: 'commandHistory', 27 | history: History.commandHistory 28 | }); 29 | }); 30 | }); 31 | }, 32 | 33 | append: function(value, type) { 34 | if (~this.historyTypes.indexOf(type)) { 35 | this.commandHistory[type].push('' + value); 36 | this.commandHistory[type] = 37 | this.commandHistory[type].splice(-500); 38 | this.saveCommandHistory(); 39 | } 40 | }, 41 | 42 | retrieve: function(type) { 43 | return [type, this.commandHistory[type]]; 44 | }, 45 | 46 | refreshStore: (function() { 47 | var utime; 48 | var calculateWeight = function(item) { 49 | var weight = 1; 50 | var points = 0; 51 | var delta = utime - item.lastVisitTime; 52 | switch (true) { 53 | case delta < 345600000: // 0-4 days 54 | break; 55 | case delta < 1209600000: // 5-14 days 56 | weight = 0.7; break; 57 | case delta < 2678400000: // 15-31 days 58 | weight = 0.5; break; 59 | case delta < 7776000000: // 32-90 days 60 | weight = 0.3; break; 61 | default: weight = 0.1; 62 | } 63 | points += item.visitCount * 100 * weight; 64 | points += item.typedCount * 200 * weight; 65 | return points; 66 | }; 67 | return function() { 68 | utime = new Date().getTime(); 69 | this.shouldRefresh = false; 70 | chrome.history.search({ 71 | text: '', 72 | startTime: 0, 73 | maxResults: 2147483647, 74 | }, function(results) { 75 | History.historyStore = results.sort(function(a, b) { 76 | return calculateWeight(b) - calculateWeight(a); 77 | }); 78 | }); 79 | }; 80 | })(), 81 | 82 | retrieveSearchHistory: function(search, limit, callback) { 83 | if (History.shouldRefresh) { 84 | History.refreshStore(); 85 | } 86 | callback(searchArray({ 87 | array: this.historyStore, 88 | search: search, 89 | limit: limit, 90 | fn: function(item) { 91 | return item.title + ' ' + item.url; 92 | } 93 | }), true); 94 | } 95 | 96 | }; 97 | 98 | (function() { 99 | History.historyTypes.forEach(function(type) { 100 | var data = localStorage[type]; 101 | try { 102 | data = JSON.parse(data); 103 | } catch (e) { 104 | data = typeof data === 'string' ? data.split(',') : []; 105 | } 106 | History.commandHistory[type] = data; 107 | }); 108 | })(); 109 | 110 | History.refreshStore(); 111 | -------------------------------------------------------------------------------- /docs/contribute.adoc: -------------------------------------------------------------------------------- 1 | :uri-asciidoctor: http://asciidoctor.org 2 | :icons: font 3 | :source-highlighter: pygments 4 | :nofooter: 5 | link:index.html[Home] 6 | 7 | == Contribution guideline 8 | 9 | 10 | == Types of contributions 11 | 12 | - <> 13 | - <> 14 | - <> 15 | - <> 16 | - <> 17 | - <> 18 | 19 | 20 | == Enhancements 21 | 22 | . search existing tickets (labeled #enhancement) for similar ideas and comment there https://github.com/hbt/mouseless/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement 23 | . log a ticket in https://github.com/hbt/mouseless/issues 24 | . propose a command explaining how it would enhance your workflow 25 | . propose ways of building it or refer to existing implementation if already done in other software 26 | . fork it and build it for yourself 27 | . send pull request <> 28 | 29 | == Bug reports 30 | 31 | . search existing tickets (labeled as #bug) for similar bugs and comment there or upvote it https://github.com/hbt/mouseless/issues?q=is%3Aissue+is%3Aopen+label%3Abug 32 | . log a ticket in https://github.com/hbt/mouseless/issues 33 | . write steps to reproduce the bug with URLs if needed 34 | . write the result you *expected* and what happened instead 35 | . share your configuration if you believe it to be the source 36 | . propose a way of fixing the issue 37 | . send pull request <> 38 | :uri-asciidoctor: http://asciidoctor.org 39 | :icons: font 40 | :source-highlighter: pygments 41 | :nofooter: 42 | link:index.html[Home] 43 | 44 | == Ideas 45 | 46 | it is fine to log tickets (labeled #idea) to brainstorm ideas and suggest workflows. Take the time to clear it your head first. 47 | https://github.com/hbt/mouseless/issues?q=is%3Aissue+is%3Aopen+label%3Aidea 48 | 49 | Unlike enhancement requests, idea requests are vague and often lack a design proposal on how to implement it. 50 | 51 | == Code review 52 | 53 | . watch the repository https://github.com/hbt/mouseless 54 | . comment on pull request and what needs to be done to meet guideline <> 55 | . comment on implementation, potential bugs, performance issues, code quality etc. 56 | 57 | 58 | == Community support 59 | 60 | This is the most time consuming of all and any help is appreciated. 61 | 62 | . watch the repository https://github.com/hbt/mouseless 63 | . answer tickets label as #question to the best of your ability https://github.com/hbt/mouseless/issues?q=is%3Aissue+is%3Aopen+label%3Aquestion 64 | . don't let shitty people get to you ;-) They are a tiny minority but your brain will exaggerate their effect 65 | 66 | 67 | == Documentation 68 | 69 | Improving documentation by: 70 | 71 | - fixing typos 72 | - sharing tips like cool configuration tricks, mappings etc. link:tips.html[view tips] 73 | - doing other type of grunt work which is appreciated but time consuming https://github.com/hbt/mouseless/issues?q=is%3Aissue+is%3Aopen+label%3Agrunt_work 74 | 75 | == Pull request guideline 76 | 77 | . create branch and commit work referring to ticket 78 | . commits should refer to ticket number created in https://github.com/hbt/mouseless/issues 79 | . minimal changes in diffs 80 | . update docs/commands.md docs/settings.md if relevant 81 | . wait at least a week for a maintainer to review and merge your changes 82 | 83 | NOTE: Do not bother proposing a mapping. It will be ignored. Add the new function as an isolated module and it will be merged. 84 | 85 | -------------------------------------------------------------------------------- /content_scripts/hud.js: -------------------------------------------------------------------------------- 1 | var HUD = { 2 | visible: false, 3 | slideDuration: 40 4 | }; 5 | 6 | HUD.transitionEvent = function() { 7 | if (HUD.overflowValue) { 8 | document.body.style.overflowX = HUD.overflowValue; 9 | } 10 | delete HUD.overflowValue; 11 | HUD.element.removeEventListener('transitionend', HUD.transitionEvent, true); 12 | HUD.element.parentNode.removeChild(HUD.element); 13 | delete HUD.element; 14 | HUD.visible = false; 15 | HUD.transition = false; 16 | }; 17 | 18 | HUD.hide = function(ignoreSetting) { 19 | if (!ignoreSetting) { 20 | if (!settings.hud || this.element === void 0) { 21 | return false; 22 | } 23 | if (Find.matches.length) { 24 | return HUD.display(Find.index + 1 + ' / ' + Find.matches.length); 25 | } 26 | } 27 | if (!this.element) { 28 | return false; 29 | } 30 | HUD.transition = true; 31 | this.element.addEventListener('transitionend', this.transitionEvent, true); 32 | var width = this.element.offsetWidth; 33 | this.element.style.right = -width + 'px'; 34 | }; 35 | 36 | HUD.setMessage = function(text, duration) { 37 | window.clearTimeout(this.hideTimeout); 38 | if (!settings.hud || this.element === void 0) { 39 | return false; 40 | } 41 | this.element.firstElementChild.textContent = text; 42 | if (duration) { 43 | this.hideTimeout = window.setTimeout(function() { 44 | HUD.hide(); 45 | }, duration * 1000); 46 | } 47 | }; 48 | 49 | HUD.display = function(text, duration) { 50 | if (HUD.visible && HUD.transition) { 51 | this.element.removeEventListener('transitionend', this.transitionEvent, true); 52 | if (this.element.parentNode) { 53 | this.element.parentNode.removeChild(this.element); 54 | } 55 | delete this.element; 56 | } 57 | HUD.visible = true; 58 | if (!settings.hud || HUD.element !== void 0) { 59 | return HUD.setMessage(text, duration); 60 | } 61 | if (this.element) { 62 | this.element.removeEventListener('transitionend', this.transitionEvent, true); 63 | if (this.element.parentNode) { 64 | this.element.parentNode.removeChild(this.element); 65 | } 66 | delete this.element; 67 | } 68 | window.clearTimeout(this.hideTimeout); 69 | var span, pageWidth, screenWidth; 70 | if (!this.element) { 71 | this.element = document.createElement('div'); 72 | this.element.id = 'cVim-hud'; 73 | if (Command.onBottom) { 74 | this.element.style.bottom = 'initial'; 75 | this.element.style.top = '0'; 76 | } 77 | } 78 | this.element.innerHTML = ''; 79 | span = document.createElement('span'); 80 | span.textContent = text; 81 | this.element.appendChild(span); 82 | 83 | try { 84 | document.lastElementChild.appendChild(this.element); 85 | } catch (e) { 86 | if (document.body === void 0) { 87 | return false; 88 | } else { 89 | document.body.appendChild(this.element); 90 | } 91 | } 92 | 93 | this.element.style.right = -this.element.offsetWidth + 'px'; 94 | 95 | screenWidth = document.documentElement.clientWidth; 96 | pageWidth = document.body.scrollWidth; 97 | if (screenWidth === pageWidth) { 98 | this.overflowValue = getComputedStyle(document.body).overflowX; 99 | document.body.style.overflowX = 'hidden'; 100 | } 101 | 102 | this.element.style.right = '0'; 103 | 104 | if (duration) { 105 | this.hideTimeout = window.setTimeout(function() { 106 | HUD.hide(); 107 | }, duration * 1000); 108 | } 109 | 110 | }; 111 | -------------------------------------------------------------------------------- /background_scripts/user.css: -------------------------------------------------------------------------------- 1 | #cVim-command-bar, #cVim-command-bar-mode, #cVim-command-bar-input, #cVim-command-bar-search-results, 2 | .cVim-completion-item, .cVim-completion-item .cVim-full, .cVim-completion-item .cVim-left, 3 | .cVim-completion-item .cVim-right { 4 | font-family: Helvetica, Helvetica Neue, Neue, sans-serif, monospace, Arial; 5 | font-size: 10pt !important; 6 | -webkit-font-smoothing: antialiased !important; 7 | } 8 | 9 | #cVim-command-bar { 10 | position: fixed; 11 | z-index: 2147483646; 12 | background-color: #1b1d1e; 13 | color: #bbb; 14 | display: none; 15 | box-sizing: content-box; 16 | box-shadow: 0 3px 3px rgba(0,0,0,0.4); 17 | left: 0; 18 | width: 100%; 19 | height: 20px; 20 | } 21 | 22 | #cVim-command-bar-mode { 23 | display: inline-block; 24 | vertical-align: middle; 25 | box-sizing: border-box; 26 | padding-left: 2px; 27 | height: 100%; 28 | width: 10px; 29 | padding-top: 2px; 30 | color: #888; 31 | } 32 | 33 | #cVim-command-bar-input { 34 | background-color: #1b1d1e; 35 | color: #bbb; 36 | height: 100%; 37 | right: 0; 38 | top: 0; 39 | width: calc(100% - 10px); 40 | position: absolute; 41 | } 42 | 43 | #cVim-command-bar-search-results { 44 | position: fixed; 45 | width: 100%; 46 | overflow: hidden; 47 | z-index: 2147483647; 48 | left: 0; 49 | box-shadow: 0 3px 3px rgba(0,0,0,0.4); 50 | background-color: #1c1c1c; 51 | } 52 | 53 | .cVim-completion-item, .cVim-completion-item .cVim-full, .cVim-completion-item .cVim-left, .cVim-completion-item .cVim-right { 54 | text-overflow: ellipsis; 55 | padding: 1px; 56 | display: inline-block; 57 | box-sizing: border-box; 58 | vertical-align: middle; 59 | overflow: hidden; 60 | white-space: nowrap; 61 | } 62 | 63 | .cVim-completion-item:nth-child(even) { 64 | background-color: #1f1f1f; 65 | } 66 | 67 | .cVim-completion-item { 68 | width: 100%; left: 0; 69 | color: #bcbcbc; 70 | } 71 | 72 | .cVim-completion-item[active] { 73 | width: 100%; left: 0; 74 | color: #1b1d1e; 75 | background-color: #f1f1f1; 76 | } 77 | 78 | .cVim-completion-item[active] span { 79 | color: #1b1d1e; 80 | } 81 | 82 | .cVim-completion-item .cVim-left { 83 | color: #fff; 84 | width: 37%; 85 | } 86 | 87 | .cVim-completion-item .cVim-right { 88 | font-style: italic; 89 | color: #888; 90 | width: 57%; 91 | } 92 | 93 | 94 | #cVim-link-container, .cVim-link-hint, 95 | #cVim-hud, #cVim-status-bar { 96 | font-family: Helvetica, Helvetica Neue, Neue, sans-serif, monospace, Arial; 97 | font-size: 10pt !important; 98 | -webkit-font-smoothing: antialiased !important; 99 | } 100 | 101 | #cVim-link-container { 102 | /*position: absolute;*/ 103 | pointer-events: none; 104 | width: 100%; left: 0; 105 | height: 100%; top: 0; 106 | z-index: 2147483647; 107 | } 108 | 109 | .cVim-link-hint { 110 | position: absolute; 111 | color: #302505 !important; 112 | background-color: #ffd76e !important; 113 | border-radius: 2px !important; 114 | padding: 2px !important; 115 | font-size: 8pt !important; 116 | font-weight: 500 !important; 117 | text-transform: uppercase !important; 118 | border: 1px solid #ad810c; 119 | display: inline-block !important; 120 | vertical-align: middle !important; 121 | text-align: center !important; 122 | box-shadow: 2px 2px 1px rgba(0,0,0,0.25) !important; 123 | } 124 | 125 | .cVim-link-hint_match { 126 | color: #777; 127 | text-transform: uppercase !important; 128 | } 129 | 130 | 131 | #cVim-hud { 132 | background-color: rgba(28,28,28,0.9); 133 | position: fixed !important; 134 | transition: right 0.2s ease-out; 135 | z-index: 24724289; 136 | } 137 | 138 | #cVim-hud span { 139 | padding: 2px; 140 | padding-left: 4px; 141 | padding-right: 4px; 142 | color: #8f8f8f; 143 | font-size: 10pt; 144 | } 145 | 146 | #cVim-frames-outline { 147 | position: fixed; 148 | width: 100%; 149 | height: 100%; 150 | left: 0; 151 | top: 0; 152 | right: 0; 153 | z-index: 9999999999; 154 | box-sizing: border-box; 155 | border: 3px solid yellow; 156 | } 157 | -------------------------------------------------------------------------------- /background_scripts/popup.js: -------------------------------------------------------------------------------- 1 | var Popup = { 2 | active: true 3 | }; 4 | 5 | Popup.getBlacklisted = function(callback) { 6 | if (typeof callback === 'object') 7 | callback = callback.callback; 8 | var blacklists = Utils.compressArray(settings.blacklists); 9 | this.getActiveTab(function(tab) { 10 | var url = tab.url; 11 | for (var i = 0, l = blacklists.length; i < l; ++i) { 12 | if (matchLocation(url, blacklists[i])) { 13 | callback(true); 14 | } 15 | } 16 | }); 17 | }; 18 | 19 | Popup.getActiveTab = function(callback) { 20 | chrome.tabs.query({active: true, currentWindow: true}, function(tab) { 21 | callback(tab[0]); 22 | }); 23 | }; 24 | 25 | Popup.setIconDisabled = function() { 26 | this.getActiveTab(function(tab) { 27 | chrome.browserAction.setIcon({ 28 | path: 'icons/disabled.png', 29 | tabId: tab.id 30 | }, function() { 31 | return chrome.runtime.lastError; 32 | }); 33 | }); 34 | }; 35 | 36 | Popup.setIconEnabled = function(obj) { 37 | if (obj.sender) { 38 | return chrome.browserAction.setIcon({ 39 | path: 'icons/38.png', 40 | tabId: obj.sender.tab.id 41 | }, function() { 42 | return chrome.runtime.lastError; 43 | }); 44 | } 45 | this.getActiveTab(function(tab) { 46 | chrome.browserAction.setIcon({ 47 | path: 'icons/38.png', 48 | tabId: tab.id 49 | }, function() { 50 | return chrome.runtime.lastError; 51 | }); 52 | }); 53 | }; 54 | 55 | Popup.getActiveState = function(obj) { 56 | obj.callback(this.active); 57 | }; 58 | 59 | Popup.toggleEnabled = function(obj) { 60 | var request = obj.request; 61 | if (request && request.singleTab) { 62 | this.getActiveTab(function(tab) { 63 | chrome.tabs.sendMessage(tab.id, {action: 'toggleEnabled'}); 64 | }); 65 | if (request.blacklisted) { 66 | return this.setIconDisabled({}); 67 | } 68 | return this.setIconEnabled({}); 69 | } 70 | chrome.tabs.query({}, function(tabs) { 71 | this.active = !this.active; 72 | if (!request || (request && !request.blacklisted)) { 73 | tabs.map(function(tab) { return tab.id; }).forEach(function(id) { 74 | chrome.tabs.sendMessage(id, {action: 'toggleEnabled'}); 75 | if (this.active) { 76 | chrome.browserAction.setIcon({path: 'icons/38.png', tabId: id}); 77 | } else { 78 | chrome.browserAction.setIcon({path: 'icons/disabled.png', tabId: id}); 79 | } 80 | }.bind(this)); 81 | } 82 | }.bind(this)); 83 | }; 84 | 85 | Popup.toggleBlacklisted = function() { 86 | var blacklists = Utils.compressArray(settings.blacklists); 87 | this.getActiveTab(function(tab) { 88 | var url = tab.url; 89 | var foundMatch = false; 90 | for (var i = 0, l = blacklists.length; i < l; ++i) { 91 | if (matchLocation(url, blacklists[i])) { 92 | blacklists.splice(i, 1); 93 | foundMatch = true; 94 | } 95 | } 96 | if (!foundMatch) { 97 | url = new URL(url); 98 | blacklists.push(url.protocol + '//' + url.hostname + '/*'); 99 | } 100 | settings.blacklists = blacklists; 101 | Options.saveSettings({settings: settings}); 102 | Options.updateBlacklistsMappings(); 103 | }); 104 | }; 105 | 106 | chrome.runtime.onConnect.addListener(function(port) { 107 | if (port.name === 'popup') { 108 | port.onMessage.addListener(function(request) { 109 | if (Popup.hasOwnProperty(request.action)) { 110 | Popup[request.action]({ 111 | callback: function(response) { 112 | port.postMessage(response); 113 | }, 114 | request: request, 115 | sender: request.sender 116 | }); 117 | } 118 | }); 119 | } 120 | }); 121 | 122 | chrome.runtime.onMessage.addListener(function(request, sender, callback) { 123 | if (Popup.hasOwnProperty(request.action)) { 124 | if (!sender.tab) 125 | return; 126 | Popup[request.action]({ 127 | callback: function(response) { 128 | callback(response); 129 | }, 130 | request: request, 131 | sender: sender 132 | }); 133 | } 134 | }); 135 | -------------------------------------------------------------------------------- /pages/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | cVim Options 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 | cVim Options – 38 | Help – 39 | Changelog – 40 | Donate 41 | 42 |
43 |
44 | 45 | 46 | 47 | 51 | 52 | 53 | 56 | 59 | 60 | 61 | 68 | 71 | 72 | 73 | 74 | 77 | 78 |
Sync cVimrc with GitHub Gist 48 | 49 | 50 |
54 | cVimrc ? 55 | 57 | 58 |
CSS 62 | 67 | 69 | 70 |
Clear the command bar history 75 | 76 |
79 |
80 |
81 | 82 | 83 |
84 |
85 | 86 | 87 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "cVim", 4 | "version": "1.4.7", 5 | "description": "An extension adding Vim-like bindings to Google Chrome", 6 | "update_url": "https://clients2.google.com/service/update2/crx", 7 | "icons": { 8 | "128": "icons/128.png", 9 | "48": "icons/48.png", 10 | "16": "icons/16.png" 11 | }, 12 | "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self';", 13 | "commands": { 14 | "nextTab": { 15 | "description": "Go to the next tab" 16 | }, 17 | "previousTab": { 18 | "description": "Go to the previous tab" 19 | }, 20 | "togglecVim": { 21 | "description": "Toggle the enabled state of cVim" 22 | }, 23 | "toggleBlacklisted": { 24 | "description": "Toggle the blacklist for the domain in the current tab" 25 | }, 26 | "nextCompletionResult": { 27 | "description": "Let Chrome use for cncpcompletion setting (see Help file)" 28 | }, 29 | "deleteBackWord": { 30 | "description": "Let Chrome use for the deleteBackWord insert mapping" 31 | }, 32 | "closeTab": { 33 | "description": "Close the current tab" 34 | }, 35 | "reloadTab": { 36 | "description": "Reload the current tab" 37 | }, 38 | "newTab": { 39 | "description": "Open a new tab to a blank page" 40 | }, 41 | "restartcVim": { 42 | "description": "Restart the background scripts (pages will need a refresh)" 43 | }, 44 | "viewSource": { 45 | "description": "View the page source" 46 | } 47 | }, 48 | "browser_action": { 49 | "default_icon": { 50 | "19": "icons/disabled.png", 51 | "38": "icons/disabled-38.png" 52 | }, 53 | "default_title": "cVim", 54 | "default_popup": "pages/popup.html" 55 | }, 56 | "author": "Jake Eaton", 57 | "permissions": [ 58 | "", 59 | "tabs", 60 | "management", 61 | "history", 62 | "bookmarks", 63 | "storage", 64 | "sessions", 65 | "downloads", 66 | "downloads.open", 67 | "downloads.shelf", 68 | "topSites", 69 | "clipboardRead", 70 | "clipboardWrite", 71 | "webNavigation" 72 | ], 73 | "chrome_url_overrides": { 74 | "newtab": "newtab.html" 75 | }, 76 | "background": { 77 | "persistant": false, 78 | "scripts": [ 79 | "vendor/jquery.js", 80 | "vendor/underscore-min.js", 81 | "content_scripts/utils.js", 82 | "content_scripts/cvimrc_parser.js", 83 | "background_scripts/clipboard.js", 84 | "background_scripts/bookmarks.js", 85 | "background_scripts/sites.js", 86 | "background_scripts/files.js", 87 | "background_scripts/links.js", 88 | "background_scripts/history.js", 89 | "background_scripts/actions.js", 90 | "background_scripts/main.js", 91 | "background_scripts/options.js", 92 | "background_scripts/sessions.js", 93 | "background_scripts/popup.js", 94 | "background_scripts/update.js", 95 | "background_scripts/tab_creation_order.js", 96 | "background_scripts/extension-reloader.js", 97 | "background_scripts/extension-enabler.js", 98 | "background_scripts/frames.js" 99 | ] 100 | }, 101 | "content_scripts": [ 102 | { 103 | "matches": [""], 104 | "js": [ 105 | "vendor/jquery.js", 106 | "vendor/underscore-min.js", 107 | "vendor/linkify.min.js", 108 | "vendor/linkify-jquery.min.js", 109 | "vendor/linkify-element.min.js", 110 | "content_scripts/cvimrc_parser.js", 111 | "content_scripts/session.js", 112 | "content_scripts/utils.js", 113 | "content_scripts/dom.js", 114 | "content_scripts/hints.js", 115 | "content_scripts/bookmarks.js", 116 | "content_scripts/command.js", 117 | "content_scripts/keys.js", 118 | "content_scripts/clipboard.js", 119 | "content_scripts/complete.js", 120 | "content_scripts/mappings.js", 121 | "content_scripts/find.js", 122 | "content_scripts/cursor.js", 123 | "content_scripts/status.js", 124 | "content_scripts/hud.js", 125 | "content_scripts/visual.js", 126 | "content_scripts/scroll.js", 127 | "content_scripts/search.js", 128 | "content_scripts/frames.js", 129 | "content_scripts/messenger.js" 130 | ], 131 | "css": ["content_scripts/main.css"], 132 | "run_at": "document_start", 133 | "all_frames": true 134 | } 135 | ], 136 | "options_page": "pages/options.html", 137 | "web_accessible_resources": ["cmdline_frame.html"] 138 | } 139 | -------------------------------------------------------------------------------- /content_scripts/bookmarks.js: -------------------------------------------------------------------------------- 1 | Marks = (function() { 2 | 3 | var bookmarks = [], 4 | quickMarks = {}, 5 | files = []; 6 | 7 | var _ = {}; 8 | 9 | _.filePath = function(_files) { 10 | files = _files || files; 11 | var input = Command.input.value.replace(/.*\//, ''); 12 | Command.completions = { files: [] }; 13 | var i, c; 14 | if (!files) { 15 | return; 16 | } 17 | for (i = 0, c = 0; i < files.length; ++i) { 18 | if (files[i][0] && files[i][0].indexOf(input) === 0) { 19 | if (!input && files[i][0] !== '..' && files[i][0][0] === '.') { 20 | continue; 21 | } 22 | Command.completions.files.push([files[i][0], files[i][1]]); 23 | if (++c > settings.searchlimit) { 24 | break; 25 | } 26 | } 27 | } 28 | if (c <= settings.searchlimit && !input) { 29 | for (i = 0; i < files.length; ++i) { 30 | if (files[i] !== '..' && files[i][0] === '.') { 31 | Command.completions.files.push([files[i][0], !files[i][1]]); 32 | if (++c > settings.searchlimit) { 33 | break; 34 | } 35 | } 36 | } 37 | } 38 | Command.updateCompletions(); 39 | }; 40 | 41 | _.addQuickMark = function(ch) { 42 | if (quickMarks[ch] === void 0) { 43 | Status.setMessage('New QuickMark "' + ch + '" added', 1); 44 | quickMarks[ch] = [document.URL]; 45 | } else if (quickMarks[ch].indexOf(document.URL) === -1) { 46 | Status.setMessage('Current URL added to QuickMark "' + ch + '"', 1); 47 | quickMarks[ch].push(document.URL); 48 | } else { 49 | quickMarks[ch].splice(quickMarks[ch].indexOf(document.URL)); 50 | if (quickMarks[ch].length === 0) { 51 | Status.setMessage('Quickmark "' + ch + '" removed', 1); 52 | delete quickMarks[ch]; 53 | } else { 54 | Status.setMessage('Current URL removed from existing QuickMark "' + ch + '"', 1); 55 | } 56 | } 57 | RUNTIME('updateMarks', {marks: quickMarks}); 58 | }; 59 | 60 | _.openQuickMark = function(ch, opts, repeats) { 61 | if (!quickMarks.hasOwnProperty(ch)) { 62 | return Status.setMessage('mark not set', 1, 'error'); 63 | } 64 | if (repeats !== 1 || (!opts.tab.tabbed && !opts.tab.newWindow)) { 65 | if (quickMarks[ch][repeats - 1]) { 66 | opts.url = quickMarks[ch][repeats - 1]; 67 | RUNTIME('openLink', opts); 68 | } else { 69 | opts.url = quickMarks[ch][0]; 70 | RUNTIME('openLink', opts); 71 | } 72 | } else if (opts.tab.tabbed) { 73 | for (var i = 0, l = quickMarks[ch].length; i < l; ++i) { 74 | opts.url = quickMarks[ch][i]; 75 | RUNTIME('openLink', opts); 76 | } 77 | } else if (opts.tab.newWindow) { 78 | RUNTIME('openLinksWindow', { 79 | urls: quickMarks[ch] 80 | }); 81 | } 82 | }; 83 | 84 | _.parseQuickMarks = function(marks) { 85 | quickMarks = {}; 86 | for (var key in marks) { 87 | if (Array.isArray(marks[key])) { 88 | quickMarks[key] = marks[key]; 89 | } else if (typeof marks[key] === 'string') { 90 | quickMarks[key] = [marks[key]]; 91 | } 92 | } 93 | }; 94 | 95 | _.parse = function(marks) { 96 | bookmarks = []; 97 | (function recurse(marks) { 98 | marks.forEach(function(bookmark) { 99 | if (bookmark.url) { 100 | bookmarks.push([bookmark.title, bookmark.url]); 101 | } 102 | if (bookmark.children) { 103 | recurse(bookmark.children); 104 | } 105 | }); 106 | })(marks); 107 | }; 108 | 109 | _.match = function(string, callback, limit) { 110 | if (string.trim() === '') { 111 | callback(bookmarks.slice(0, settings.searchlimit + 1)); 112 | return; 113 | } 114 | callback(searchArray({ 115 | array: bookmarks, 116 | search: string, 117 | limit: limit, 118 | fn: function(item) { return item.join(' '); } 119 | })); 120 | }; 121 | 122 | var lastFileSearch, lastSearchLength; 123 | _.parseFileCommand = function(search) { 124 | if ((search.slice(-1) === '/' && lastSearchLength < search.length) || lastSearchLength > search.length || !(lastFileSearch && lastFileSearch.replace(/[^\/]+$/, '') === search) && (search.slice(-1) === '/' && !(lastFileSearch && lastFileSearch.slice(-1) === '/'))) { 125 | lastFileSearch = search; 126 | lastSearchLength = search.length; 127 | if (settings.homedirectory) { 128 | search = search.replace('~', settings.homedirectory); 129 | } 130 | RUNTIME('getFilePath', { path: search }, function(data) { 131 | Marks.filePath(data); 132 | }); 133 | } else { 134 | lastFileSearch = search; 135 | _.filePath(); 136 | } 137 | }; 138 | 139 | _.matchPath = function(path) { PORT('getBookmarkPath', {path: path}); }; 140 | 141 | return _; 142 | 143 | })(); 144 | -------------------------------------------------------------------------------- /content_scripts/search.js: -------------------------------------------------------------------------------- 1 | var Search = {}; 2 | 3 | Search.index = null; 4 | Search.topSites = []; 5 | 6 | Search.chromeUrls = ['about', 'accessibility', 'appcache-internals', 'apps', 'blob-internals', 'bluetooth-internals', 'bookmarks', 'cache', 'chrome', 'chrome-urls', 'components', 'crashes', 'credits', 'device-log', 'devices', 'dino', 'dns', 'downloads', 'extensions', 'flags', 'flash', 'gcm-internals', 'gpu', 'help', 'histograms', 'history', 'indexeddb-internals', 'inspect', 'invalidations', 'linux-proxy-config', 'local-state', 'media-engagement', 'media-internals', 'net-export', 'net-internals', 'network-error', 'network-errors', 'newtab', 'ntp-tiles-internals', 'omnibox', 'password-manager-internals', 'policy', 'predictors', 'print', 'profiler', 'quota-internals', 'safe-browsing', 'sandbox', 'serviceworker-internals', 'settings', 'signin-internals', 'site-engagement', 'suggestions', 'supervised-user-internals', 'sync-internals', 'system', 'taskscheduler-internals', 'terms', 'thumbnails', 'tracing', 'translate-internals', 'usb-internals', 'user-actions', 'version', 'view-http-cache', 'webrtc-internals', 'webrtc-logs']; 7 | 8 | Search.chromeMatch = function(string, callback) { 9 | callback(searchArray({ 10 | array: this.chromeUrls, 11 | search: string, 12 | limit: settings.searchlimit 13 | })); 14 | }; 15 | 16 | Search.settingsMatch = function(string, callback) { 17 | callback(searchArray({ 18 | array: this.settings, 19 | search: string.replace(/^no/, ''), 20 | limit: settings.searchlimit 21 | })); 22 | }; 23 | 24 | Search.nextResult = function(reverse) { 25 | if (!Command.dataElements.length) { 26 | if (Command.input.value.length) { 27 | return false; 28 | } 29 | return Command.complete(''); 30 | } 31 | 32 | if (this.index === null) { 33 | if (!reverse) { 34 | this.index = 0; 35 | } else { 36 | this.index = Command.dataElements.length - 1; 37 | } 38 | } else { 39 | Command.dataElements[this.index].removeAttribute('active'); 40 | if (!reverse) { 41 | if (this.index + 1 < Command.dataElements.length) { 42 | this.index++; 43 | } else { 44 | this.index = null; 45 | Command.input.value = Command.typed || ''; 46 | return; 47 | } 48 | } else { 49 | if (this.index === 0) { 50 | this.index = null; 51 | Command.input.value = Command.typed || ''; 52 | return; 53 | } else { 54 | this.index--; 55 | } 56 | } 57 | } 58 | 59 | Command.dataElements[this.index].setAttribute('active', ''); 60 | 61 | switch (Command.completionResults[this.index][0]) { 62 | case 'chrome': 63 | Command.input.value = Command.input.value.match(/^\S+ /)[0] + 64 | Command.completionResults[this.index][1]; 65 | break; 66 | case 'bookmarks': 67 | case 'history': 68 | case 'topsites': 69 | Command.input.value = Command.input.value.match(/^\S+ /)[0] + Command.completionResults[this.index][2]; 70 | break; 71 | case 'tabhistory': 72 | Command.input.value = 'tabhistory ' + Command.completionResults[this.index][1]; 73 | break; 74 | case 'engines': 75 | Command.input.value = Command.input.value.match(/^\S+ /)[0] + Command.completionResults[this.index][1]; 76 | break; 77 | case 'search': 78 | var value = Utils.split(Command.input.value, /\s+/); 79 | var repl = ''; 80 | if (Command.customCommands.hasOwnProperty(value[0])) { 81 | value = [value[0]]; 82 | repl = Utils.split(Command.customCommands[value[0]], /\s+/).slice(2).join(' '); 83 | } 84 | var inputValue, searchValue; 85 | if (Command.completionResults[this.index].length === 3) { 86 | inputValue = value[0] + ' '; 87 | searchValue = Command.completionResults[this.index][2]; 88 | } else { 89 | inputValue = value.slice(0, 2).join(' ') + ' '; 90 | searchValue = Command.completionResults[this.index][1]; 91 | } 92 | if (searchValue.indexOf(repl) === 0) 93 | searchValue = Utils.trim(searchValue.replace(repl, '')); 94 | Command.input.value = inputValue + searchValue; 95 | break; 96 | case 'windows': 97 | Command.input.value = Command.input.value.match(/^\S+/)[0] + ' ' + Command.completionResults[this.index][1].replace(/ .*/, ''); 98 | break; 99 | case 'chromesessions': 100 | Command.input.value = Command.input.value.match(/^\S+/)[0] + ' ' + Command.completionResults[this.index][3].replace(/ .*/, ''); 101 | break; 102 | case 'markOptions': 103 | Command.input.value = Command.input.value.replace(/-[a-zA-Z]*$/, Command.completionResults[this.index][1]); 104 | break; 105 | case 'sessions': 106 | Command.input.value = Command.input.value.match(/^\S+/)[0] + ' ' + Command.completionResults[this.index][1]; 107 | break; 108 | case 'files': 109 | Command.input.value = Command.input.value.replace(/[^\/]+$/, '') + Command.completionResults[this.index][1]; 110 | break; 111 | case 'settings': 112 | var command = Command.input.value.split(/\s+/); 113 | Command.input.value = command[0] + ' ' + (/^no/.test(command[1]) ? 'no' : '') + Command.completionResults[this.index][1]; 114 | break; 115 | case 'paths': 116 | if (Command.completionResults[this.index][2] !== 'folder') { 117 | Command.input.value = 'bookmarks ' + Command.completionResults[this.index][2]; 118 | } else { 119 | Command.input.value = 'bookmarks ' + Command.completionResults[this.index][3] + Command.completionResults[this.index][1]; 120 | } 121 | break; 122 | case 'buffers': 123 | Command.input.value = Command.input.value.match(/^\S+/)[0] + ' ' + Command.completionResults[this.index][1].replace(/:.*/, ''); 124 | break; 125 | case 'complete': 126 | if (Command.completionResults[this.index][1] !== void 0) { 127 | Command.input.value = Command.completionResults[this.index][1]; 128 | } 129 | break; 130 | } 131 | 132 | }; 133 | -------------------------------------------------------------------------------- /content_scripts/dom.js: -------------------------------------------------------------------------------- 1 | window.DOM = { 2 | 3 | isSubmittable: function(element) { 4 | if (!element) { 5 | return false; 6 | } 7 | if (element.localName !== 'input') 8 | return false; 9 | if (element.hasAttribute('submit')) 10 | return true; 11 | while (element = element.parentElement) { 12 | if (element.localName === 'form') 13 | return true; 14 | } 15 | return false; 16 | }, 17 | 18 | isEditable: function(element) { 19 | if (!element) { 20 | return false; 21 | } 22 | if (element.localName === 'textarea' || 23 | element.localName === 'select' || 24 | element.hasAttribute('contenteditable')) 25 | return true; 26 | if (element.localName !== 'input') 27 | return false; 28 | var type = element.getAttribute('type'); 29 | switch (type) { 30 | case 'button': 31 | case 'checkbox': 32 | case 'color': 33 | case 'file': 34 | case 'hidden': 35 | case 'image': 36 | case 'radio': 37 | case 'reset': 38 | case 'submit': 39 | case 'week': 40 | return false; 41 | } 42 | return true; 43 | }, 44 | 45 | isTextElement: function(element) { 46 | if (!element) { 47 | return false; 48 | } 49 | if (element.localName === 'input' || element.localName === 'textarea') { 50 | return true; 51 | } 52 | while (element) { 53 | if (element.isContentEditable) { 54 | return true; 55 | } 56 | element = element.parentElement; 57 | } 58 | return false; 59 | }, 60 | 61 | onTitleChange: function(callback) { 62 | waitForLoad(function() { 63 | var title = (document.getElementsByTagName('title') || [])[0]; 64 | if (!title) { 65 | return; 66 | } 67 | new MutationObserver(function() { 68 | callback(title.textContent); 69 | }).observe(title, { 70 | childList: true 71 | }); 72 | }); 73 | }, 74 | 75 | /** 76 | * Retrieves the proper boundingRect of an element if it is visible on-screen. 77 | * @return boundingRect or null (if element is not visible on-screen) 78 | */ 79 | getVisibleBoundingRect: function(node) { 80 | var style = getComputedStyle(node, null); 81 | if (style.visibility !== 'visible' || 82 | style.display === 'none') { 83 | return null; 84 | } 85 | 86 | var rects = node.getClientRects(); 87 | if (rects.length === 0) 88 | return null; 89 | 90 | var result = null; 91 | 92 | outer: 93 | for (var i = 0; i < rects.length; i++) { 94 | var r = rects[i]; 95 | 96 | if (r.height <= 1 || r.width <= 1) { 97 | var children = node.children; 98 | for (var j = 0; j < children.length; j++) { 99 | var child = children[j]; 100 | var childRect = this.getVisibleBoundingRect(child); 101 | if (childRect !== null) { 102 | result = childRect; 103 | break outer; 104 | } 105 | } 106 | } else { 107 | if (r.left + r.width < 5 || r.top + r.height < 5) 108 | continue; 109 | if (innerWidth - r.left < 5 || innerHeight - r.top < 5) 110 | continue; 111 | 112 | result = r; 113 | break; 114 | } 115 | } 116 | if (result !== null) { 117 | result = this.cloneRect(result); 118 | result.left = Math.max(0, result.left); 119 | result.top = Math.max(0, result.top); 120 | result.right = Math.min(result.right, innerWidth); 121 | result.bottom = Math.min(result.bottom, innerHeight); 122 | } 123 | 124 | return result; 125 | }, 126 | 127 | // makes bounding rect writeable 128 | cloneRect: function(rect) { 129 | return { 130 | left: rect.left, 131 | right: rect.right, 132 | top: rect.top, 133 | bottom: rect.bottom, 134 | width: rect.width, 135 | height: rect.height, 136 | }; 137 | }, 138 | 139 | getVisibleBoundingAreaRect: function(node) { 140 | var map = node.parentElement; 141 | if (!map || map.localName.toLowerCase() !== 'map') 142 | return null; 143 | var mapName = map.getAttribute('name'); 144 | if (!mapName) 145 | return null; 146 | var mapImg = document.querySelector('*[usemap="#' + mapName + '"]'); 147 | if (!mapImg) 148 | return null; 149 | var mapImgRect = DOM.getVisibleBoundingRect(mapImg); 150 | if (mapImgRect === null) 151 | return null; 152 | var coords = node.coords.split(',').map(function(coord) { 153 | return parseInt(coord, 10); 154 | }); 155 | return { 156 | left: mapImgRect.left + coords[0], 157 | right: mapImgRect.left + coords[2], 158 | top: mapImgRect.top + coords[1], 159 | bottom: mapImgRect.top + coords[3], 160 | width: this.right - this.left, 161 | height: this.bottom - this.top, 162 | }; 163 | }, 164 | 165 | /** 166 | * Checks if an element is visible (not necessarily on-screen) 167 | */ 168 | isVisible: function(element) { 169 | if (!(element instanceof Element)) 170 | return false; 171 | return element.offsetParent && 172 | !element.disabled && 173 | element.getAttribute('type') !== 'hidden' && 174 | getComputedStyle(element).visibility !== 'hidden' && 175 | element.getAttribute('display') !== 'none'; 176 | }, 177 | 178 | mouseEvent: function(type, element) { 179 | var events; 180 | switch (type) { 181 | case 'hover': events = ['mouseover', 'mouseenter']; break; 182 | case 'unhover': events = ['mouseout', 'mouseleave']; break; 183 | case 'click': events = ['mouseover', 'mousedown', 'mouseup', 'click']; break; 184 | } 185 | events.forEach(function(eventName) { 186 | var event = document.createEvent('MouseEvents'); 187 | event.initMouseEvent(eventName, true, true, window, 1, 0, 0, 0, 0, false, 188 | false, false, false, 0, null); 189 | element.dispatchEvent(event); 190 | }); 191 | } 192 | 193 | }; 194 | -------------------------------------------------------------------------------- /content_scripts/main.css: -------------------------------------------------------------------------------- 1 | #cVim-command-frame { 2 | display: none; 3 | border: 0; 4 | position: fixed; 5 | background-color: transparent !important; 6 | margin: 0; 7 | padding: 0; 8 | left: 0; 9 | width: 100%; 10 | top: 0; 11 | height: 100%; 12 | z-index: 2147483647 !important; 13 | } 14 | 15 | #cVim-link-container { 16 | transition: opacity 0.1s ease; 17 | opacity: 0; 18 | } 19 | 20 | .cVim-hint-scale { 21 | -webkit-animation: scale 0.25s; 22 | } 23 | 24 | @keyframes scale { 25 | 0% { transform: scale(0) perspective(1px); } 26 | 60% { transform: scale(1.15) perspective(1px); } 27 | 100% { transform: scale(1) perspective(0px); } 28 | } 29 | 30 | .cVim-link-hint_match { 31 | opacity: 0.6; 32 | } 33 | 34 | .cVim-link-hint_match_hidden { 35 | display: none; 36 | } 37 | 38 | .cVim-link-hint { 39 | line-height: 1; 40 | } 41 | 42 | #cVim-command-bar { 43 | } 44 | 45 | #cVim-command-bar-mode { 46 | } 47 | #cVim-command-bar-input { 48 | box-sizing: border-box; 49 | outline-style: none; 50 | border: 0; 51 | } 52 | 53 | #cVim-command-bar-search-results { 54 | } 55 | 56 | .cVim-completion-item { 57 | } 58 | 59 | .cVim-completion-item .cVim-full { 60 | } 61 | 62 | .cVim-completion-item .cVim-left { 63 | } 64 | 65 | .cVim-completion-item .cVim-right { 66 | } 67 | 68 | .cVim-highlight { 69 | min-height: 0 !important;background-repeat: none !important;background-position: none !important;border-radius: 0 !important;outline: 0 !important;border: 0 !important;text-shadow: none !important;outline: 0 !important;outline-style: none !important; 70 | background-color: yellow; 71 | padding: 0 !important; 72 | margin: 0 !important; 73 | } 74 | 75 | #cVim-hud { 76 | min-height: 0 !important;background-repeat: none !important;background-position: none !important;border-radius: 0 !important;box-sizing: border-box !important;vertical-align: none !important;box-shadow: none !important;margin: 0 !important;-webkit-appearance: none !important;letter-spacing: normal !important;word-spacing: normal !important;text-transform: none !important;text-indent: 0px !important;text-shadow: none !important;text-align: start !important;outline: 0 !important;border: 0 !important;outline-style: none !important;line-height: 1 !important; font-style: normal !important; 77 | bottom: 0; 78 | display: inline-block !important; 79 | vertical-align: middle !important; 80 | position: fixed !important; 81 | z-index: 24724289 !important; 82 | } 83 | 84 | #cVim-hud span { 85 | display: inline-block !important; 86 | box-sizing: border-box !important; 87 | vertical-align: middle !important; 88 | } 89 | 90 | #cVim-status-bar { 91 | min-height: 0 !important;background-repeat: none !important;background-position: none !important;border-radius: 0 !important;box-sizing: content-box !important;vertical-align: none !important;outline: 0 !important;border: 0 !important;-webkit-appearance: none !important;letter-spacing: normal !important;word-spacing: normal !important;text-transform: none !important;text-indent: 0px !important;text-shadow: none !important;text-align: start !important;outline: 0 !important;margin: 0 !important;outline-style: none !important;font-style: normal !important; 92 | position: fixed !important; 93 | z-index: 2147483647 !important; 94 | background-color: #1b1d1e !important; 95 | color: #bbb !important; 96 | display: none; 97 | vertical-align: middle; 98 | padding-left: 2px; 99 | padding-top: 2px; 100 | box-sizing: border-box !important; 101 | box-shadow: 0 3px 3px rgba(0,0,0,0.4); 102 | left: 0 !important; 103 | width: 100% !important; 104 | height: 20px !important; 105 | } 106 | 107 | .cVim-error { 108 | min-height: 0 !important;background-repeat: none !important;background-position: none !important;border-radius: 0 !important;box-sizing: content-box !important;vertical-align: none !important;box-shadow: none !important;margin: 0 !important;-webkit-appearance: none !important;letter-spacing: normal !important;word-spacing: normal !important;text-transform: none !important;text-indent: 0px !important;text-shadow: none !important;text-align: start !important;outline: 0 !important;border: 0 !important;outline-style: none !important;line-height: 1 !important; font-style: normal !important; 109 | } 110 | 111 | #cVim-cursor { 112 | min-height: 0 !important;background-repeat: none !important;background-position: none !important;border-radius: 0 !important;box-sizing: content-box !important;vertical-align: none !important;box-shadow: none !important;margin: 0 !important;-webkit-appearance: none !important;letter-spacing: normal !important;word-spacing: normal !important;text-transform: none !important;text-indent: 0px !important;text-shadow: none !important;text-align: start !important;outline: 0 !important;border: 0 !important;outline-style: none !important;line-height: 1 !important; font-style: normal !important; 113 | position: fixed !important; background: none !important; background-image: none !important; 114 | left: 0 !important; 115 | width: 100% !important; 116 | top: 0 !important; 117 | height: 100% !important; 118 | z-index: 999999999999 !important; 119 | cursor: none !important; 120 | display: none; 121 | transform: translateZ(0) !important; 122 | -webkit-transform: translateZ(0) !important; 123 | } 124 | 125 | .cVim-find-mark { 126 | all: unset; 127 | background-color: #ffff00; 128 | color: #111; 129 | } 130 | 131 | .cVim-find-mark[active] { 132 | background-color: #ff9632; 133 | } 134 | 135 | /* {{ Delete these later */ 136 | #cVim-frames-outline { 137 | position: fixed; 138 | width: 100%; 139 | height: 100%; 140 | left: 0; 141 | top: 0; 142 | right: 0; 143 | z-index: 9999999999; 144 | box-sizing: border-box; 145 | border: 3px solid yellow; 146 | } 147 | 148 | .cVim-completion-item[active] { 149 | width: 100%; left: 0; 150 | color: #1b1d1e; 151 | background-color: #f1f1f1; 152 | } 153 | 154 | .cVim-completion-item[active] span { 155 | color: #1b1d1e; 156 | } 157 | /* }} Delete these later */ 158 | -------------------------------------------------------------------------------- /docs/tips.adoc: -------------------------------------------------------------------------------- 1 | :uri-asciidoctor: http://asciidoctor.org 2 | :icons: font 3 | :source-highlighter: pygments 4 | :nofooter: 5 | link:index.html[Home] 6 | 7 | == Tips (wip) 8 | 9 | . <> (Contribute more) 10 | . <> 11 | .. <> 12 | . <> 13 | .. <> 14 | .. <> 15 | . <> 16 | .. <> 17 | .. <> 18 | .. <> 19 | .. <> 20 | .. <> 21 | . <> 22 | 23 | 24 | 25 | == Cool custom scripts 26 | 27 | 28 | === customize mapping per domain 29 | 30 | [source,javascript] 31 | ---- 32 | site '*://mail.google.com/*' { 33 | set ignorenumerickeys 34 | unmap ` 35 | } 36 | site '*://*.reddit.com/*' { 37 | " use reddit RES extension instead 38 | unmap j 39 | unmap k 40 | unmap J 41 | unmap K 42 | unmap s 43 | } 44 | ---- 45 | 46 | === custom hints detection per domain 47 | 48 | [source,javascript] 49 | ---- 50 | " site filters 51 | siteFilters() -> {{ 52 | var filters = { 53 | '*://*.reddit.com/*': { 54 | reject: [ 55 | 'a:not([href])', 56 | '*[onclick^=click_thing]', 57 | ], 58 | accept: [ 59 | '.grippy' 60 | ], 61 | }, 62 | '*://*.google.*/*': { 63 | reject: [ 64 | 'li[class$="_dropdownitem"]', 65 | 'div[class$="_dropdown"]', 66 | 'div[aria-label="Apps"]', 67 | '.hdtbna.notl', 68 | '.irc_rit', 69 | 'a[href^="imgres"]', 70 | 'div[id=hdtbMenus]', 71 | 'div[aria-label="Account Information"]', 72 | 'img[jsaction^="load:"]' 73 | ], 74 | }, 75 | '*://github.com/*': { 76 | reject: [ 77 | '.select-menu-modal-holder.js-menu-content' 78 | ], 79 | accept: [ 80 | '.js-menu-close', 81 | ], 82 | }, 83 | '*://twitter.com/*': { 84 | accept: [ 85 | '.new-tweets-bar.js-new-tweets-bar' 86 | ], 87 | }, 88 | '*://imgur.com/*': { 89 | accept: [ 90 | '.thumb-title', 91 | '.carousel-button' 92 | ], 93 | }, 94 | '*://www.pogdesign.co.uk/*': { 95 | accept: ['.largemenutext', '.dropdown', '.icon-heart selectison'] 96 | } 97 | }; 98 | return filters; 99 | }} 100 | ---- 101 | 102 | 103 | === reload configuration on demand 104 | 105 | [source,javascript] 106 | ---- 107 | reloadcvim() -> {{ 108 | console.log('reloading cvim ' + (new Date())); 109 | var value = "call :source /home/hassen/.cvimrc"; 110 | Mappings.parseLine(value); 111 | window.location.reload(true); 112 | }} 113 | ---- 114 | 115 | 116 | === copy link description 117 | 118 | [source,javascript] 119 | ---- 120 | copyDescription(elem) -> {{ 121 | var text = elem.innerText || elem.value; 122 | RUNTIME('copy', {text: text}); 123 | }} 124 | map yd createScriptHint(copyDescription) 125 | ---- 126 | 127 | === focus on element 128 | 129 | [source,javascript] 130 | ---- 131 | focusElem(elem) -> {{ 132 | elem.focus(); 133 | }} 134 | map ys createScriptHint(focusElem) 135 | ---- 136 | 137 | == open link in incognito 138 | 139 | [source,javascript] 140 | ---- 141 | openLinkIncognito(link) -> {{ 142 | var value = "call :tabopen " + link.href + " |" 143 | Mappings.parseLine(value); 144 | }} 145 | ---- 146 | 147 | == reopen current tab in incognito 148 | 149 | [source,javascript] 150 | ---- 151 | reopenInIncognito(link) -> {{ 152 | var value = "call :tabopen " + window.location.href + " |" 153 | Mappings.parseLine(value); 154 | }} 155 | ---- 156 | 157 | 158 | == turn current youtube in fullscreen 159 | 160 | (bypassing age restriction when not authenticated) 161 | 162 | 163 | [source,javascript] 164 | ---- 165 | " youtube 166 | youtubeFullscreen() -> {{ 167 | var location=window.location.href; 168 | location = location.replace('watch', 'watch_popup') 169 | window.location.href=location; 170 | }} 171 | map ;yt :call youtubeFullscreen 172 | ---- 173 | 174 | 175 | == General tips 176 | 177 | === How to use python server for extra commands? 178 | 179 | A python server is required. 180 | Start it using `nohup python /home/hassen/workspace/mouseless/cvim_server.py > /dev/null 2>&1 &` on boot 181 | 182 | == Example config 183 | 184 | === hbt cvimrc configuration example 185 | 186 | link:https://gist.github.com/hbt/b3d6b753c5a060667582327b85300ba1[cvimrc] 187 | 188 | === How to find more examples? 189 | 190 | link:https://github.com/search?utf8=%E2%9C%93&q=filename%3A.pentadactyl+map&type=Code&ref=searchresults[github search pentadactyl examples] 191 | 192 | link:https://github.com/search?utf8=%E2%9C%93&q=filename%3A.cvimrc+map&type=Code&ref=searchresults[github search cvimrc examples] 193 | 194 | 195 | == Developer tips 196 | 197 | === What do the branches mean? 198 | 199 | - dev = active development branch and could be unstable 200 | - master = stable code and latest stable release 201 | 202 | === How to instant reload extension while developing? 203 | 204 | - `./scripts/watch.sh` 205 | - Uncomment loop in background_scripts/extension-reloader.js:25 206 | - reload extension or restart browser 207 | - tabs reload whenever a file is saved 208 | 209 | === How to generate the documentation? 210 | 211 | Original files are in adoc, generated files are in HTML. We store both instead of relying on github pages. 212 | 213 | ``` 214 | cd docs 215 | bundle 216 | asciidoctor -v -B docs/ *.adoc 217 | ``` 218 | 219 | === How to review pull request? 220 | 221 | . review diffs + quality 222 | . merge on dev branch 223 | .. git checkout -b rosshadden-fix/editing-divs master 224 | .. git pull git://github.com/rosshadden/mouseless.git fix/editing-divs 225 | .. git co dev 226 | .. git merge fix/editing-divs 227 | .. rebase + amend if needed - verify ticket number + code quality 228 | . add to commands.adoc 229 | . update changelog.adoc 230 | . add user to thanks.adoc 231 | . render all adoc files 232 | 233 | 234 | 235 | 236 | == FAQ 237 | 238 | Check issues labeled link:https://github.com/hbt/mouseless/issues?utf8=%E2%9C%93&q=is%3Aissue%20label%3Aquestion[question] 239 | 240 | 241 | -------------------------------------------------------------------------------- /pages/options.js: -------------------------------------------------------------------------------- 1 | var Settings = { 2 | initialLoad: true 3 | }; 4 | 5 | Settings.loadrc = function(config) { 6 | this.rcEl.value = config.RC; 7 | this.rcEl.style.height = this.rcEl.scrollHeight + 'px'; 8 | if (this.cssEl) { 9 | this.cssEl.setValue(config.COMMANDBARCSS); 10 | } 11 | this.gistUrl.value = config.GISTURL; 12 | }; 13 | 14 | Settings.resetSettings = function() { 15 | if (confirm('Reset all configuration and CSS settings to their default values?')) { 16 | RUNTIME('getDefaults', function(defaults) { 17 | this.rcEl.value = defaults.RC; 18 | this.cssEl.setValue(defaults.COMMANDBARCSS); 19 | this.gistUrl.value = defaults.GISTURL; 20 | delete this.settings; 21 | this.settings = Object.clone(defaults); 22 | }.bind(this)); 23 | } 24 | }; 25 | 26 | Settings.saveSettings = function() { 27 | RUNTIME('getDefaults', function(defaults) { 28 | var hadLocalConfigSet = !!this.settings.localconfig; 29 | var lastConfigPath = this.settings.configpath; 30 | this.settings = defaults; 31 | var res = window.parseConfig(Settings.rcEl.value); 32 | if (res.error !== null) { 33 | console.error('Line %d: %s', res.error.lineno, res.error.message); 34 | alert('parse error on line ' + res.error.lineno + 35 | ' of config (see console for more info)'); 36 | Status.setMessage('Error in cVimrc (line ' + res.error.lineno + ')', 2, 'error'); 37 | } else { 38 | Object.merge(this.settings, res.value); 39 | } 40 | this.settings.COMMANDBARCSS = this.cssEl.getValue(); 41 | this.settings.GISTURL = this.gistUrl.value; 42 | this.settings.mapleader = this.settings.mapleader.replace(/ /g, ''); 43 | if (hadLocalConfigSet && this.settings.localconfig && this.settings.configpath && 44 | lastConfigPath === this.settings.configpath) { 45 | alert('cVim Error: unset the localconfig before saving from here'); 46 | } 47 | this.saveButton.value = 'Saved'; 48 | chrome.runtime.sendMessage({ 49 | action: 'saveSettings', 50 | settings: this.settings, 51 | sendSettings: true 52 | }); 53 | setTimeout(function() { 54 | this.saveButton.value = 'Save'; 55 | }.bind(this), 3000); 56 | }.bind(this)); 57 | }; 58 | 59 | Settings.editMode = function(e) { 60 | if (this.cssEl) { 61 | this.cssEl.setOption('keyMap', 62 | e.target.value === 'Vim' ? 'vim' : 'default'); 63 | } 64 | }; 65 | 66 | Settings.syncGist = function() { 67 | var url = new URL(Utils.trim(this.gistUrl.value)); 68 | if (url.hostname === 'gist.github.com') { 69 | url.hostname = 'gist.githubusercontent.com'; 70 | url.pathname += '/raw'; 71 | } else if (url.hostname === 'github.com') { 72 | url.hostname = 'raw.githubusercontent.com'; 73 | var path = Utils.split(url.pathname, '/'); 74 | if (path[2] === 'blob') 75 | path.splice(2, 1); 76 | url.pathname = path.join('/'); 77 | } 78 | httpRequest({url: url.toString()}, function(res) { 79 | this.rcEl.value = res; 80 | }.bind(this)); 81 | }; 82 | 83 | function addVersionInfo() { 84 | var el = document.getElementById('version-number'); 85 | var version = chrome.runtime.getManifest().version; 86 | if (version) { 87 | el.textContent = '(' + version + ')'; 88 | } 89 | } 90 | 91 | Settings.init = function() { 92 | addVersionInfo(); 93 | document.body.spellcheck = false; 94 | 95 | this.saveButton = document.getElementById('save_button'); 96 | this.rcEl = document.getElementById('mappings'); 97 | this.editModeEl = document.getElementById('edit_mode'); 98 | 99 | function autoSize() { 100 | var stop = document.scrollingElement.scrollTop; 101 | this.style.height = ''; 102 | this.style.height = this.scrollHeight + 'px'; 103 | document.scrollingElement.scrollTop = stop; 104 | } 105 | 106 | this.rcEl.addEventListener('input', autoSize); 107 | 108 | this.editModeEl.addEventListener('change', this.editMode.bind(this), false); 109 | this.saveButton.addEventListener('click', this.saveSettings.bind(this), false); 110 | document.getElementById('reset_button').addEventListener('click', this.resetSettings.bind(this), false); 111 | document.getElementById('clearHistory').addEventListener('click', function() { 112 | RUNTIME('clearHistory'); 113 | }); 114 | this.gistUrl = document.getElementById('gistUrl'); 115 | document.getElementById('gistSync').addEventListener('click', this.syncGist.bind(this)); 116 | this.gistPlaceHolder = 'https://gist.github.com/1995eaton/9e68803bf1f1e7524340'; 117 | this.gistUrl.addEventListener('focus', function() { 118 | this.setAttribute('placeholder', ''); 119 | }); 120 | this.gistUrl.addEventListener('blur', function() { 121 | this.setAttribute('placeholder', Settings.gistPlaceHolder); 122 | }); 123 | 124 | }; 125 | 126 | port.onMessage.addListener(function(response) { 127 | if (response.type === 'sendSettings') { 128 | waitForLoad(function() { 129 | if (Settings.initialLoad) { 130 | Settings.cssEl = CodeMirror.fromTextArea(document.getElementById('commandBarCSS'), {lineNumbers: true}); 131 | Settings.initialLoad = false; 132 | Settings.settings = response.settings; 133 | Settings.init(); 134 | if (response.settings.localconfig && 135 | response.settings.configpath) { 136 | var path = 'file://' + response.settings.configpath 137 | .split('~').join(response.settings.homedirectory || '~'); 138 | RUNTIME('loadLocalConfig', { path: path }, function(e) { 139 | Settings.loadrc(e.config); 140 | switch (e.code) { 141 | case -1: 142 | alert('error loading configpath: "' + path + '"'); 143 | break; 144 | case -2: 145 | console.error('Line %d: %s', e.error.lineno, e.error.message); 146 | alert('parse error on line ' + e.error.lineno + 147 | ' of config (see console for more info)'); 148 | } 149 | }); 150 | } else { 151 | Settings.loadrc(response.settings); 152 | } 153 | } 154 | }); 155 | } 156 | }); 157 | 158 | PORT('getSettings'); 159 | -------------------------------------------------------------------------------- /cvimrc_parser/parser.peg: -------------------------------------------------------------------------------- 1 | { 2 | var scopeIdentifiers = {}; 3 | var autoJsLength = 0; 4 | function merge(a, b) { 5 | for (var p in b) { 6 | if (typeof b[p] === 'object' && !Array.isArray(b[p])) { 7 | if (!a.hasOwnProperty(p)) { 8 | a[p] = {}; 9 | } 10 | merge(a[p], b[p]); 11 | } else { 12 | a[p] = b[p]; 13 | } 14 | } 15 | } 16 | function parseScope(data) { 17 | var result = {MAPPINGS: []}; 18 | data.forEach(function(e) { 19 | if (e === null) 20 | return; 21 | if (e.MAPPINGS) { 22 | result.MAPPINGS.push(e.MAPPINGS); 23 | } else { 24 | merge(result, e); 25 | } 26 | }); 27 | result.MAPPINGS = result.MAPPINGS.join('\n'); 28 | return result; 29 | } 30 | function objectPlural(key) { 31 | var replacements = { 32 | qmark: 's', 33 | searchengine: 's', 34 | searchalias: 'es' 35 | }; 36 | if (replacements.hasOwnProperty(key)) 37 | key += replacements[key]; 38 | return key; 39 | } 40 | } 41 | 42 | Start = Scope 43 | 44 | EOF = !. 45 | EOL = [\n\r] 46 | WhiteSpace = [ \t] 47 | MapIdentifier = a:(!WhiteSpace !EOL .)+ { return text(); } 48 | LineSeparator 49 | = WhiteSpace* EOL+ WhiteSpace* "\\" WhiteSpace* { return null; } 50 | _ = (WhiteSpace / LineSeparator)+ 51 | __ = (WhiteSpace / LineSeparator)* 52 | 53 | Identifier = ([@][@])?[a-zA-Z_$][a-zA-Z_$0-9]* { return text(); } 54 | 55 | LET = 'let' 56 | SET = 'set' 57 | SITE = 'site' 58 | CALL = 'call' 59 | MAP = 'map' 60 | IMAP = 'imap' 61 | UNMAP = 'unmap' 62 | IUNMAP = 'iunmap' 63 | COMMAND = 'command' 64 | 65 | Comment 66 | = WhiteSpace* '"' [^\n]* { return null; } 67 | 68 | StringCharacter 69 | = '\\' a:[^\n] { return a; } 70 | / [^\n] 71 | / _ { return ''; } 72 | 73 | DoubleStringCharacter 74 | = !'"' a:StringCharacter { return a; } 75 | SingleStringCharacter 76 | = !'\'' a:StringCharacter { return a; } 77 | 78 | StringLiteral 79 | = '"' a:DoubleStringCharacter* '"' { return a.join(''); } 80 | / '\'' a:SingleStringCharacter* '\'' { return a.join(''); } 81 | 82 | FloatingPointLiteral 83 | = IntegerLiteral '.' ('0'* IntegerLiteral / '0'+)? { 84 | return parseFloat(text()); 85 | } 86 | / '.' ('0'* IntegerLiteral / '0'+) { return parseFloat(text()); } 87 | 88 | IntegerLiteral 89 | = '0' { return 0; } 90 | / [1-9][0-9]* { return parseInt(text(), 10); } 91 | 92 | NumberLiteral 93 | = FloatingPointLiteral 94 | / IntegerLiteral 95 | 96 | ExpressionList 97 | = head:( 98 | __ expr:Expression { 99 | return expr; 100 | } 101 | ) 102 | tail:( 103 | __ ',' __ expr:Expression { 104 | return expr; 105 | } 106 | )* 107 | { 108 | return [head].concat(tail); 109 | } 110 | 111 | ArrayLiteral 112 | = '[' __ ']' { return []; } 113 | / '[' elems:ExpressionList __ ']' { 114 | return elems; 115 | } 116 | 117 | MemberExpression 118 | = head:( 119 | Identifier 120 | ) 121 | tail:( 122 | __ '[' __ a:Expression __ ']' { 123 | return a; 124 | } 125 | )* 126 | { 127 | var result = scopeIdentifiers[head]; 128 | tail.forEach(e => result = result[e]); 129 | return result; 130 | } 131 | 132 | Expression 133 | = StringLiteral 134 | / NumberLiteral 135 | / ArrayLiteral 136 | / MemberExpression 137 | 138 | LetStatement 139 | = LET _ a:Identifier __ '=' __ b:Expression { 140 | scopeIdentifiers[a] = b; 141 | var r = {}; r[a] = b; return r; 142 | } 143 | / LET _ a:Identifier _ b:(Identifier / NumberLiteral) __ '=' __ c:Expression { 144 | scopeIdentifiers[a] = {[b]: c}; 145 | a = objectPlural(a); 146 | var r = {}; r[a] = {}; r[a][b] = c; return r; 147 | } 148 | 149 | SetStatement 150 | = SET _ 'no' a:Identifier { var r = {}; r[a] = false; return r; } 151 | / SET _ !'no' a:Identifier { var r = {}; r[a] = true; return r; } 152 | 153 | SiteStatement 154 | = SITE _ a:StringLiteral __ '{' WhiteSpace* EOL b:Scope '}' { 155 | var r = {sites: {}}; r.sites[a] = b; return r; 156 | } 157 | 158 | UnmapIdentifierList 159 | = __ a:MapIdentifier _ b:UnmapIdentifierList __ { return [a].concat(b); } 160 | / __ a:MapIdentifier __ { return [a]; } 161 | 162 | UnmapStatement 163 | = a:(UNMAP / IUNMAP) _ b:UnmapIdentifierList { 164 | return { MAPPINGS: a + ' ' + b.join(' ') }; 165 | } 166 | 167 | MapStatement 168 | = a:(MAP / IMAP) _ b:MapIdentifier _ c:[^\n]* { 169 | return { MAPPINGS: [a, b, c.join('')].join(' ') }; 170 | } 171 | 172 | CallStatement 173 | = CALL _ a:[^\n]* { 174 | return { MAPPINGS: 'call ' + a.join('') }; 175 | } 176 | 177 | CommandStatement 178 | = COMMAND _ a:[a-zA-Z]+ _ b:[^\n]+ { 179 | var r = { COMMANDS: {} }; 180 | r.COMMANDS[a.join('')] = b.join(''); 181 | return r; 182 | } 183 | 184 | JavaScriptBlockChar 185 | = (!'}}' .) { return text(); } 186 | 187 | Parameters 188 | = a:Identifier __ ',' __ b:Parameters { 189 | return [a].concat(b); 190 | } 191 | / a:Identifier { return [a]; } 192 | 193 | ParameterList 194 | = '(' __ a:Parameters? __ ')' { return a || []; } 195 | 196 | JavaScriptBlock 197 | = '{{' EOL* a:JavaScriptBlockChar* '}}' { 198 | return a.join(''); 199 | } 200 | 201 | AutoRunJavaScriptBlock 202 | = '->' __ a:JavaScriptBlock { 203 | var result = { 204 | AUTOFUNCTIONS: {} 205 | }; 206 | result.AUTOFUNCTIONS[autoJsLength] = a; 207 | autoJsLength++; 208 | return result; 209 | } 210 | 211 | JavaScriptBlockDefinition 212 | = a:Identifier __ b:ParameterList? __ '->' __ c:JavaScriptBlock { 213 | var result = { 214 | FUNCTIONS: { 215 | } 216 | }; 217 | result.FUNCTIONS[a] = '(function(' + (b || []).join(',') + ')' + 218 | '{' + c + '})'; 219 | return result; 220 | } 221 | 222 | StatementValue 223 | = LetStatement 224 | / JavaScriptBlockDefinition 225 | / AutoRunJavaScriptBlock 226 | / SetStatement 227 | / SiteStatement 228 | / ('unmapAll' / 'iunmapAll') { 229 | return {MAPPINGS: text() }; 230 | } 231 | / CallStatement 232 | / CommandStatement 233 | / MapStatement 234 | / UnmapStatement 235 | / Comment 236 | 237 | Statement 238 | = (WhiteSpace / EOL)* a:StatementValue Comment? (EOL / WhiteSpace)* { return a; } 239 | 240 | Scope 241 | = (WhiteSpace / EOL)* EOF { return parseScope([]); } 242 | / a:Statement* { return parseScope(a); } 243 | -------------------------------------------------------------------------------- /pages/codemirror/codemirror.css: -------------------------------------------------------------------------------- 1 | .CodeMirror { 2 | font-family: monospace; 3 | position: absolute; 4 | padding: 0; 5 | margin: 0; 6 | opacity: 0.8; 7 | transition: opacity 0.5s; 8 | height: 700px; 9 | font-size: 10pt; 10 | } 11 | 12 | .CodeMirror-scroll { 13 | overflow: auto; 14 | } 15 | 16 | .CodeMirror:hover { 17 | opacity: 1; 18 | } 19 | .CodeMirror-focused { 20 | opacity: 1; 21 | } 22 | 23 | .CodeMirror-lines { 24 | padding: 8px 0; /* Vertical padding around content */ 25 | } 26 | .CodeMirror pre { 27 | padding: 0 8px; /* Horizontal padding of content */ 28 | } 29 | 30 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 31 | background-color: white; /* The little square between H and V scrollbars */ 32 | } 33 | 34 | .CodeMirror-gutters { 35 | border-right: 1px solid #ddd; 36 | background-color: #f7f7f7; 37 | white-space: nowrap; 38 | } 39 | .CodeMirror-linenumbers {} 40 | .CodeMirror-linenumber { 41 | padding: 0 3px 0 5px; 42 | min-width: 20px; 43 | text-align: right; 44 | color: #999; 45 | -moz-box-sizing: content-box; 46 | box-sizing: content-box; 47 | } 48 | 49 | /* CURSOR */ 50 | 51 | .CodeMirror div.CodeMirror-cursor { 52 | border-left: 1px solid black; 53 | z-index: 3; 54 | } 55 | /* Shown when moving in bi-directional text */ 56 | .CodeMirror div.CodeMirror-secondarycursor { 57 | border-left: 1px solid silver; 58 | } 59 | .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor { 60 | width: auto; 61 | border: 0; 62 | background: #7e7; 63 | z-index: 1; 64 | } 65 | /* Can style cursor different in overwrite (non-insert) mode */ 66 | .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {} 67 | 68 | .cm-tab { display: inline-block; } 69 | 70 | .CodeMirror-ruler { 71 | border-left: 1px solid #ccc; 72 | position: absolute; 73 | } 74 | 75 | /* DEFAULT THEME */ 76 | 77 | .cm-s-default .cm-keyword {color: #708;} 78 | .cm-s-default .cm-atom {color: #219;} 79 | .cm-s-default .cm-number {color: #164;} 80 | .cm-s-default .cm-def {color: #00f;} 81 | .cm-s-default .cm-variable {color: black;} 82 | .cm-s-default .cm-variable-2 {color: #05a;} 83 | .cm-s-default .cm-variable-3 {color: #085;} 84 | .cm-s-default .cm-property {color: black;} 85 | .cm-s-default .cm-operator {color: black;} 86 | .cm-s-default .cm-comment {color: #a50;} 87 | .cm-s-default .cm-string {color: #a11;} 88 | .cm-s-default .cm-string-2 {color: #f50;} 89 | .cm-s-default .cm-meta {color: #555;} 90 | .cm-s-default .cm-qualifier {color: #555;} 91 | .cm-s-default .cm-builtin {color: #30a;} 92 | .cm-s-default .cm-bracket {color: #997;} 93 | .cm-s-default .cm-tag {color: #170;} 94 | .cm-s-default .cm-attribute {color: #00c;} 95 | .cm-s-default .cm-header {color: blue;} 96 | .cm-s-default .cm-quote {color: #090;} 97 | .cm-s-default .cm-hr {color: #999;} 98 | .cm-s-default .cm-link {color: #00c;} 99 | 100 | .cm-negative {color: #d44;} 101 | .cm-positive {color: #292;} 102 | .cm-header, .cm-strong {font-weight: bold;} 103 | .cm-em {font-style: italic;} 104 | .cm-link {text-decoration: underline;} 105 | 106 | .cm-s-default .cm-error {color: #f00;} 107 | .cm-invalidchar {color: #f00;} 108 | 109 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 110 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 111 | .CodeMirror-activeline-background {background: #e8f2ff;} 112 | 113 | /* STOP */ 114 | 115 | /* The rest of this file contains styles related to the mechanics of 116 | the editor. You probably shouldn't touch them. */ 117 | 118 | .CodeMirror { 119 | line-height: 1; 120 | position: relative; 121 | overflow: hidden; 122 | background: white; 123 | color: black; 124 | } 125 | 126 | .CodeMirror-scroll { 127 | /* 30px is the magic margin used to hide the element's real scrollbars */ 128 | /* See overflow: hidden in .CodeMirror */ 129 | margin-bottom: -30px; margin-right: -30px; 130 | padding-bottom: 30px; 131 | height: 100%; 132 | outline: none; /* Prevent dragging from highlighting the element */ 133 | position: relative; 134 | -moz-box-sizing: content-box; 135 | box-sizing: content-box; 136 | } 137 | .CodeMirror-sizer { 138 | position: relative; 139 | border-right: 30px solid transparent; 140 | -moz-box-sizing: content-box; 141 | box-sizing: content-box; 142 | } 143 | 144 | /* The fake, visible scrollbars. Used to force redraw during scrolling 145 | before actuall scrolling happens, thus preventing shaking and 146 | flickering artifacts. */ 147 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 148 | position: absolute; 149 | z-index: 6; 150 | display: none; 151 | } 152 | .CodeMirror-vscrollbar { 153 | right: 0; top: 0; 154 | overflow-x: hidden; 155 | overflow-y: scroll; 156 | } 157 | .CodeMirror-hscrollbar { 158 | bottom: 0; left: 0; 159 | overflow-y: hidden; 160 | overflow-x: scroll; 161 | } 162 | .CodeMirror-scrollbar-filler { 163 | right: 0; bottom: 0; 164 | } 165 | .CodeMirror-gutter-filler { 166 | left: 0; bottom: 0; 167 | } 168 | 169 | .CodeMirror-gutters { 170 | position: absolute; left: 0; top: 0; 171 | padding-bottom: 30px; 172 | z-index: 3; 173 | } 174 | .CodeMirror-gutter { 175 | white-space: normal; 176 | height: 100%; 177 | -moz-box-sizing: content-box; 178 | box-sizing: content-box; 179 | padding-bottom: 30px; 180 | margin-bottom: -32px; 181 | display: inline-block; 182 | } 183 | .CodeMirror-gutter-elt { 184 | position: absolute; 185 | cursor: default; 186 | z-index: 4; 187 | } 188 | 189 | .CodeMirror-lines { 190 | cursor: text; 191 | } 192 | .CodeMirror pre { 193 | /* Reset some styles that the rest of the page might have set */ 194 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 195 | border-width: 0; 196 | background: transparent; 197 | font-family: inherit; 198 | font-size: inherit; 199 | margin: 0; 200 | white-space: pre; 201 | word-wrap: normal; 202 | line-height: inherit; 203 | color: inherit; 204 | z-index: 2; 205 | position: relative; 206 | overflow: visible; 207 | } 208 | .CodeMirror-wrap pre { 209 | word-wrap: break-word; 210 | white-space: pre-wrap; 211 | word-break: normal; 212 | } 213 | 214 | .CodeMirror-linebackground { 215 | position: absolute; 216 | left: 0; right: 0; top: 0; bottom: 0; 217 | z-index: 0; 218 | } 219 | 220 | .CodeMirror-linewidget { 221 | position: relative; 222 | z-index: 2; 223 | overflow: auto; 224 | } 225 | 226 | .CodeMirror-widget {} 227 | 228 | .CodeMirror-wrap .CodeMirror-scroll { 229 | overflow-x: hidden; 230 | } 231 | 232 | .CodeMirror-measure { 233 | position: absolute; 234 | width: 100%; 235 | height: 0; 236 | overflow: hidden; 237 | visibility: hidden; 238 | } 239 | .CodeMirror-measure pre { position: static; } 240 | 241 | .CodeMirror div.CodeMirror-cursor { 242 | position: absolute; 243 | visibility: hidden; 244 | border-right: none; 245 | width: 0; 246 | } 247 | .CodeMirror-focused div.CodeMirror-cursor { 248 | visibility: visible; 249 | } 250 | 251 | .CodeMirror-selected { background: #d9d9d9; } 252 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 253 | 254 | .cm-searching { 255 | background: #ffa; 256 | background: rgba(255, 255, 0, .4); 257 | } 258 | 259 | @media print { 260 | .CodeMirror div.CodeMirror-cursor { 261 | visibility: hidden; 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /pages/markdown.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 3 | font-size: 14px; 4 | line-height: 1.6; 5 | padding-top: 10px; 6 | padding-bottom: 10px; 7 | background-color: white; 8 | padding: 30px; 9 | color: #333; 10 | min-width: 1000px; 11 | max-width: 1280px; 12 | position: absolute; 13 | left: 0; right: 0; 14 | margin: 0 auto; 15 | } 16 | 17 | body > *:first-child { 18 | margin-top: 0 !important; 19 | text-align: center; 20 | font-size: 28pt; 21 | } 22 | 23 | body > *:last-child { 24 | margin-bottom: 0 !important; 25 | } 26 | 27 | a { 28 | color: #4183C4; 29 | text-decoration: none; 30 | } 31 | 32 | a.absent { 33 | color: #cc0000; 34 | } 35 | 36 | a.anchor { 37 | display: block; 38 | padding-left: 30px; 39 | margin-left: -30px; 40 | cursor: pointer; 41 | position: absolute; 42 | top: 0; 43 | left: 0; 44 | bottom: 0; 45 | } 46 | 47 | h1, h2, h3, h4, h5, h6 { 48 | margin: 20px 0 10px; 49 | padding: 0; 50 | font-weight: bold; 51 | -webkit-font-smoothing: antialiased; 52 | cursor: text; 53 | position: relative; 54 | } 55 | 56 | h2:first-child, h1:first-child, h1:first-child + h2, h3:first-child, h4:first-child, h5:first-child, h6:first-child { 57 | margin-top: 0; 58 | padding-top: 0; 59 | } 60 | 61 | h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor { 62 | text-decoration: none; 63 | } 64 | 65 | code, tt { 66 | padding: 0; 67 | padding-top: 0.2em; 68 | padding-bottom: 0.2em; 69 | margin: 0; 70 | font-size: 85%; 71 | background-color: rgba(0,0,0,0.04); 72 | border-radius: 3px 73 | } 74 | 75 | h1 { 76 | font-size: 28px; 77 | color: black; 78 | } 79 | 80 | h2 { 81 | font-size: 24px; 82 | border-bottom: 1px solid #cccccc; 83 | color: black; 84 | } 85 | 86 | h3 { 87 | font-size: 18px; 88 | } 89 | 90 | h4 { 91 | font-size: 16px; 92 | } 93 | 94 | h5 { 95 | font-size: 14px; 96 | } 97 | 98 | h6 { 99 | color: #777777; 100 | font-size: 14px; 101 | } 102 | 103 | p, blockquote, ul, ol, dl, li, table, pre { 104 | margin: 15px 0; 105 | box-sizing: border-box; 106 | width: 100%; 107 | } 108 | 109 | hr { 110 | background: transparent url("http://tinyurl.com/bq5kskr") repeat-x 0 0; 111 | border: 0 none; 112 | color: #cccccc; 113 | height: 4px; 114 | padding: 0; 115 | } 116 | 117 | body > h2:first-child { 118 | margin-top: 0; 119 | padding-top: 0; 120 | } 121 | 122 | body > h1:first-child { 123 | margin-top: 0; 124 | padding-top: 0; 125 | } 126 | 127 | body > h1:first-child + h2 { 128 | margin-top: 0; 129 | padding-top: 0; 130 | } 131 | 132 | body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child { 133 | margin-top: 0; 134 | padding-top: 0; 135 | } 136 | 137 | a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 { 138 | margin-top: 0; 139 | padding-top: 0; 140 | } 141 | 142 | h1 p, h2 p, h3 p, h4 p, h5 p, h6 p { 143 | margin-top: 0; 144 | } 145 | 146 | li p.first { 147 | display: inline-block; 148 | } 149 | 150 | ul, ol { 151 | padding-left: 30px; 152 | } 153 | 154 | ul :first-child, ol :first-child { 155 | margin-top: 0; 156 | } 157 | 158 | ul :last-child, ol :last-child { 159 | margin-bottom: 0; 160 | } 161 | 162 | dl { 163 | padding: 0; 164 | } 165 | 166 | dl dt { 167 | font-size: 14px; 168 | font-weight: bold; 169 | font-style: italic; 170 | padding: 0; 171 | margin: 15px 0 5px; 172 | } 173 | 174 | dl dt:first-child { 175 | padding: 0; 176 | } 177 | 178 | dl dt > :first-child { 179 | margin-top: 0; 180 | } 181 | 182 | dl dt > :last-child { 183 | margin-bottom: 0; 184 | } 185 | 186 | dl dd { 187 | margin: 0 0 15px; 188 | padding: 0 15px; 189 | } 190 | 191 | dl dd > :first-child { 192 | margin-top: 0; 193 | } 194 | 195 | dl dd > :last-child { 196 | margin-bottom: 0; 197 | } 198 | 199 | blockquote { 200 | border-left: 4px solid #dddddd; 201 | padding: 0 15px; 202 | color: #777777; 203 | } 204 | 205 | blockquote > :first-child { 206 | margin-top: 0; 207 | } 208 | 209 | blockquote > :last-child { 210 | margin-bottom: 0; 211 | } 212 | 213 | table { 214 | padding: 0; 215 | display: block; 216 | word-break: keep-all; 217 | border-collapse: collapse; 218 | border-color: grey; 219 | } 220 | tbody { 221 | display: table-row-group; 222 | vertical-align: middle; 223 | border-color: inherit; 224 | } 225 | 226 | tr { 227 | display: table-row; 228 | vertical-align: inherit; 229 | border-color: inherit; 230 | border-top: 1px solid #cccccc; 231 | background-color: white; 232 | margin: 0; 233 | padding: 0; 234 | } 235 | 236 | table tr:nth-child(2n) { 237 | background-color: #f8f8f8; 238 | } 239 | 240 | table tr th { 241 | font-weight: bold; 242 | border: 1px solid #cccccc; 243 | text-align: left; 244 | margin: 0; 245 | padding: 6px 13px; 246 | } 247 | 248 | table tr td { 249 | border: 1px solid #cccccc; 250 | text-align: left; 251 | margin: 0; 252 | padding: 6px 13px; 253 | } 254 | 255 | table tr th :first-child, table tr td :first-child { 256 | margin-top: 0; 257 | } 258 | 259 | table tr th :last-child, table tr td :last-child { 260 | margin-bottom: 0; 261 | } 262 | 263 | img { 264 | max-width: 100%; 265 | } 266 | 267 | span.frame { 268 | display: block; 269 | overflow: hidden; 270 | } 271 | 272 | span.frame > span { 273 | border: 1px solid #dddddd; 274 | display: block; 275 | float: left; 276 | overflow: hidden; 277 | margin: 13px 0 0; 278 | padding: 7px; 279 | width: auto; 280 | } 281 | 282 | span.frame span img { 283 | display: block; 284 | float: left; 285 | } 286 | 287 | span.frame span span { 288 | clear: both; 289 | color: #333333; 290 | display: block; 291 | padding: 5px 0 0; 292 | } 293 | 294 | span.align-center { 295 | display: block; 296 | overflow: hidden; 297 | clear: both; 298 | } 299 | 300 | span.align-center > span { 301 | display: block; 302 | overflow: hidden; 303 | margin: 13px auto 0; 304 | text-align: center; 305 | } 306 | 307 | span.align-center span img { 308 | margin: 0 auto; 309 | text-align: center; 310 | } 311 | 312 | span.align-right { 313 | display: block; 314 | overflow: hidden; 315 | clear: both; 316 | } 317 | 318 | span.align-right > span { 319 | display: block; 320 | overflow: hidden; 321 | margin: 13px 0 0; 322 | text-align: right; 323 | } 324 | 325 | span.align-right span img { 326 | margin: 0; 327 | text-align: right; 328 | } 329 | 330 | span.float-left { 331 | display: block; 332 | margin-right: 13px; 333 | overflow: hidden; 334 | float: left; 335 | } 336 | 337 | span.float-left span { 338 | margin: 13px 0 0; 339 | } 340 | 341 | span.float-right { 342 | display: block; 343 | margin-left: 13px; 344 | overflow: hidden; 345 | float: right; 346 | } 347 | 348 | span.float-right > span { 349 | display: block; 350 | overflow: hidden; 351 | margin: 13px auto 0; 352 | text-align: right; 353 | } 354 | 355 | /* .highlight pre { */ 356 | /* background-color: #f8f8f8; */ 357 | /* border: 1px solid #cccccc; */ 358 | /* font-size: 13px; */ 359 | /* line-height: 19px; */ 360 | /* overflow: auto; */ 361 | /* padding: 6px 10px; */ 362 | /* border-radius: 3px; */ 363 | /* } */ 364 | 365 | pre { 366 | background-color: #f8f8f8; 367 | border: 1px solid #cccccc; 368 | font-size: 13px; 369 | line-height: 19px; 370 | overflow: auto; 371 | padding: 6px 10px; 372 | border-radius: 3px; 373 | } 374 | 375 | code, tt { 376 | font-family: monospace; 377 | font-size: inherit; 378 | margin: 0 2px; 379 | padding: 0 5px; 380 | white-space: nowrap; 381 | border: 1px solid #eaeaea; 382 | background-color: #f8f8f8; 383 | border-radius: 3px; 384 | } 385 | 386 | pre code { 387 | margin: 0; 388 | padding: 0; 389 | white-space: pre; 390 | border: none; 391 | background: transparent; 392 | } 393 | 394 | pre code, pre tt { 395 | background-color: transparent; 396 | border: none; 397 | } 398 | -------------------------------------------------------------------------------- /content_scripts/find.js: -------------------------------------------------------------------------------- 1 | var Find = { 2 | highlights: [], 3 | matches: [], 4 | index: 0, 5 | tries: 0, 6 | mode: '/', 7 | }; 8 | 9 | Find.setIndex = function() { 10 | this.index = 0; 11 | for (var i = 0; i < this.matches.length; i++) { 12 | var br = this.matches[i].getBoundingClientRect(); 13 | if (br.top > 0 && br.left > 0) { 14 | this.index = i; 15 | HUD.display(this.index + 1 + ' / ' + this.matches.length); 16 | break; 17 | } 18 | } 19 | }; 20 | 21 | Find.getSelectedTextNode = function() { 22 | return (this.matches.length && 23 | this.matches[this.index] && 24 | this.matches[this.index].firstChild) 25 | || false; 26 | }; 27 | 28 | Find.focusParentLink = function(node) { 29 | do { 30 | if (node.hasAttribute('href')) { 31 | node.focus(); 32 | return true; 33 | } 34 | } while (node = node.parentElement); 35 | return false; 36 | }; 37 | 38 | Find.getCurrentMatch = function() { 39 | return this.matches[this.index] || null; 40 | }; 41 | 42 | Find.search = function(mode, repeats, ignoreFocus) { 43 | if (this.matches.length === 0) 44 | return; 45 | mode = mode || '/'; 46 | 47 | var reverse = repeats < 0; 48 | if (reverse) 49 | repeats = Math.abs(repeats); 50 | if (mode === '?') 51 | reverse = !reverse; 52 | 53 | if (!this.matches.length) 54 | return HUD.display('No matches', 1); 55 | 56 | if (this.index >= this.matches.length) 57 | this.index = 0; 58 | if (this.index >= 0) 59 | this.matches[this.index].removeAttribute('active'); 60 | 61 | if (reverse && repeats === 1 && this.index === 0) { 62 | this.index = this.matches.length - 1; 63 | } else if (!reverse && repeats === 1 && 64 | this.index + 1 === this.matches.length) { 65 | this.index = 0; 66 | } else { 67 | this.index = (this.index + (reverse ? -1 : 1) * repeats); 68 | this.index = Utils.trueModulo(this.index, this.matches.length); 69 | } 70 | if (!DOM.isVisible(this.matches[this.index])) { 71 | this.matches.splice(this.index, 1); 72 | this.tries++; 73 | if (this.tries > this.matches.length) { 74 | return; 75 | } 76 | return this.search(mode, 1); 77 | } else { 78 | this.tries = 0; 79 | } 80 | var br = this.matches[this.index].getBoundingClientRect(); 81 | var origTop = document.scrollingElement.scrollTop; 82 | if (!ignoreFocus) { 83 | document.activeElement.blur(); 84 | document.body.focus(); 85 | } 86 | var isLink = ignoreFocus ? false : this.focusParentLink(this.matches[this.index]); 87 | this.matches[this.index].setAttribute('active', ''); 88 | HUD.display(this.index + 1 + ' / ' + this.matches.length); 89 | var paddingTop = 0, 90 | paddingBottom = 0; 91 | if (Command.active) { 92 | paddingBottom = Command.barPaddingBottom; 93 | paddingTop = Command.barPaddingTop; 94 | } 95 | var documentZoom = parseFloat(document.body.style.zoom) || 1; 96 | if (br.top * documentZoom + br.height * documentZoom > 97 | window.innerHeight - paddingBottom) { 98 | if (isLink && !reverse) { 99 | origTop += br.height * documentZoom; 100 | } 101 | window.scrollTo(0, origTop + paddingTop + paddingBottom); 102 | window.scrollBy(0, br.top * documentZoom + br.height * 103 | documentZoom - window.innerHeight); 104 | } else if (br.top < paddingTop) { 105 | window.scrollTo(0, origTop - paddingTop - paddingBottom); 106 | window.scrollBy(0, br.top * documentZoom); 107 | } 108 | }; 109 | 110 | Find.highlight = function(params) { 111 | // params => {} 112 | // base -> node to search in 113 | // search -> text to look for 114 | // mode -> find mode 115 | // setIndex -> find the first match within the viewport 116 | // executesearch -> run Find.search after highlighting 117 | // saveSearch -> add search to search history 118 | params.base = params.base || document.body; 119 | var self = this; 120 | var regexMode = '', 121 | containsCap = params.search.search(/[A-Z]/) !== -1, 122 | useRegex = settings.regexp, 123 | markBase = document.createElement('mark'), 124 | nodes = [], 125 | linksOnly = false; 126 | 127 | markBase.className = 'cVim-find-mark'; 128 | 129 | this.mode = params.mode || '/'; 130 | if (params.saveSearch) 131 | this.lastSearch = params.search; 132 | 133 | var search = params.search; 134 | 135 | if ((settings.ignorecase || /\/i$/.test(params.search)) && 136 | !(settings.smartcase && containsCap)) { 137 | search = search.replace(/\/i$/, ''); 138 | regexMode = 'i'; 139 | } 140 | 141 | if (useRegex) { 142 | if (params.mode === '$') { 143 | linksOnly = true; 144 | } 145 | try { 146 | var rxp = new RegExp(search, 'g' + regexMode); 147 | var mts = rxp.exec('.'); 148 | if (!mts || (mts && mts[0] !== '')) { // Avoid infinite loop 149 | search = rxp; 150 | } else { 151 | useRegex = false; 152 | } 153 | } catch (e) { // RegExp was invalid 154 | useRegex = false; 155 | } 156 | } 157 | 158 | var acceptNode = function(node) { 159 | if (!node.data.trim()) 160 | return NodeFilter.FILTER_REJECT; 161 | switch (node.parentNode.localName.toLowerCase()) { 162 | case 'script': 163 | case 'style': 164 | case 'noscript': 165 | case 'mark': 166 | return NodeFilter.FILTER_REJECT; 167 | } 168 | return DOM.isVisible(node.parentNode) ? 169 | NodeFilter.FILTER_ACCEPT : 170 | NodeFilter.FILTER_REJECT; 171 | }; 172 | 173 | var acceptLinkNode = function(node) { 174 | if (!node.data.trim()) 175 | return NodeFilter.FILTER_REJECT; 176 | Hints.type = ''; 177 | if (!node.parentNode) 178 | return NodeFilter.FILTER_REJECT; 179 | if (node.parentNode.nodeType !== Node.ELEMENT_NODE || 180 | node.parentNode.localName !== 'a') { 181 | return NodeFilter.FILTER_REJECT; 182 | } 183 | return DOM.isVisible(node.parentNode) ? 184 | NodeFilter.FILTER_ACCEPT : 185 | NodeFilter.FILTER_REJECT; 186 | }; 187 | 188 | var nodeIterator = document.createNodeIterator( 189 | params.base, 190 | NodeFilter.SHOW_TEXT, { 191 | acceptNode: linksOnly ? acceptLinkNode : acceptNode 192 | }, 193 | false 194 | ); 195 | 196 | for (var node; node = nodeIterator.nextNode(); nodes.push(node)); 197 | 198 | nodes.forEach((function() { 199 | if (useRegex) { 200 | return function(node) { 201 | var matches = node.data.match(search) || []; 202 | matches.forEach(function(match) { 203 | var mark = markBase.cloneNode(false); 204 | var mid = node.splitText(node.data.indexOf(match)); 205 | var end = mid.splitText(match.length); 206 | if (node.data.length === 0) 207 | node.remove(); 208 | if (end.data.length === 0) 209 | end.remove(); 210 | mark.appendChild(mid.cloneNode(true)); 211 | mid.parentNode.replaceChild(mark, mid); 212 | self.matches.push(mark); 213 | node = mark.nextSibling; 214 | }); 215 | }; 216 | } 217 | 218 | return function(node) { 219 | var pos = containsCap || !settings.ignorecase ? 220 | node.data.indexOf(search) : 221 | node.data.toLowerCase().indexOf(search); 222 | if (~pos) { 223 | var mark = markBase.cloneNode(false), 224 | mid = node.splitText(pos); 225 | mid.splitText(search.length); 226 | mark.appendChild(mid.cloneNode(true)); 227 | mid.parentNode.replaceChild(mark, mid); 228 | self.matches.push(mark); 229 | } 230 | }; 231 | })()); 232 | 233 | document.body.normalize(); 234 | HUD.display(this.matches.length || 'No matches'); 235 | if (params.setIndex) 236 | this.setIndex(); 237 | if (params.executeSearch) 238 | this.search(params.mode, 1); 239 | }; 240 | 241 | Find.clear = function() { 242 | var nodes = this.matches; 243 | for (var i = 0; i < nodes.length; i++) 244 | if (nodes[i] && nodes[i].parentNode && nodes[i].firstChild) 245 | nodes[i].parentNode.replaceChild(nodes[i].firstChild, nodes[i]); 246 | document.documentElement.normalize(); 247 | this.matches = []; 248 | }; 249 | -------------------------------------------------------------------------------- /content_scripts/visual.js: -------------------------------------------------------------------------------- 1 | var Visual = { 2 | queue: '', 3 | visualModeActive: false, 4 | caretModeActive: false, 5 | textNodes: [] 6 | }; 7 | 8 | Visual.getTextNodes = function(callback) { 9 | var walker = document.createTreeWalker(document.body, 4, null); 10 | var node; 11 | this.textNodes = []; 12 | while (node = walker.nextNode()) { 13 | if (node.nodeType === 3 && node.data.trim() !== '') { 14 | this.textNodes.push(node); 15 | } 16 | } 17 | if (callback) { 18 | return callback(); 19 | } 20 | }; 21 | 22 | Visual.exit = function() { 23 | this.caretModeActive = false; 24 | this.visualModeActive = false; 25 | document.designMode = 'off'; 26 | if (!Find.matches.length) { 27 | HUD.hide(); 28 | } else { 29 | HUD.display(Find.index + 1 + ' / ' + Find.matches.length); 30 | } 31 | document.body.spellcheck = true; 32 | return; 33 | }; 34 | 35 | Visual.focusSearchResult = function(lineMode) { 36 | var node = Find.getSelectedTextNode(); 37 | if (!node || node.data.length === 0) 38 | return false; 39 | this.selection = document.getSelection(); 40 | this.selection.setPosition(node, 0); 41 | if (lineMode) { 42 | this.lineMode = true; 43 | this.visualModeActive = true; 44 | this.selection.setPosition(this.selection.baseNode, 0); 45 | this.selection.extend(this.selection.baseNode, this.selection.baseNode.length); 46 | HUD.display(' -- VISUAL LINE -- '); 47 | return this.enterLineMode(); 48 | } 49 | HUD.display(' -- VISUAL -- '); 50 | this.selection = document.getSelection(); 51 | this.selection.extend(node, node.data.replace(/\s+$/, '').length); 52 | this.visualModeActive = true; 53 | }; 54 | 55 | Visual.collapse = function() { 56 | this.visualModeActive = false; 57 | var b = this.textNodes.indexOf(this.selection.anchorNode); 58 | var e = this.textNodes.indexOf(this.selection.extentNode); 59 | if ((b === e && this.selection.extentOffset < this.selection.baseOffset) || (e < b)) { 60 | this.selection.collapseToStart(); 61 | } else if (this.selection.isCollapsed === false) { 62 | this.selection.collapseToEnd(); 63 | } 64 | }; 65 | 66 | Visual.closestNode = function() { 67 | for (var i = 0; i < this.textNodes.length; ++i) { 68 | var ee = this.textNodes[i].parentElement; 69 | var br = ee.getBoundingClientRect(); 70 | if (br.top > 0) { 71 | return this.textNodes[i]; 72 | } 73 | } 74 | }; 75 | 76 | Visual.selectNode = function(index) { 77 | this.selection.setPosition(this.textNodes[index], 0); 78 | this.selection.extend(this.textNodes[index], this.textNodes[index].data.replace(/\s+$/, '').length); 79 | this.visualModeActive = true; 80 | }; 81 | 82 | Visual.scrollIntoView = function() { 83 | if (!this.selection.extentNode) { 84 | return false; 85 | } 86 | var extentParent = this.selection.extentNode.parentElement; 87 | var br = extentParent.getBoundingClientRect(); 88 | if (br.top < 0) { 89 | window.scrollBy(0, br.top); 90 | } else if (br.top + br.height > document.documentElement.clientHeight) { 91 | window.scrollBy(0, br.top + br.height - document.documentElement.clientHeight); 92 | } 93 | }; 94 | 95 | Visual.enterLineMode = function() { 96 | this.selection = document.getSelection(); 97 | this.firstLine = true; 98 | var base = this.textNodes[this.textNodes.indexOf(this.selection.baseNode)]; 99 | if (base === void 0) { 100 | HUD.setMessage(' -- VISUAL -- '); 101 | return this.lineMode = false; 102 | } 103 | if (this.selection.type === 'Caret') { 104 | this.selection.setPosition(base, 0); 105 | this.selection.extend(base, base.length); 106 | } else { 107 | var bnode = this.selection.baseNode; 108 | var enode = this.selection.extentNode; 109 | if (bnode.parentNode.getBoundingClientRect().top > enode.parentNode.getBoundingClientRect().top) { 110 | this.selection.setPosition(bnode, bnode.length); 111 | this.selection.extend(enode, 0); 112 | this.selection.modify('extend', 'left', 'lineboundary'); 113 | } else { 114 | this.selection.setPosition(bnode, 0); 115 | this.selection.extend(enode, enode.length); 116 | this.selection.modify('extend', 'right', 'lineboundary'); 117 | } 118 | this.firstExtentNode = this.selection.extentNode; 119 | } 120 | }; 121 | 122 | Visual.fillLine = function() { 123 | this.selection = document.getSelection(); 124 | if (this.selection.type === 'Caret') { 125 | this.selection.setPosition(this.selection.baseNode, 0); 126 | this.selection.modify('extend', 'right', 'lineboundary'); 127 | } 128 | }; 129 | 130 | Visual.lineAction = function(key) { 131 | this.selection = document.getSelection(); 132 | switch (key) { 133 | case 'j': 134 | if (this.firstLine || this.selection.extentNode === this.firstExtentNode || this.selection.baseNode === this.selection.extentNode) { 135 | this.selection.setPosition(this.selection.baseNode, 0); 136 | this.firstLine = false; 137 | } 138 | this.selection.modify('extend', 'right', 'line'); 139 | this.selection.modify('extend', 'left', 'lineboundary'); 140 | this.fillLine(); 141 | break; 142 | case 'k': 143 | if (this.firstLine || this.selection.extentNode === this.firstExtentNode || this.selection.baseNode === this.selection.extentNode) { 144 | this.selection.setPosition(this.selection.baseNode, this.selection.baseNode.length); 145 | this.firstLine = false; 146 | } 147 | this.selection.modify('extend', 'left', 'line'); 148 | this.selection.modify('extend', 'right', 'lineboundary'); 149 | this.fillLine(); 150 | break; 151 | case 'p': 152 | case 'P': 153 | Clipboard.copy(this.selection.toString()); 154 | Clipboard.paste(key === 'P'); 155 | this.exit(); 156 | break; 157 | case 'y': 158 | Clipboard.copy(this.selection.toString()); 159 | Visual.collapse(); 160 | break; 161 | case 'G': 162 | this.selection.modify('extend', 'right', 'documentboundary'); 163 | break; 164 | } 165 | Visual.scrollIntoView(); 166 | }; 167 | 168 | Visual.movements = { 169 | l: ['right', 'character'], 170 | h: ['left', 'character'], 171 | k: ['left', 'line'], 172 | j: ['right', 'line'], 173 | w: ['right', 'word'], 174 | b: ['left', 'word'], 175 | 0: ['left', 'lineboundary'], 176 | '^': ['left', 'lineboundary'], 177 | $: ['right', 'lineboundary'], 178 | G: ['right', 'documentboundary'] 179 | }; 180 | 181 | Visual.action = function(key) { 182 | 183 | this.selection = document.getSelection(); 184 | 185 | switch (key) { 186 | case 'g': 187 | if (!this.queue.length) { 188 | this.queue += 'g'; 189 | } else { 190 | this.queue = ''; 191 | this.selection.modify((this.visualModeActive ? 'extend' : 'move'), 192 | 'left', 'documentboundary'); 193 | this.scrollIntoView(); 194 | } 195 | return; 196 | case 'v': 197 | if (this.lineMode) { 198 | HUD.setMessage(' -- VISUAL -- '); 199 | this.lineMode = false; 200 | return; 201 | } 202 | this.visualModeActive = !this.visualModeActive; 203 | HUD.setMessage(' -- ' + 204 | (this.visualModeActive ? 'VISUAL' : 'CARET') + 205 | ' -- '); 206 | break; 207 | case 'V': 208 | this.lineMode = !this.lineMode; 209 | this.visualModeActive = true; 210 | this.enterLineMode(); 211 | HUD.setMessage(' -- VISUAL LINE -- '); 212 | return; 213 | default: 214 | this.queue = ''; 215 | } 216 | 217 | if (this.lineMode) { 218 | this.lineAction(key); 219 | return; 220 | } 221 | if (this.selection.type === 'Range') { 222 | this.visualModeActive = true; 223 | } 224 | 225 | var movementType = 226 | (this.selection.type === 'Range' || this.visualModeActive) ? 227 | 'extend' : 'move'; 228 | 229 | if (this.movements.hasOwnProperty(key)) { 230 | this.selection.modify.apply(this.selection, [movementType].concat(this.movements[key])); 231 | return; 232 | } 233 | 234 | switch (key) { 235 | case 'n': 236 | case 'N': 237 | if (key === 'N') { 238 | Mappings.actions.previousSearchResult(1); 239 | } else { 240 | Mappings.actions.nextSearchResult(1); 241 | } 242 | this.focusSearchResult(); 243 | break; 244 | case 'p': 245 | case 'P': 246 | Clipboard.copy(this.selection.toString()); 247 | this.selection.collapseToEnd(); 248 | Clipboard.paste(key === 'P'); 249 | this.exit(); 250 | break; 251 | case 'y': 252 | if (movementType === 'extend') { 253 | Clipboard.copy(this.selection.toString()); 254 | Visual.collapse(); 255 | } 256 | break; 257 | } 258 | 259 | Visual.scrollIntoView(); 260 | }; 261 | -------------------------------------------------------------------------------- /background_scripts/main.js: -------------------------------------------------------------------------------- 1 | var sessions = {}, 2 | ActiveTabs = {}, 3 | TabHistory = {}, 4 | activePorts = [], 5 | LastUsedTabs = []; 6 | 7 | window.httpRequest = function(request) { 8 | return new Promise(function(acc, rej) { 9 | var xhr = new XMLHttpRequest(); 10 | xhr.open('GET', request.url); 11 | xhr.onload = function() { 12 | acc(request.json ? JSON.parse(xhr.responseText) : xhr.responseText); 13 | }; 14 | xhr.onerror = rej.bind(null, xhr); 15 | xhr.send(); 16 | }); 17 | }; 18 | 19 | function updateTabIndices() { 20 | if (settings.showtabindices) { 21 | chrome.tabs.query({currentWindow: true}, function(tabs) { 22 | tabs.forEach(function(tab) { 23 | chrome.tabs.sendMessage(tab.id, { 24 | action: 'displayTabIndices', 25 | index: tab.index + 1 26 | }); 27 | }); 28 | }); 29 | } 30 | } 31 | 32 | function loadDomainStylesheet(changeInfo, tab) { 33 | 34 | if (changeInfo.status === 'loading') { 35 | var hostname = getHostname(tab.url) 36 | // TODO(hbt) ENHANCE issue is with defaultSettings not being applied properly and the settings object not being merged properly from the start. 37 | settings.domainStylesheets = settings.domainStylesheets || {} 38 | 39 | if (!settings.domainStylesheets.hasOwnProperty(hostname)) { 40 | return; 41 | } 42 | 43 | var styleurl = settings.domainStylesheets[hostname] 44 | 45 | // TODO(hbt) ENHANCE by caching the ajax response 46 | 47 | try { 48 | 49 | // workaround error 50 | // Unchecked runtime.lastError while running tabs.insertCSS: Cannot access contents of url "data:text/html,chromewebdata". Extension manifest must request permission to access this host. 51 | // caused by chrome error pages like network out of reach or the site can't be reached -- e.g paste this in url "http://%20https//www.npmjs.com/package/fs-finder" 52 | 53 | $.ajax({ 54 | url: styleurl 55 | }).done(function (data) { 56 | chrome.tabs.insertCSS(tab.id, { 57 | code: data, 58 | runAt: 'document_start', 59 | allFrames: true 60 | }, function (res) { 61 | }); 62 | 63 | }); 64 | } catch(e) { 65 | console.log(e) 66 | } 67 | 68 | } 69 | } 70 | 71 | chrome.storage.local.get('sessions', function(e) { 72 | if (e.sessions === void 0) { 73 | chrome.storage.local.set({ sessions: {} }); 74 | } else { 75 | sessions = e.sessions; 76 | } 77 | }); 78 | 79 | function getTab(tab, reverse, count, first, last) { 80 | chrome.tabs.query({windowId: tab.windowId}, function(tabs) { 81 | if (first) { 82 | return chrome.tabs.update(tabs[0].id, {active: true}); 83 | } else if (last) { 84 | return chrome.tabs.update(tabs[tabs.length - 1].id, {active: true}); 85 | } else { 86 | var index = (reverse ? -1 : 1) * count + tab.index; 87 | if (count !== -1 && count !== 1) 88 | index = Math.min(Math.max(0, index), tabs.length - 1); 89 | else 90 | index = Utils.trueModulo(index, tabs.length); 91 | return chrome.tabs.update(tabs[index].id, {active: true}); 92 | } 93 | }); 94 | } 95 | 96 | var Listeners = { 97 | 98 | tabs: { 99 | onUpdated: function(id, changeInfo, tab) { 100 | updateTabIndices(); 101 | loadDomainStylesheet(changeInfo, tab) 102 | if (changeInfo.hasOwnProperty('url')) { 103 | History.shouldRefresh = true; 104 | if (TabHistory.hasOwnProperty(id)) { 105 | if (TabHistory[id].links.indexOf(changeInfo.url) === -1) { 106 | if (TabHistory.state !== void 0 && TabHistory[id].state + 1 !== 107 | TabHistory[id].length) { 108 | TabHistory[id].links.splice(TabHistory[id].state); 109 | } 110 | TabHistory[id].links.push(changeInfo.url); 111 | TabHistory[id].state = TabHistory[id].state + 1; 112 | } else { 113 | TabHistory[id].state = TabHistory[id].links.indexOf(changeInfo.url); 114 | } 115 | } else { 116 | TabHistory[id] = {}; 117 | TabHistory[id].links = [changeInfo.url]; 118 | TabHistory[id].state = 0; 119 | } 120 | } 121 | }, 122 | onActivated: function(tab) { 123 | LastUsedTabs.push(tab.tabId); 124 | if (LastUsedTabs.length > 2) { 125 | LastUsedTabs.shift(); 126 | } 127 | if (ActiveTabs[tab.windowId] === void 0) { 128 | ActiveTabs[tab.windowId] = []; 129 | } 130 | ActiveTabs[tab.windowId].push(tab.tabId); 131 | if (ActiveTabs[tab.windowId].length > 2) { 132 | ActiveTabs[tab.windowId].shift(); 133 | } 134 | }, 135 | onRemoved: function(id, removeInfo) { 136 | updateTabIndices(); 137 | if (ActiveTabs[removeInfo.windowId] !== void 0) { 138 | ActiveTabs[removeInfo.windowId] = ActiveTabs[removeInfo.windowId] 139 | .filter(function(e) { 140 | return e !== id; 141 | }); 142 | } 143 | if (TabHistory[id] !== void 0) { 144 | delete TabHistory[id]; 145 | } 146 | Frames.remove(id); 147 | }, 148 | onCreated: updateTabIndices, 149 | onMoved: updateTabIndices, 150 | }, 151 | 152 | windows: { 153 | onRemoved: function(windowId) { delete ActiveTabs[windowId]; } 154 | }, 155 | 156 | extension: { 157 | onConnect: function(port) { 158 | if (activePorts.indexOf(port) !== -1) 159 | return; 160 | var frameId = port.sender.frameId; 161 | port.postMessage({type: 'hello'}); 162 | port.postMessage({type: 'addFrame', frameId: frameId}); 163 | activePorts.push(port); 164 | port.onMessage.addListener(function(request) { 165 | return Actions(request, port.sender, port.postMessage.bind(port), port); 166 | }); 167 | port.onDisconnect.addListener(function() { 168 | Frames.removeFrame(frameId); 169 | 170 | for (var i = 0; i < activePorts.length; i++) { 171 | if (activePorts[i] === port) { 172 | activePorts.splice(i, 1); 173 | break; 174 | } 175 | } 176 | }); 177 | } 178 | }, 179 | 180 | runtime: { onMessage: Actions }, 181 | 182 | commands: { 183 | onCommand: function(command) { 184 | switch (command) { 185 | case 'togglecVim': 186 | Popup.toggleEnabled({}); 187 | break; 188 | case 'toggleBlacklisted': 189 | Popup.toggleBlacklisted(); 190 | Popup.toggleEnabled({ 191 | request: { 192 | singleTab: true 193 | } 194 | }); 195 | break; 196 | case 'nextTab': 197 | case 'previousTab': 198 | chrome.tabs.query({active: true, currentWindow: true}, function(e) { 199 | return getTab(e[0], false, (command === 'nextTab' ? 1 : -1), 200 | false, false); 201 | }); 202 | break; 203 | case 'viewSource': 204 | chrome.tabs.query({active: true, currentWindow: true}, function(e) { 205 | chrome.tabs.create({url: 'view-source:' + e[0].url, index: e[0].index + 1}); 206 | }); 207 | break; 208 | case 'nextCompletionResult': 209 | chrome.tabs.query({active: true, currentWindow: true}, function(tab) { 210 | chrome.tabs.sendMessage(tab[0].id, { 211 | action: 'nextCompletionResult' 212 | }, function() { 213 | chrome.windows.create({url: 'chrome://newtab', state: 'maximized'}); 214 | }); 215 | }); 216 | break; 217 | case 'deleteBackWord': 218 | chrome.tabs.query({active: true, currentWindow: true}, function(tab) { 219 | chrome.tabs.sendMessage(tab[0].id, {action: 'deleteBackWord'}); 220 | }); 221 | break; 222 | case 'closeTab': 223 | chrome.tabs.query({active: true, currentWindow: true}, function(tab) { 224 | chrome.tabs.remove(tab[0].id, function() { 225 | return chrome.runtime.lastError; 226 | }); 227 | }); 228 | break; 229 | case 'reloadTab': 230 | chrome.tabs.query({active: true, currentWindow: true}, function(tab) { 231 | chrome.tabs.reload(tab[0].id); 232 | }); 233 | break; 234 | case 'newTab': 235 | chrome.tabs.create({url: chrome.runtime.getURL('pages/blank.html')}); 236 | break; 237 | case 'restartcVim': 238 | chrome.runtime.reload(); 239 | break; 240 | default: 241 | break; 242 | } 243 | } 244 | } 245 | 246 | }; 247 | 248 | (function() { 249 | for (var api in Listeners) { 250 | for (var method in Listeners[api]) { 251 | chrome[api][method].addListener(Listeners[api][method]); 252 | } 253 | } 254 | })(); 255 | -------------------------------------------------------------------------------- /background_scripts/options.js: -------------------------------------------------------------------------------- 1 | var storageMethod = 'local', 2 | settings = {}, 3 | Options = {}; 4 | 5 | var defaultSettings = { 6 | experimental: false, 7 | handlebothcwevents: true, 8 | searchlimit: 25, 9 | scrollstep: 70, 10 | automakelinks: false, 11 | fullpagescrollpercent: 0, 12 | typelinkhintsdelay: 300, 13 | qmarks: {}, 14 | sites: {}, 15 | searchengines: {}, 16 | searchaliases: {}, 17 | debug: false, 18 | newtabalwaysontheright: false, 19 | hud: true, 20 | regexp: true, 21 | scalehints: false, 22 | linkanimations: false, 23 | sortlinkhints: false, 24 | ignorecase: true, 25 | numerichints: false, 26 | cncpcompletion: false, 27 | smartcase: true, 28 | incsearch: true, 29 | autohidecursor: false, 30 | typelinkhints: false, 31 | autofocus: true, 32 | insertmappings: true, 33 | defaultnewtabpage: false, 34 | dimhintcharacters: true, 35 | ignorenumerickeys: false, 36 | smoothscroll: false, 37 | autoupdategist: false, 38 | nativelinkorder: false, 39 | showtabindices: false, 40 | changelog: true, 41 | localconfig: false, 42 | completeonopen: false, 43 | debugcss: false, // Always use default COMMANDBARCSS 44 | scrollduration: 500, 45 | zoomfactor: 0.10, 46 | configpath: '', 47 | locale: '', 48 | mapleader: '\\', 49 | timeoutlen: 1000, 50 | newtaburl: 'https://www.google.com', 51 | defaultengine: 'google', 52 | hintcharacters: 'asdfgqwertzxcvb', 53 | mouselesshintcharacters: 'dsafrewq,tgcx', 54 | homedirectory: '', 55 | langmap: '', 56 | completionengines: ['google', 'duckduckgo', 'wikipedia', 'amazon'], 57 | nextmatchpattern: '((?!first)(next|older|more|>|›|»|forward| )+)', 58 | previousmatchpattern: '((?!last)(prev(ious)?|newer|back|«|less|<|‹| )+)', 59 | barposition: 'top', 60 | vimport: 8001, 61 | blacklists: [], 62 | RC: '', 63 | MAPPINGS: '', 64 | GISTURL: '', 65 | FUNCTIONS: {}, 66 | domainStylesheets: {}, 67 | COMMANDBARCSS: '#cVim-command-bar, #cVim-command-bar-mode, #cVim-command-bar-input, #cVim-command-bar-search-results,\n.cVim-completion-item, .cVim-completion-item .cVim-full, .cVim-completion-item .cVim-left,\n.cVim-completion-item .cVim-right {\n font-family: Helvetica, Helvetica Neue, Neue, sans-serif, monospace, Arial;\n font-size: 10pt !important;\n -webkit-font-smoothing: antialiased !important;\n}\n\n#cVim-command-bar {\n position: fixed;\n z-index: 2147483646;\n background-color: #1b1d1e;\n color: #bbb;\n display: none;\n box-sizing: content-box;\n box-shadow: 0 3px 3px rgba(0,0,0,0.4);\n left: 0;\n width: 100%;\n height: 20px;\n}\n\n#cVim-command-bar-mode {\n display: inline-block;\n vertical-align: middle;\n box-sizing: border-box;\n padding-left: 2px;\n height: 100%;\n width: 10px;\n padding-top: 2px;\n color: #888;\n}\n\n#cVim-command-bar-input {\n background-color: #1b1d1e;\n color: #bbb;\n height: 100%;\n right: 0;\n top: 0;\n width: calc(100% - 10px);\n position: absolute;\n}\n\n#cVim-command-bar-search-results {\n position: fixed;\n width: 100%;\n overflow: hidden;\n z-index: 2147483647;\n left: 0;\n box-shadow: 0 3px 3px rgba(0,0,0,0.4);\n background-color: #1c1c1c;\n}\n\n.cVim-completion-item, .cVim-completion-item .cVim-full, .cVim-completion-item .cVim-left, .cVim-completion-item .cVim-right {\n text-overflow: ellipsis;\n padding: 1px;\n display: inline-block;\n box-sizing: border-box;\n vertical-align: middle;\n overflow: hidden;\n white-space: nowrap;\n}\n\n.cVim-completion-item:nth-child(even) {\n background-color: #1f1f1f;\n}\n\n.cVim-completion-item {\n width: 100%; left: 0;\n color: #bcbcbc;\n}\n\n.cVim-completion-item[active] {\n width: 100%; left: 0;\n color: #1b1d1e;\n background-color: #f1f1f1;\n}\n\n.cVim-completion-item[active] span {\n color: #1b1d1e;\n}\n\n.cVim-completion-item .cVim-left {\n color: #fff;\n width: 37%;\n}\n\n.cVim-completion-item .cVim-right {\n font-style: italic;\n color: #888;\n width: 57%;\n}\n\n\n#cVim-link-container, .cVim-link-hint,\n#cVim-hud, #cVim-status-bar {\n font-family: Helvetica, Helvetica Neue, Neue, sans-serif, monospace, Arial;\n font-size: 10pt !important;\n -webkit-font-smoothing: antialiased !important;\n}\n\n#cVim-link-container {\n position: absolute;\n pointer-events: none;\n width: 100%; left: 0;\n height: 100%; top: 0;\n z-index: 2147483647;\n}\n\n.cVim-link-hint {\n position: absolute;\n color: #302505 !important;\n background-color: #ffd76e !important;\n border-radius: 2px !important;\n padding: 2px !important;\n font-size: 8pt !important;\n font-weight: 500 !important;\n text-transform: uppercase !important;\n border: 1px solid #ad810c;\n display: inline-block !important;\n vertical-align: middle !important;\n text-align: center !important;\n box-shadow: 2px 2px 1px rgba(0,0,0,0.25) !important;\n}\n\n.cVim-link-hint_match {\n color: #777;\n text-transform: uppercase !important;\n}\n\n\n#cVim-hud {\n background-color: rgba(28,28,28,0.9);\n position: fixed !important;\n transition: right 0.2s ease-out;\n z-index: 24724289;\n}\n\n#cVim-hud span {\n padding: 2px;\n padding-left: 4px;\n padding-right: 4px;\n color: #8f8f8f;\n font-size: 10pt;\n}\n\n#cVim-frames-outline {\n position: fixed;\n width: 100%;\n height: 100%;\n left: 0;\n top: 0;\n right: 0;\n z-index: 9999999999;\n box-sizing: border-box;\n border: 3px solid yellow;\n}\n' 68 | }; 69 | 70 | chrome.storage.onChanged.addListener(function(changes) { 71 | if (!changes.hasOwnProperty('sessions')) { 72 | settings = changes.settings ? changes.settings.newValue : defaultSettings; 73 | } 74 | }); 75 | 76 | Options.refreshSettings = function(callback) { 77 | for (var key in defaultSettings) { 78 | if (settings[key] === void 0) { 79 | settings[key] = defaultSettings[key]; 80 | } 81 | } 82 | if (callback) { 83 | callback(); 84 | } 85 | }; 86 | 87 | Options.saveSettings = function(request) { 88 | settings = request.settings; 89 | for (var key in settings.qmarks) { 90 | Quickmarks[key] = settings.qmarks[key]; 91 | } 92 | this.refreshSettings(function() { 93 | chrome.storage[storageMethod].set({settings: settings}); 94 | if (request.sendSettings) { 95 | Options.sendSettings(); 96 | } 97 | }); 98 | }; 99 | 100 | Options.sendSettings = function() { 101 | activePorts.forEach(function(port) { 102 | port.postMessage({ 103 | type: 'sendSettings', 104 | settings: settings 105 | }); 106 | }); 107 | }; 108 | 109 | Options.getSettings = function(request, sender) { 110 | this.refreshSettings(function() { 111 | chrome.tabs.sendMessage(sender.tab.id, { 112 | action: 'sendSettings', 113 | settings: request.reset ? defaultSettings : settings 114 | }); 115 | }); 116 | }; 117 | 118 | Options.setDefaults = function() { 119 | settings = defaultSettings; 120 | this.saveSettings(settings); 121 | }; 122 | 123 | Options.getDefaults = function(request, sender, callback) { 124 | callback(defaultSettings); 125 | }; 126 | 127 | // retrieve default and current settings 128 | Options.getAllSettings = function(request, sender, callback) { 129 | callback({ 130 | defaults: defaultSettings, 131 | current: settings 132 | }); 133 | }; 134 | 135 | Options.updateBlacklistsMappings = function() { 136 | var rc = Utils.compressArray(settings.RC.split(/\n+/)), 137 | i, index, line; 138 | if (settings.BLACKLISTS) { 139 | settings.blacklists = settings.BLACKLISTS.split(/\n+/); 140 | delete settings.BLACKLISTS; 141 | } 142 | for (i = 0; i < rc.length; ++i) { 143 | if (/ *let *blacklists *= */.test(rc[i])) { 144 | rc.splice(i, 1); 145 | index = i; 146 | } 147 | } 148 | settings.blacklists = Utils.uniqueElements(settings.blacklists); 149 | if (settings.blacklists.length) { 150 | line = 'let blacklists = ' + JSON.stringify(settings.blacklists); 151 | if (index) { 152 | rc = rc.slice(0, index).concat(line).concat(rc.slice(index)); 153 | } else { 154 | rc.push(line); 155 | } 156 | } 157 | settings.RC = rc.join('\n'); 158 | Options.saveSettings({settings: settings}); 159 | }; 160 | 161 | Options.fetchGist = function() { 162 | httpRequest({ 163 | url: settings.GISTURL + (settings.GISTURL.indexOf('raw') === -1 && 164 | settings.GISTURL.indexOf('github') !== -1 ? '/raw' : '') 165 | }).then(function(res) { 166 | var updated; 167 | try { 168 | updated = RCParser.parse(res); 169 | } catch (e) { 170 | console.error('cVim Error: error parsing config file'); 171 | } 172 | updated.GISTURL = settings.GISTURL; 173 | updated.COMMANDBARCSS = settings.COMMANDBARCSS; 174 | Options.saveSettings({ 175 | settings: updated, 176 | sendSettings: true 177 | }); 178 | if (updated.autoupdategist) { 179 | window.setTimeout(Options.fetchGist, 1000 * 60 * 60); 180 | } 181 | }); 182 | }; 183 | 184 | chrome.storage[storageMethod].get('settings', function(data) { 185 | if (data.settings) { 186 | settings = data.settings; 187 | Quickmarks = settings.qmarks; 188 | } 189 | if (settings.debugcss) 190 | settings.COMMANDBARCSS = defaultSettings.COMMANDBARCSS; 191 | this.refreshSettings(); 192 | this.updateBlacklistsMappings(); 193 | if (settings.autoupdategist && settings.GISTURL) { 194 | this.fetchGist(); 195 | } 196 | }.bind(Options)); 197 | 198 | chrome.runtime.onMessage.addListener(function(request, sender, callback) { 199 | if (Options.hasOwnProperty(request.action)) { 200 | Options[request.action](request, sender, callback); 201 | } 202 | }); 203 | -------------------------------------------------------------------------------- /content_scripts/messenger.js: -------------------------------------------------------------------------------- 1 | var port = chrome.extension.connect({name: 'main'}); 2 | port.onDisconnect.addListener(function() { 3 | window.portDestroyed = true; 4 | chrome.runtime.sendMessage = function() {}; 5 | chrome.runtime.connect = function() {}; 6 | Command.hide(); 7 | removeListeners(); 8 | Visual.exit(); 9 | Find.clear(); 10 | Command.destroy(); 11 | }); 12 | 13 | (function() { 14 | var $ = function(FN, caller) { 15 | return function(action, args, callback) { 16 | if (typeof args === 'function') { 17 | callback = args; 18 | args = {}; 19 | } 20 | (args = args || {}).action = action; 21 | FN.call(caller, args, typeof callback === 'function' ? 22 | callback : void 0); 23 | }; 24 | }; 25 | RUNTIME = $(chrome.runtime.sendMessage, chrome.runtime); 26 | PORT = $(port.postMessage, port); 27 | ECHO = function(action, args, callback) { 28 | args.action = 'echoRequest'; 29 | args.call = action; 30 | port.postMessage(args, typeof callback === 'function' ? 31 | callback : void 0); 32 | }; 33 | })(); 34 | 35 | port.onMessage.addListener(function(response) { 36 | var key; 37 | switch (response.type) { 38 | case 'hello': 39 | PORT('getSettings'); 40 | PORT('getBookmarks'); 41 | PORT('getQuickMarks'); 42 | PORT('getSessionNames'); 43 | PORT('retrieveAllHistory'); 44 | PORT('sendLastSearch'); 45 | PORT('getTopSites'); 46 | PORT('getLastCommand'); 47 | break; 48 | case 'addFrame': 49 | if (innerWidth > 5 && innerHeight > 5) 50 | Frames.init(response.frameId); 51 | break; 52 | case 'focusFrame': 53 | Frames.focus(response.disableAnimation); 54 | break; 55 | case 'updateLastCommand': 56 | Mappings.lastCommand = JSON.parse(response.data); 57 | break; 58 | case 'commandHistory': 59 | for (key in response.history) { 60 | Command.history[key] = response.history[key]; 61 | } 62 | break; 63 | case 'history': 64 | var matches = []; 65 | for (key in response.history) { 66 | if (response.history[key].url) { 67 | if (response.history[key].title.trim() === '') { 68 | matches.push(['Untitled', response.history[key].url]); 69 | } else { 70 | matches.push([response.history[key].title, response.history[key].url]); 71 | } 72 | } 73 | } 74 | if (Command.historyMode) { 75 | if (Command.active && Command.bar.style.display !== 'none') { 76 | Command.completions = { history: matches }; 77 | Command.updateCompletions(false); 78 | } 79 | } else if (Command.searchMode) { 80 | Command.searchMode = false; 81 | if (Command.active && Command.bar.style.display !== 'none') { 82 | Command.completions.history = matches; 83 | Command.updateCompletions(true); 84 | } 85 | } 86 | break; 87 | case 'bookmarks': 88 | Marks.parse(response.bookmarks); 89 | break; 90 | case 'topsites': 91 | Search.topSites = response.sites; 92 | break; 93 | case 'buffers': 94 | if (Command.bar.style.display !== 'none') { 95 | var val = Command.input.value.replace(/\S+\s+/, ''); 96 | Command.hideData(); 97 | Command.completions = { 98 | buffers: (function() { 99 | if (!val.trim() || 100 | Number.isNaN(val) || 101 | !response.buffers[+val - 1]) 102 | return searchArray({ 103 | array: response.buffers, 104 | search: val, 105 | limit: settings.searchlimit, 106 | fn: function(item) { return item.join(' '); } 107 | }); 108 | return [response.buffers[+val - 1]] || []; 109 | })() 110 | }; 111 | Command.updateCompletions(); 112 | } 113 | break; 114 | case 'sessions': 115 | sessions = response.sessions; 116 | break; 117 | case 'quickMarks': 118 | Marks.parseQuickMarks(response.marks); 119 | break; 120 | case 'bookmarkPath': 121 | if (response.path.length) { 122 | Command.completions = {}; 123 | Command.completions.paths = response.path; 124 | Command.updateCompletions(); 125 | } else { 126 | Command.hideData(); 127 | } 128 | break; 129 | case 'editWithVim': 130 | //var lastInputElement = Mappings.insertFunctions.__getElement__(); 131 | var lastInputElement = document.getElementsByClassName(response.elementId)[0] 132 | if (lastInputElement) { 133 | lastInputElement[lastInputElement.value !== void 0 ? 'value' : 'innerHTML'] = 134 | response.text.replace(/\n$/, ''); // remove trailing line left by vim 135 | if (!DOM.isSubmittable(lastInputElement)) { 136 | lastInputElement.blur(); 137 | } 138 | } 139 | break; 140 | case 'httpRequest': 141 | httpCallback(response.id, response.text); 142 | break; 143 | case 'parseRC': 144 | if (response.config.MAPPINGS) { 145 | Utils.split(response.config.MAPPINGS, '\n').forEach(Mappings.parseLine); 146 | delete response.config.MAPPINGS; 147 | } 148 | Command.updateSettings(response.config); 149 | break; 150 | case 'sendSettings': 151 | Mappings.defaults = Object.clone(Mappings.defaultsClone); 152 | KeyHandler.listener.setLangMap(response.settings.langmap || ''); 153 | if (!Command.initialLoadStarted) { 154 | Command.configureSettings(response.settings); 155 | } else { 156 | settings = response.settings; 157 | Mappings.parseCustom(settings.MAPPINGS, true); 158 | } 159 | break; 160 | case 'updateLastCommand': 161 | if (request.data) { 162 | Mappings.lastCommand = JSON.parse(request.data); 163 | } 164 | break; 165 | 166 | case 'openUrlExternalEditorCallback': 167 | window.location.href = response.text 168 | break; 169 | 170 | case 'Status.setMessage': 171 | Status.setMessage(response.text) 172 | break; 173 | 174 | } 175 | }); 176 | 177 | chrome.extension.onMessage.addListener(function(request, sender, callback) { 178 | 179 | // Note(hbt) this gets called twice. Since it is included in content_script and cmdline_frame.html 180 | // Note(hbt) probably a bug but not fixing until it's a problem 181 | // workaround 182 | //if (window.self === window.top) { 183 | //} 184 | 185 | 186 | switch (request.action) { 187 | case 'hideHud': 188 | HUD.hide(true); 189 | break; 190 | case 'commandHistory': 191 | for (var key in request.history) { 192 | Command.history[key] = request.history[key]; 193 | } 194 | break; 195 | case 'updateLastSearch': 196 | Find.lastSearch = request.value; 197 | break; 198 | case 'sendSettings': 199 | Mappings.defaults = Object.clone(Mappings.defaultsClone); 200 | if (!Command.initialLoadStarted) { 201 | Command.configureSettings(request.settings); 202 | } else { 203 | settings = request.settings; 204 | Mappings.parseCustom(settings.MAPPINGS, true); 205 | } 206 | break; 207 | case 'cancelAllWebRequests': 208 | window.stop(); 209 | break; 210 | case 'updateMarks': 211 | Marks.parseQuickMarks(request.marks); 212 | break; 213 | case 'nextCompletionResult': 214 | if (window.isCommandFrame) { 215 | if (settings.cncpcompletion && 216 | Command.commandBarFocused() && 217 | Command.type === 'action') { 218 | Search.nextResult(); 219 | break; 220 | } 221 | callback(true); 222 | } 223 | break; 224 | case 'deleteBackWord': 225 | if(window.self === window.top && !DOM.isEditable(document.activeElement) && settings.experimental && settings.handlebothcwevents && document.activeElement.tagName !== 'IFRAME') 226 | { 227 | RUNTIME('closeTab'); 228 | } 229 | else { 230 | 231 | if (!insertMode && DOM.isEditable(document.activeElement)) { 232 | Mappings.insertFunctions.deleteWord(); 233 | if (Command.commandBarFocused() && Command.type === 'action') 234 | Command.complete(Command.input.value); 235 | } 236 | } 237 | 238 | 239 | 240 | break; 241 | case 'toggleEnabled': 242 | addListeners(); 243 | if (!settings) { 244 | RUNTIME('getSettings'); 245 | } 246 | Command.init(!Command.loaded); 247 | break; 248 | case 'getBlacklistStatus': 249 | callback(Command.blacklisted); 250 | break; 251 | case 'alert': 252 | alert(request.message); 253 | break; 254 | case 'showCommandFrame': 255 | if (Command.frame) { 256 | Command.frame.style.display = 'block'; 257 | Command.frame.contentWindow.focus(); 258 | } 259 | if (window.isCommandFrame === true) { 260 | window.focus(); 261 | Command.show(request.search, request.value, request.complete); 262 | } 263 | break; 264 | case 'hideCommandFrame': 265 | window.wasFocused = false; 266 | if (Command.frame) { 267 | Command.frame.style.display = 'none'; 268 | callback(); 269 | } 270 | break; 271 | case 'callFind': 272 | if (window.wasFocused) { 273 | Find[request.command].apply(Find, request.params); 274 | } 275 | break; 276 | case 'setFindIndex': 277 | if (window.wasFocused) { 278 | Find.index = request.index; 279 | } 280 | break; 281 | case 'doIncSearch': 282 | if (!window.wasFocused) 283 | break; 284 | Find.clear(); 285 | Find.highlight({ 286 | base: document.body, 287 | mode: request.mode, 288 | search: request.search 289 | }); 290 | Find.setIndex(); 291 | Find.search(request.mode, request.mode === '?' ? 1 : 0, true); 292 | break; 293 | case 'cancelIncSearch': 294 | if (Command.lastScrollTop !== void 0) 295 | document.scrollingElement.scrollTop = Command.lastScrollTop; 296 | if (Find.previousMatches && 297 | request.search && 298 | Find.lastSearch && 299 | Find.lastSearch !== request.search) { 300 | Find.clear(); 301 | HUD.hide(); 302 | Find.highlight({ base: document.body, 303 | search: Find.lastSearch, 304 | setIndex: false, 305 | executeSearch: false, 306 | reverse: true, 307 | saveSearch: true 308 | }); 309 | Find.index = Find.lastIndex - 1; 310 | Find.search('/', 1, false); 311 | } else { 312 | Find.clear(); 313 | HUD.hide(); 314 | } 315 | break; 316 | case 'echoRequest': 317 | if (!window.isCommandFrame && document.hasFocus()) { 318 | switch (request.call) { 319 | case 'callMapFunction': 320 | Mappings.actions[request.name](1); 321 | break; 322 | case 'eval': 323 | eval(settings.FUNCTIONS[request.name] + request.args); 324 | break; 325 | } 326 | } 327 | break; 328 | case 'displayTabIndices': 329 | if (Session.isRootFrame) { 330 | Command.onSettingsLoad(function() { 331 | if (settings.showtabindices) { 332 | Session.ignoreTitleUpdate = true; 333 | if (document.title === '' + request.index) { 334 | if (location.hostname + location.pathname === 335 | 'www.google.com/_/chrome/newtab') { 336 | document.title = Session.tabIndex + ' New Tab'; 337 | } else { 338 | document.title = Session.tabIndex + ' ' + 339 | location.href.replace(/.*\//, ''); 340 | } 341 | } else { 342 | document.title = document.title.replace( 343 | new RegExp('^(' + Session.tabIndex + ' )?'), 344 | (request.index ? request.index + ' ' : '')); 345 | } 346 | } 347 | Session.tabIndex = request.index; 348 | }); 349 | } 350 | break; 351 | case 'isFrameVisible': 352 | callback(e.innerWidth > 5 && e.innerHeight > 5); 353 | break; 354 | } 355 | }); 356 | -------------------------------------------------------------------------------- /content_scripts/scroll.js: -------------------------------------------------------------------------------- 1 | var Scroll = { 2 | positions: {}, 3 | history: [], 4 | historyIndex: 0, 5 | }; 6 | 7 | const NON_SCROLLABLE = 0, 8 | SCROLLABLE_Y_DOWN = 1, 9 | SCROLLABLE_Y_UP = 2, 10 | SCROLLABLE_X_RIGHT = 4, 11 | SCROLLABLE_X_LEFT = 8, 12 | SCROLLABLE = SCROLLABLE_X_LEFT | SCROLLABLE_X_RIGHT | 13 | SCROLLABLE_Y_UP | SCROLLABLE_Y_DOWN; 14 | // const SCROLLABLE_Y = SCROLLABLE_Y_UP | SCROLLABLE_Y_DOWN, 15 | // SCROLLABLE_X = SCROLLABLE_X_LEFT | SCROLLABLE_X_RIGHT; 16 | 17 | var scrollingElement = (function() { 18 | function getScrollType(elem) { 19 | var cs = getComputedStyle(elem); 20 | var st = NON_SCROLLABLE; 21 | if (cs.overflow === 'hidden') 22 | return st; 23 | if (cs.overflowX !== 'hidden' && 24 | elem.offsetHeight > elem.clientHeight && 25 | elem.scrollWidth > elem.clientWidth) { 26 | if (elem.scrollLeft > 0) 27 | st |= SCROLLABLE_X_LEFT; 28 | if (elem.scrollLeft + elem.clientWidth < elem.scrollWidth) 29 | st |= SCROLLABLE_X_RIGHT; 30 | } 31 | if (cs.overflowY !== 'hidden' && 32 | elem.offsetWidth > elem.clientWidth && 33 | elem.scrollHeight > elem.clientHeight) { 34 | if (elem.scrollTop > 0) 35 | st |= SCROLLABLE_Y_UP; 36 | if (elem.scrollTop + elem.clientHeight < elem.scrollHeight) 37 | st |= SCROLLABLE_Y_DOWN; 38 | } 39 | return st; 40 | } 41 | 42 | var lastActiveElem = null, 43 | lastScrollElem = null, 44 | clickFocus = false; 45 | 46 | window.resetScrollFocus = function() { 47 | clickFocus = false; 48 | }; 49 | 50 | document.addEventListener('mousedown', function(event) { 51 | if (!event.isTrusted) 52 | return true; 53 | clickFocus = true; 54 | lastActiveElem = event.srcElement; 55 | }); 56 | 57 | return function scrollingElement(dir) { 58 | var elem; 59 | if (clickFocus) { 60 | elem = lastActiveElem; 61 | } else { 62 | elem = lastActiveElem = document.activeElement; 63 | } 64 | if (elem === null) 65 | return null; 66 | return (function climb(elem) { 67 | if (elem === null) 68 | return lastScrollElem || document.scrollingElement; 69 | if (elem === document.scrollingElement) 70 | return elem; 71 | var st = getScrollType(elem); 72 | return st & dir ? elem : climb(elem.parentElement); 73 | })(elem); 74 | }; 75 | })(); 76 | 77 | function $scrollBy(elem, x, y) { 78 | elem.scrollLeft += x; 79 | elem.scrollTop += y; 80 | } 81 | function $scrollTo(elem, x, y) { 82 | if (x !== null) 83 | elem.scrollLeft = x; 84 | if (y !== null) 85 | elem.scrollTop = y; 86 | } 87 | 88 | (function($) { 89 | 90 | var animationYFrame, animationXFrame, 91 | scrollXFunction, scrollYFunction, 92 | scrollElem, scrollXElem, 93 | lastX, lastY; 94 | var holdKeyScroll = false; 95 | 96 | var easeFn = function(t, b, c, d) { 97 | return (t === d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b; 98 | }; 99 | 100 | var timeFn = typeof window.performance === 'undefined' ? 101 | Date.now : performance.now.bind(performance); 102 | 103 | var scroll = { 104 | x0: 0, // starting x position 105 | x1: 0, // ending x position 106 | xc: 0, // delta-x during scroll 107 | tx: 0, // delta-t 108 | txo: 0, // last time measurement 109 | dx: 0, // x-duration 110 | y0: 0, 111 | y1: 0, 112 | yc: 0, 113 | ty: 0, 114 | tyo: 0, 115 | dy: 0, 116 | callback: null, 117 | }; 118 | var scrollx = Object.clone(scroll); 119 | 120 | scrollYFunction = function() { 121 | var delta = easeFn(scroll.ty, scroll.y0, scroll.y1 - scroll.y0, scroll.dy); 122 | var time = timeFn(); 123 | scroll.yc = delta; 124 | scroll.ty += time - scroll.tyo; 125 | scroll.tyo = time; 126 | $scrollTo(scrollElem, null, delta); 127 | if (!holdKeyScroll && scroll.ty <= scroll.dy) { 128 | animationYFrame = $.requestAnimationFrame(scrollYFunction); 129 | } else { 130 | $.cancelAnimationFrame(animationYFrame); 131 | animationYFrame = null; 132 | $scrollTo(scrollElem, null, scroll.y1); 133 | scroll.y0 = scroll.y1 = scroll.yc = scroll.ty = 0; 134 | if (scroll.callback) 135 | scroll.callback(); 136 | } 137 | }; 138 | 139 | scrollXFunction = function() { 140 | var delta = easeFn(scrollx.tx, scrollx.x0, scrollx.x1 - scrollx.x0, scrollx.dx); 141 | var time = timeFn(); 142 | scrollx.xc = delta; 143 | scrollx.tx += time - scrollx.txo; 144 | scrollx.txo = time; 145 | $scrollTo(scrollXElem, delta, null); 146 | if (!holdKeyScroll && scrollx.tx <= scrollx.dx) { 147 | animationXFrame = $.requestAnimationFrame(scrollXFunction); 148 | } else { 149 | $.cancelAnimationFrame(animationXFrame); 150 | $scrollTo(scrollXElem, scrollx.x1, null); 151 | scrollx.x0 = scrollx.x1 = scrollx.xc = scrollx.tx = 0; 152 | if (scroll.callback) 153 | scroll.callback(); 154 | } 155 | }; 156 | 157 | $.setSmoothScrollEaseFN = function(fn) { 158 | easeFn = fn; 159 | }; 160 | 161 | $.smoothScrollTo = function(elem, x, y, d, callback) { 162 | scrollElem = elem; 163 | $.cancelAnimationFrame(animationXFrame); 164 | $.cancelAnimationFrame(animationYFrame); 165 | scroll.dx = scroll.dy = d; 166 | scrollx.dx = scrollx.dy = d; 167 | scroll.callback = callback || function() {}; 168 | if (x !== scrollElem.scrollLeft) { 169 | scrollXElem = elem; 170 | scrollx.x0 = scrollXElem.scrollLeft; 171 | scrollx.x1 = x; 172 | scrollx.tx = 0; 173 | scrollx.txo = timeFn(); 174 | scrollXFunction(); 175 | } 176 | if (y !== scrollElem.scrollTop) { 177 | scroll.y0 = scrollElem.scrollTop; 178 | scroll.y1 = y; 179 | scroll.ty = 0; 180 | scroll.tyo = timeFn(); 181 | scrollYFunction(); 182 | } 183 | }; 184 | 185 | var holdFunction = function(dx, dy) { 186 | var se = dx ? scrollXElem : scrollElem; 187 | return (function animationLoop() { 188 | if ($.scrollKeyUp) { 189 | holdKeyScroll = false; 190 | return; 191 | } 192 | $scrollBy(se, dx, dy); 193 | $.requestAnimationFrame(animationLoop); 194 | })(); 195 | }; 196 | 197 | $.smoothScrollBy = function(elem, x, y, d, callback) { 198 | if (x) { 199 | scrollXElem = elem; 200 | } else { 201 | scrollElem = elem; 202 | } 203 | if (!$.scrollKeyUp && x === lastX && y === lastY) { 204 | if (!holdKeyScroll) { 205 | holdKeyScroll = true; 206 | holdFunction(x * 0.25, y * 0.25); 207 | } 208 | return; 209 | } 210 | scroll.callback = callback || function() {}; 211 | lastX = x; 212 | lastY = y; 213 | $.scrollKeyUp = false; 214 | holdKeyScroll = false; 215 | if (x) { 216 | var oldDx = scrollx.x1 - scrollx.xc; 217 | $.cancelAnimationFrame(animationXFrame); 218 | scrollx.dx = d; 219 | scrollx.x0 = scrollXElem.scrollLeft; 220 | scrollx.x1 = oldDx + scrollx.x0 + x; 221 | scrollx.tx = 0; 222 | scrollx.txo = timeFn(); 223 | scrollXFunction(); 224 | } 225 | if (y) { 226 | var oldDy = scroll.y1 - scroll.yc; 227 | scroll.dy = d; 228 | scroll.y0 = scrollElem.scrollTop; 229 | scroll.y1 = oldDy + scroll.y0 + y; 230 | scroll.ty = 0; 231 | scroll.tyo = timeFn(); 232 | $.cancelAnimationFrame(animationYFrame); 233 | scrollYFunction(); 234 | } 235 | }; 236 | 237 | $.scrollKeyUp = true; 238 | 239 | })(this); 240 | 241 | Scroll.historyStateEquals = function(s1, s2) { 242 | if (!s1 || !s2) 243 | return false; 244 | return s1[0] === s2[0] && 245 | s1[1] === s2[1] && 246 | s1[2] === s2[2]; 247 | }; 248 | 249 | Scroll.scrollToHistoryState = function(index) { 250 | index = index || this.historyIndex; 251 | var state = this.history[index]; 252 | if (!state) 253 | return; 254 | var scrollElem = state[0]; 255 | scrollElem.scrollLeft = state[1]; 256 | scrollElem.scrollTop = state[2]; 257 | this.historyIndex = index; 258 | }; 259 | 260 | Scroll.previousHistoryState = function() { 261 | if (!this.historyStateEquals(this.lastState(), this.currentState())) 262 | this.addHistoryState(); 263 | if (this.historyIndex > 0) { 264 | this.historyIndex--; 265 | this.scrollToHistoryState(this.historyIndex); 266 | } 267 | }; 268 | 269 | Scroll.nextHistoryState = function() { 270 | if (this.historyIndex + 1 < this.history.length) { 271 | this.historyIndex++; 272 | this.scrollToHistoryState(this.historyIndex); 273 | } 274 | }; 275 | 276 | Scroll.currentState = function() { 277 | // TODO make work with nested scrolling elements 278 | // var scrollElem = scrollingElement(SCROLLABLE); 279 | 280 | var scrollElem = document.scrollingElement; 281 | if (!scrollElem) 282 | return null; 283 | return [scrollElem, scrollElem.scrollLeft, scrollElem.scrollTop]; 284 | }; 285 | 286 | Scroll.lastState = function() { 287 | if (this.historyIndex >= this.history.length) 288 | return null; 289 | return this.history[this.historyIndex]; 290 | }; 291 | 292 | Scroll.addHistoryState = function() { 293 | var nextState = this.currentState(); 294 | if (!nextState) 295 | return false; 296 | if (this.historyIndex + 1 < this.history.length) 297 | this.history = this.history.slice(0, this.historyIndex + 1); 298 | if (this.history.length) { 299 | if (this.historyStateEquals(this.lastState(), nextState)) 300 | return false; 301 | } 302 | this.history.push(nextState); 303 | this.historyIndex = this.history.length - 1; 304 | return true; 305 | }; 306 | 307 | Scroll.scroll = function(type, repeats) { 308 | 309 | var stepSize = settings ? settings.scrollstep : 60; 310 | 311 | var shouldLogPosition = !/^(up|down|left|right|pageUp|pageDown)$/.test(type); 312 | if (document.body && shouldLogPosition) { 313 | this.lastPosition = [document.scrollingElement.scrollLeft, document.scrollingElement.scrollTop]; 314 | Scroll.addHistoryState(); 315 | } 316 | 317 | var direction = (function() { 318 | switch (type) { 319 | case 'up': case 'pageUp': case 'fullPageUp': case 'top': 320 | return SCROLLABLE_Y_UP; 321 | case 'left': case 'leftmost': 322 | return SCROLLABLE_X_LEFT; 323 | case 'right': case 'rightmost': 324 | return SCROLLABLE_X_RIGHT; 325 | default: 326 | return SCROLLABLE_Y_DOWN; 327 | } 328 | })(); 329 | 330 | var scrollElem = scrollingElement(direction), 331 | hy = scrollElem === document.body ? innerHeight : scrollElem.clientHeight, 332 | hw = scrollElem === document.body ? innerWidth : scrollElem.clientWidth, 333 | x = 0, 334 | y = 0; 335 | 336 | switch (type) { 337 | case 'down': 338 | y = repeats * stepSize; 339 | break; 340 | case 'up': 341 | y -= repeats * stepSize; 342 | break; 343 | case 'pageDown': 344 | y = repeats * hy >> 1; 345 | break; 346 | case 'fullPageDown': 347 | y = repeats * hy * (settings.fullpagescrollpercent / 100 || 1); 348 | break; 349 | case 'pageUp': 350 | y -= repeats * hy >> 1; 351 | break; 352 | case 'fullPageUp': 353 | y -= repeats * hy * (settings.fullpagescrollpercent / 100 || 1); 354 | break; 355 | case 'top': 356 | y -= scrollElem.scrollTop; 357 | break; 358 | case 'bottom': 359 | y = scrollElem.scrollHeight - scrollElem.scrollTop - hy + 20; 360 | break; 361 | case 'left': 362 | x -= repeats * stepSize >> 1; 363 | break; 364 | case 'right': 365 | x = repeats * stepSize >> 1; 366 | break; 367 | case 'leftmost': 368 | x -= scrollElem.scrollLeft; 369 | break; 370 | case 'rightmost': 371 | x = scrollElem.scrollWidth - scrollElem.scrollLeft - hw + 20; 372 | break; 373 | } 374 | 375 | if (settings && settings.smoothscroll) { 376 | window.smoothScrollBy(scrollElem, x, y, settings.scrollduration); 377 | } else { 378 | $scrollBy(scrollElem, x, y); 379 | } 380 | }; 381 | -------------------------------------------------------------------------------- /docs/settings.adoc: -------------------------------------------------------------------------------- 1 | :uri-asciidoctor: http://asciidoctor.org 2 | :icons: font 3 | :source-highlighter: pygments 4 | :nofooter: 5 | link:index.html[Home] 6 | 7 | == All settings 8 | 9 | 10 | link:commands.html[commands list] 11 | 12 | 13 | |=== 14 | |Name|Type|Description|Default 15 | | experimental 16 | | boolean 17 | | turns on commands flagged as experimental. View link:changelog.html[changelog] 18 | | false 19 | 20 | 21 | | handlebothcwevents 22 | | boolean 23 | | turns on support for C-w to both close the tab AND delete the backword -- https://github.com/hbt/mouseless/issues/117 24 | | false 25 | 26 | | ignorenumerickeys 27 | | boolean 28 | | counter/repeats are ignored. e.g 3j will only execute once 29 | | false 30 | 31 | | debug 32 | | boolean 33 | | prints helpful debug messages related to configuration file 34 | | false 35 | 36 | | mouselesshintcharacters 37 | | string 38 | | the mouseless hint characters as opposed to the cvim ones. first set of letters is prioritized over second until we run out of permutations (separated by `,`) 39 | | dsafrewq,tgcx 40 | 41 | | newtabalwaysontheright 42 | | boolean 43 | | new tabs are always open on the right of the current tab 44 | | false 45 | 46 | 47 | | automakelinks 48 | | boolean 49 | | detects urls and transforms them into clickable links after document has rendered 50 | | false 51 | 52 | 53 | | newtaburl 54 | | string 55 | | url when opening a new tab with `` - defaults to blank in incognito 56 | | false 57 | 58 | | searchlimit 59 | | integer 60 | | set the amount of results displayed in the command bar 61 | | 25 62 | 63 | | scrollstep 64 | | integer 65 | | set the amount of pixels scrolled when using the scrollUp and scrollDown commands 66 | | 70 67 | 68 | | timeoutlen 69 | | integer 70 | | The amount of time to wait for a `` mapping in milliseconds 71 | | 1000 72 | 73 | | fullpagescrollpercent 74 | | integer 75 | | set the percent of the page to be scrolled by when using the scrollFullPageUp and scrollFullPageDown commands 76 | | 0 77 | 78 | | typelinkhintsdelay 79 | | integer 80 | | the amount of time (in milliseconds) to wait before taking input after opening a link hint with typelinkhints and numerichints enabled 81 | | 300 82 | 83 | | scrollduration 84 | | integer 85 | | the duration of smooth scrolling 86 | | 500 87 | 88 | | vimport 89 | | integer 90 | | set the port to be used with the `editWithVim` insert mode command 91 | | 8001 92 | 93 | | zoomfactor 94 | | integer / double 95 | | the step size when zooming the page in/out 96 | | 0.1 97 | 98 | | scalehints 99 | | boolean 100 | | animate link hints as they appear 101 | | false 102 | 103 | | hud 104 | | boolean 105 | | show the heads-up-display 106 | | true 107 | 108 | | regexp 109 | | boolean 110 | | use regexp in find mode 111 | | true 112 | 113 | | ignorecase 114 | | boolean 115 | | ignore search case in find mode 116 | | true 117 | 118 | | linkanimations 119 | | boolean 120 | | show fade effect when link hints open and close 121 | | false 122 | 123 | | numerichints 124 | | boolean 125 | | use numbers for link hints instead of a set of characters 126 | | false 127 | 128 | | dimhintcharacters 129 | | boolean 130 | | dim letter matches in hint characters rather than remove them from the hint 131 | | true 132 | 133 | | defaultnewtabpage 134 | | boolean 135 | | use the default chrome://newtab page instead of a blank page 136 | | false 137 | 138 | | cncpcompletion 139 | | boolean 140 | | use `` and `` to cycle through completion results (requires you to set the nextCompletionResult keybinding in the chrome://extensions page (bottom right) 141 | | false 142 | 143 | | smartcase 144 | | boolean 145 | | case-insensitive find mode searches except when input contains a capital letter 146 | | true 147 | 148 | | incsearch 149 | | boolean 150 | | begin auto-highlighting find mode matches when input length is greater thant two characters 151 | | true 152 | 153 | | typelinkhints 154 | | boolean 155 | | (numerichints required) type text in the link to narrow down numeric hints 156 | | false 157 | 158 | | autohidecursor 159 | | boolean 160 | | hide the mouse cursor when scrolling (useful for Linux, which doesn't auto-hide the cursor on keydown) 161 | | false 162 | 163 | | autofocus 164 | | boolean 165 | | allows websites to automatically focus an input box when they are first loaded 166 | | true 167 | 168 | | insertmappings 169 | | boolean 170 | | use insert mappings to navigate the cursor in text boxes (see bindings below) 171 | | true 172 | 173 | | smoothscroll 174 | | boolean 175 | | use smooth scrolling 176 | | false 177 | 178 | | autoupdategist 179 | | boolean 180 | | if a GitHub Gist is used to sync settings, pull updates every hour (and when Chrome restarts) 181 | | false 182 | 183 | | nativelinkorder 184 | | boolean 185 | | Open new tabs like Chrome does rather than next to the currently opened tab 186 | | false 187 | 188 | | showtabindices 189 | | boolean 190 | | Display the tab index in the tab's title 191 | | false 192 | 193 | | sortlinkhints 194 | | boolean 195 | | Sort link hint lettering by the link's distance from the top-left corner of the page 196 | | false 197 | 198 | | localconfig 199 | | boolean 200 | | Read the cVimrc config from `configpath` (when this is set, you connot save from cVim's options page 201 | | false 202 | 203 | | completeonopen 204 | | boolean 205 | | Automatically show a list of command completions when the command bar is opened 206 | | false 207 | 208 | | configpath 209 | | string 210 | | Read the cVimrc from this local file when configpath is set 211 | | "" 212 | 213 | | changelog 214 | | boolean 215 | | Auto open the changelog when cVim is updated 216 | | true 217 | 218 | | completionengines 219 | | array of strings 220 | | use only the specified search engines 221 | | ["google", "duckduckgo", "wikipedia", "amazon"] 222 | 223 | | blacklists 224 | | array of strings 225 | | disable cVim on the sites matching one of the patterns 226 | | [] 227 | 228 | | mapleader 229 | | string 230 | | The default `` key 231 | | \ 232 | 233 | | defaultengine 234 | | string 235 | | set the default search engine 236 | | "google" 237 | 238 | | locale 239 | | string 240 | | set the locale of the site being completed/searched on (see example configuration below) 241 | | "" 242 | 243 | | homedirectory 244 | | string 245 | | the directory to replace `~` when using the `file` command 246 | | "" 247 | 248 | | qmark <alphanumeric charcter> 249 | | string 250 | | add a persistent QuickMark (e.g. ```let qmark a = ["http://google.com", "http://reddit.com"]```) 251 | | none 252 | 253 | | previousmatchpattern 254 | | string (regexp) 255 | | the pattern looked for when navigating a page's back button 256 | | ((?!last)(prev(ious)?|newer|back|«|less|<|‹| )+) 257 | 258 | | nextmatchpattern 259 | | string (regexp) 260 | | the pattern looked for when navigation a page's next button 261 | | ((?!first)(next|older|more|>|›|»|forward| )+) 262 | 263 | | hintcharacters 264 | | string (alphanumeric) 265 | | set the default characters to be used in link hint mode 266 | | "asdfgqwertzxcvb" 267 | 268 | | barposition 269 | | string ["top", "bottom"] 270 | | set the default position of the command bar 271 | | "top" 272 | 273 | 274 | | langmap 275 | | string 276 | | set a list of characters to be remapped (see vims langmap) 277 | | "" 278 | 279 | 280 | --------------------------------------------------------------------------------