diff options
| -rw-r--r-- | README.md | 7 | ||||
| -rw-r--r-- | background_scripts/commands.coffee | 49 | ||||
| -rw-r--r-- | lib/keyboard_utils.coffee | 5 | ||||
| -rw-r--r-- | lib/settings.coffee | 32 | ||||
| -rw-r--r-- | manifest.json | 4 | ||||
| -rw-r--r-- | pages/blank.html | 25 | ||||
| -rw-r--r-- | pages/completion_engines.html | 25 | ||||
| -rw-r--r-- | pages/content_script_loader.coffee | 28 | ||||
| -rw-r--r-- | pages/help_dialog.html | 25 | ||||
| -rw-r--r-- | pages/logging.html | 25 | ||||
| -rw-r--r-- | pages/options.html | 25 | ||||
| -rw-r--r-- | pages/vimium_resources.html | 24 | ||||
| -rw-r--r-- | tests/unit_tests/commands_test.coffee | 44 |
13 files changed, 116 insertions, 202 deletions
@@ -159,12 +159,15 @@ Please see [CONTRIBUTING.md](https://github.com/philc/vimium/blob/master/CONTRIB Release Notes ------------- +<!-- Changes since the previous release (not in the Chrome Store version) +--> + +1.57 (2016-10-01) - New commands: - `toggleMuteTab` - mute or unmute the current tab (default binding - `<a-m>`), see also [advanced - usage](https://github.com/philc/vimium/wiki/Tips-and-Tricks#muting-tabs). + `<a-m>`), see also [advanced usage](https://github.com/philc/vimium/wiki/Tips-and-Tricks#muting-tabs). - Other new features: - You can now map `<backspace>` to a Vimium command (e.g. `map <backspace> goBack`). - For link hints, when one hint marker is covered by another, `<Space>` now diff --git a/background_scripts/commands.coffee b/background_scripts/commands.coffee index e905c410..7ab09f24 100644 --- a/background_scripts/commands.coffee +++ b/background_scripts/commands.coffee @@ -23,13 +23,13 @@ Commands = @availableCommands[command] = extend options, description: description - mapKeyToCommand: ({ key, command, options }) -> + mapKeyToCommand: ({ key, keySequence, command, options }) -> unless @availableCommands[command] BgUtils.log "#{command} doesn't exist!" return options ?= {} - @keyToCommandRegistry[key] = extend { command, options }, @availableCommands[command] + @keyToCommandRegistry[key] = extend { keySequence, command, options }, @availableCommands[command] # Lower-case the appropriate portions of named keys. # @@ -39,10 +39,20 @@ Commands = # humans may prefer other forms <Left> or <C-a>. # On the other hand, <c-a> and <c-A> are different named keys - for one of # them you have to press "shift" as well. - normalizeKey: (key) -> - key.replace(/<[acm]-/ig, (match) -> match.toLowerCase()) - .replace(/<([acm]-)?([a-zA-Z0-9]{2,})>/g, (match, optionalPrefix, keyName) -> - "<" + (if optionalPrefix then optionalPrefix else "") + keyName.toLowerCase() + ">") + # We sort modifiers here to match the order used in keyboard_utils.coffee. + # The return value is a sequence of keys: e.g. "<Space><c-A>b" -> ["<space>", "<c-A>", "b"]. + parseKeySequence: (key) -> + if key.length == 0 + [] + # Parse "<c-a>bcd" as "<c-a>" and "bcd". + else if 0 == key.search /^<((?:[acm]-)*(?:.|[a-zA-Z0-9]{2,}))>(.*)/i + [modifiers..., keyChar] = RegExp.$1.split "-" + keyChar = keyChar.toLowerCase() unless keyChar.length == 1 + modifiers = (modifier.toLowerCase() for modifier in modifiers) + modifiers.sort() + ["<#{[modifiers..., keyChar].join '-'}>", @parseKeySequence(RegExp.$2)...] + else + [key[0], @parseKeySequence(key[1..])...] parseCustomKeyMappings: (customKeyMappings) -> for line in customKeyMappings.split "\n" @@ -52,13 +62,15 @@ Commands = when "map" [ _, key, command, optionList... ] = tokens if command? and @availableCommands[command] - key = @normalizeKey key + keySequence = @parseKeySequence key + key = keySequence.join "" BgUtils.log "Mapping #{key} to #{command}" - @mapKeyToCommand { key, command, options: @parseCommandOptions command, optionList } + @mapKeyToCommand { key, command, keySequence, options: @parseCommandOptions command, optionList } when "unmap" if tokens.length == 2 - key = @normalizeKey tokens[1] + keySequence = @parseKeySequence tokens[1] + key = keySequence.join "" BgUtils.log "Unmapping #{key}" delete @keyToCommandRegistry[key] @@ -90,24 +102,25 @@ Commands = clearKeyMappingsAndSetDefaults: -> @keyToCommandRegistry = {} - @mapKeyToCommand { key, command } for own key, command of defaultKeyMappings + for own key, command of defaultKeyMappings + keySequence = @parseKeySequence key + key = keySequence.join "" + @mapKeyToCommand { key, command, keySequence } # This generates a nested key-to-command mapping structure. There is an example in mode_key_handler.coffee. generateKeyStateMapping: -> - # Keys are either literal characters, or "named" - for example <a-b> (alt+b), <left> (left arrow) or <f12> - # This regular expression captures two groups: the first is a named key, the second is the remainder of - # the string. - namedKeyRegex = /^(<(?:[amc]-.|(?:[amc]-)?[a-z0-9]{2,})>)(.*)$/ keyStateMapping = {} for own keys, registryEntry of @keyToCommandRegistry currentMapping = keyStateMapping - while 0 < keys.length - [key, keys] = if 0 == keys.search namedKeyRegex then [RegExp.$1, RegExp.$2] else [keys[0], keys[1..]] + for key, index in registryEntry.keySequence if currentMapping[key]?.command - break # Do not overwrite existing command bindings, they take priority. - else if 0 < keys.length + # Do not overwrite existing command bindings, they take priority. NOTE(smblott) This is the legacy + # behaviour. + break + else if index < registryEntry.keySequence.length - 1 currentMapping = currentMapping[key] ?= {} else + delete registryEntry.keySequence # We don't need this any more. currentMapping[key] = registryEntry chrome.storage.local.set normalModeKeyStateMapping: keyStateMapping diff --git a/lib/keyboard_utils.coffee b/lib/keyboard_utils.coffee index dabf864d..f0e791d4 100644 --- a/lib/keyboard_utils.coffee +++ b/lib/keyboard_utils.coffee @@ -111,9 +111,10 @@ KeyboardUtils = modifiers = [] keyChar = keyChar.toUpperCase() if event.shiftKey - modifiers.push "m" if event.metaKey - modifiers.push "c" if event.ctrlKey + # These must be in alphabetical order (to match the sorted modifier order in Commands.normalizeKey). modifiers.push "a" if event.altKey + modifiers.push "c" if event.ctrlKey + modifiers.push "m" if event.metaKey keyChar = [modifiers..., keyChar].join "-" if 1 < keyChar.length then "<#{keyChar}>" else keyChar diff --git a/lib/settings.coffee b/lib/settings.coffee index 92871ee2..e16261d0 100644 --- a/lib/settings.coffee +++ b/lib/settings.coffee @@ -184,40 +184,20 @@ Settings.init() # Perform migration from old settings versions, if this is the background page. if Utils.isBackgroundPage() - if not Settings.get "settingsVersion" + unless Settings.get "settingsVersion" # This is a new install. For some settings, we retain a legacy default behaviour for existing users but # use a non-default behaviour for new users. - # For waitForEnterForFilteredHints, we (smblott) think that "true" gives a better UX; see #1950. However, - # forcing the change on existing users would be unnecessarily disruptive. So, only new users default to - # "true". + # For waitForEnterForFilteredHints, "true" gives a better UX; see #1950. However, forcing the change on + # existing users would be unnecessarily disruptive. So, only new users default to "true". Settings.set "waitForEnterForFilteredHints", true # We use settingsVersion to coordinate any necessary schema changes. Settings.set("settingsVersion", Utils.getCurrentVersion()) - # In 1.46 we migrated the old "excludedUrls" setting to the new "exclusionRules" setting. And we kept a - # backup in "excludedUrlsBackup". Now (post 1.54, post 2016-02-12) we can clear up that backup (and any - # extraordinalrily old "excludedUrls" setting). - Settings.nuke "excludedUrlsBackup" - Settings.nuke "excludedUrls" - - # Migration (post 1.54, post 2016-2-12). Nuke legacy "findModeRawQuery" setting. - Settings.nuke "findModeRawQuery" - - # Migration (after 1.51, 2015/6/17). - # Copy options with non-default values (and which are not in synced storage) to chrome.storage.local; - # thereby making these settings accessible within content scripts. - do (migrationKey = "copyNonDefaultsToChromeStorage-20150717") -> - unless localStorage[migrationKey] - chrome.storage.sync.get null, (items) -> - unless chrome.runtime.lastError - updates = {} - for own key of localStorage - if Settings.shouldSyncKey(key) and not items[key] - updates[key] = localStorage[key] - chrome.storage.local.set updates, -> - localStorage[migrationKey] = not chrome.runtime.lastError + # Remove legacy key which was used to control storage migration. This was after 1.57 (2016-10-01), and can + # be removed after 1.58 has been out for sufficiently long. + Settings.nuke "copyNonDefaultsToChromeStorage-20150717" root = exports ? window root.Settings = Settings diff --git a/manifest.json b/manifest.json index 1409b272..a40cd134 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Vimium", - "version": "1.56", + "version": "1.57", "description": "The Hacker's Browser. Vimium provides keyboard shortcuts for navigation and control in the spirit of Vim.", "icons": { "16": "icons/icon16.png", "48": "icons/icon48.png", @@ -35,6 +35,8 @@ ], "content_scripts": [ { + "_comment": + "IMPORTANT: All resources listed here must also be listed in ./pages/vimium_resources.html.", "matches": ["<all_urls>"], "js": ["lib/utils.js", "lib/keyboard_utils.js", diff --git a/pages/blank.html b/pages/blank.html index 4f0d7bfa..c238282d 100644 --- a/pages/blank.html +++ b/pages/blank.html @@ -1,30 +1,7 @@ <html> <head> <title>New Tab</title> - <!-- <script src="content_script_loader.js"></script> --> - <!-- NOTE(smblott) Temporarily, do not use the content-script loader (2016-09-27). It appears no longer - to be loading scripts synchronously, and so all dependent pages fail. --> - <script src="../lib/utils.js"></script> - <script src="../lib/keyboard_utils.js"></script> - <script src="../lib/dom_utils.js"></script> - <script src="../lib/rect.js"></script> - <script src="../lib/handler_stack.js"></script> - <script src="../lib/settings.js"></script> - <script src="../lib/find_mode_history.js"></script> - <script src="../content_scripts/mode.js"></script> - <script src="../content_scripts/ui_component.js"></script> - <script src="../content_scripts/link_hints.js"></script> - <script src="../content_scripts/vomnibar.js"></script> - <script src="../content_scripts/scroller.js"></script> - <script src="../content_scripts/marks.js"></script> - <script src="../content_scripts/mode_insert.js"></script> - <script src="../content_scripts/mode_find.js"></script> - <script src="../content_scripts/mode_key_handler.js"></script> - <script src="../content_scripts/mode_visual.js"></script> - <script src="../content_scripts/hud.js"></script> - <script src="../content_scripts/vimium_frontend.js"></script> - <link rel="stylesheet" type="text/css" href="../content_scripts/vimium.css" /> - + <link rel="import" href="vimium_resources.html"> </head> <body> </body> diff --git a/pages/completion_engines.html b/pages/completion_engines.html index 114dc0db..d47bb87b 100644 --- a/pages/completion_engines.html +++ b/pages/completion_engines.html @@ -4,30 +4,7 @@ <!-- We re-use some styling from the options page, so that the look and feel here is similar --> <link rel="stylesheet" type="text/css" href="options.css"> <link rel="stylesheet" type="text/css" href="completion_engines.css"> - <!-- <script src="content_script_loader.js"></script> --> - <!-- NOTE(smblott) Temporarily, do not use the content-script loader (2016-09-27). It appears no longer - to be loading scripts synchronously, and so all dependent pages fail. --> - <script src="../lib/utils.js"></script> - <script src="../lib/keyboard_utils.js"></script> - <script src="../lib/dom_utils.js"></script> - <script src="../lib/rect.js"></script> - <script src="../lib/handler_stack.js"></script> - <script src="../lib/settings.js"></script> - <script src="../lib/find_mode_history.js"></script> - <script src="../content_scripts/mode.js"></script> - <script src="../content_scripts/ui_component.js"></script> - <script src="../content_scripts/link_hints.js"></script> - <script src="../content_scripts/vomnibar.js"></script> - <script src="../content_scripts/scroller.js"></script> - <script src="../content_scripts/marks.js"></script> - <script src="../content_scripts/mode_insert.js"></script> - <script src="../content_scripts/mode_find.js"></script> - <script src="../content_scripts/mode_key_handler.js"></script> - <script src="../content_scripts/mode_visual.js"></script> - <script src="../content_scripts/hud.js"></script> - <script src="../content_scripts/vimium_frontend.js"></script> - <link rel="stylesheet" type="text/css" href="../content_scripts/vimium.css" /> - + <link rel="import" href="vimium_resources.html"> <script src="../background_scripts/completion_engines.js"></script> <script src="completion_engines.js"></script> </head> diff --git a/pages/content_script_loader.coffee b/pages/content_script_loader.coffee deleted file mode 100644 index 5058bb7b..00000000 --- a/pages/content_script_loader.coffee +++ /dev/null @@ -1,28 +0,0 @@ -injectContentScripts = -> - manifest = chrome.runtime.getManifest() - content_scripts = manifest.content_scripts - - insertLocation = document.head.firstChild - - for scriptInfo in content_scripts - continue if scriptInfo.matches.indexOf("<all_urls>") == -1 - - if scriptInfo.js - for script in scriptInfo.js - scriptElement = document.createElement "script" - scriptElement.type = "text/javascript" - scriptElement.async = false # Don't load out of order! - scriptElement.src = chrome.runtime.getURL script - - insertLocation.parentElement.insertBefore scriptElement, insertLocation - - if scriptInfo.css - for style in scriptInfo.css - styleElement = document.createElement "link" - styleElement.rel = "stylesheet" - styleElement.type = "text/css" - styleElement.href = chrome.runtime.getURL style - - insertLocation.parentElement.insertBefore styleElement, insertLocation - -injectContentScripts() diff --git a/pages/help_dialog.html b/pages/help_dialog.html index 1e7fdd80..c23b2ac1 100644 --- a/pages/help_dialog.html +++ b/pages/help_dialog.html @@ -1,30 +1,7 @@ <html> <head> <title>Vimium Help</title> - <!-- <script src="content_script_loader.js"></script> --> - <!-- NOTE(smblott) Temporarily, do not use the content-script loader (2016-09-27). It appears no longer - to be loading scripts synchronously, and so all dependent pages fail. --> - <script src="../lib/utils.js"></script> - <script src="../lib/keyboard_utils.js"></script> - <script src="../lib/dom_utils.js"></script> - <script src="../lib/rect.js"></script> - <script src="../lib/handler_stack.js"></script> - <script src="../lib/settings.js"></script> - <script src="../lib/find_mode_history.js"></script> - <script src="../content_scripts/mode.js"></script> - <script src="../content_scripts/ui_component.js"></script> - <script src="../content_scripts/link_hints.js"></script> - <script src="../content_scripts/vomnibar.js"></script> - <script src="../content_scripts/scroller.js"></script> - <script src="../content_scripts/marks.js"></script> - <script src="../content_scripts/mode_insert.js"></script> - <script src="../content_scripts/mode_find.js"></script> - <script src="../content_scripts/mode_key_handler.js"></script> - <script src="../content_scripts/mode_visual.js"></script> - <script src="../content_scripts/hud.js"></script> - <script src="../content_scripts/vimium_frontend.js"></script> - <link rel="stylesheet" type="text/css" href="../content_scripts/vimium.css" /> - + <link rel="import" href="vimium_resources.html"> <script type="text/javascript" src="ui_component_server.js"></script> <script type="text/javascript" src="help_dialog.js"></script> </head> diff --git a/pages/logging.html b/pages/logging.html index dd05e819..bc4ffb80 100644 --- a/pages/logging.html +++ b/pages/logging.html @@ -1,30 +1,7 @@ <html> <head> <title>Vimium Logging</title> - <!-- <script src="content_script_loader.js"></script> --> - <!-- NOTE(smblott) Temporarily, do not use the content-script loader (2016-09-27). It appears no longer - to be loading scripts synchronously, and so all dependent pages fail. --> - <script src="../lib/utils.js"></script> - <script src="../lib/keyboard_utils.js"></script> - <script src="../lib/dom_utils.js"></script> - <script src="../lib/rect.js"></script> - <script src="../lib/handler_stack.js"></script> - <script src="../lib/settings.js"></script> - <script src="../lib/find_mode_history.js"></script> - <script src="../content_scripts/mode.js"></script> - <script src="../content_scripts/ui_component.js"></script> - <script src="../content_scripts/link_hints.js"></script> - <script src="../content_scripts/vomnibar.js"></script> - <script src="../content_scripts/scroller.js"></script> - <script src="../content_scripts/marks.js"></script> - <script src="../content_scripts/mode_insert.js"></script> - <script src="../content_scripts/mode_find.js"></script> - <script src="../content_scripts/mode_key_handler.js"></script> - <script src="../content_scripts/mode_visual.js"></script> - <script src="../content_scripts/hud.js"></script> - <script src="../content_scripts/vimium_frontend.js"></script> - <link rel="stylesheet" type="text/css" href="../content_scripts/vimium.css" /> - + <link rel="import" href="vimium_resources.html"> <script src="logging.js"></script> <style type="text/css"> body { diff --git a/pages/options.html b/pages/options.html index b24bdec2..92bed6f0 100644 --- a/pages/options.html +++ b/pages/options.html @@ -2,30 +2,7 @@ <head> <title>Vimium Options</title> <link rel="stylesheet" type="text/css" href="options.css"> - <!-- <script src="content_script_loader.js"></script> --> - <!-- NOTE(smblott) Temporarily, do not use the content-script loader (2016-09-27). It appears no longer - to be loading scripts synchronously, and so all dependent pages fail. --> - <script src="../lib/utils.js"></script> - <script src="../lib/keyboard_utils.js"></script> - <script src="../lib/dom_utils.js"></script> - <script src="../lib/rect.js"></script> - <script src="../lib/handler_stack.js"></script> - <script src="../lib/settings.js"></script> - <script src="../lib/find_mode_history.js"></script> - <script src="../content_scripts/mode.js"></script> - <script src="../content_scripts/ui_component.js"></script> - <script src="../content_scripts/link_hints.js"></script> - <script src="../content_scripts/vomnibar.js"></script> - <script src="../content_scripts/scroller.js"></script> - <script src="../content_scripts/marks.js"></script> - <script src="../content_scripts/mode_insert.js"></script> - <script src="../content_scripts/mode_find.js"></script> - <script src="../content_scripts/mode_key_handler.js"></script> - <script src="../content_scripts/mode_visual.js"></script> - <script src="../content_scripts/hud.js"></script> - <script src="../content_scripts/vimium_frontend.js"></script> - <link rel="stylesheet" type="text/css" href="../content_scripts/vimium.css" /> - + <link rel="import" href="vimium_resources.html"> <script type="text/javascript" src="options.js"></script> </head> diff --git a/pages/vimium_resources.html b/pages/vimium_resources.html new file mode 100644 index 00000000..2fad22a2 --- /dev/null +++ b/pages/vimium_resources.html @@ -0,0 +1,24 @@ +<!-- All content scripts (and CSS) listed in the manifest must be listed here too. + These load Vimium on Vimium's internal pages (such as the options page). --> + +<script src="/lib/utils.js"></script> +<script src="/lib/keyboard_utils.js"></script> +<script src="/lib/dom_utils.js"></script> +<script src="/lib/rect.js"></script> +<script src="/lib/handler_stack.js"></script> +<script src="/lib/settings.js"></script> +<script src="/lib/find_mode_history.js"></script> +<script src="/content_scripts/mode.js"></script> +<script src="/content_scripts/ui_component.js"></script> +<script src="/content_scripts/link_hints.js"></script> +<script src="/content_scripts/vomnibar.js"></script> +<script src="/content_scripts/scroller.js"></script> +<script src="/content_scripts/marks.js"></script> +<script src="/content_scripts/mode_insert.js"></script> +<script src="/content_scripts/mode_find.js"></script> +<script src="/content_scripts/mode_key_handler.js"></script> +<script src="/content_scripts/mode_visual.js"></script> +<script src="/content_scripts/hud.js"></script> +<script src="/content_scripts/vimium_frontend.js"></script> + +<link rel="stylesheet" type="text/css" href="/content_scripts/vimium.css" /> diff --git a/tests/unit_tests/commands_test.coffee b/tests/unit_tests/commands_test.coffee index f501a960..c8ded6a9 100644 --- a/tests/unit_tests/commands_test.coffee +++ b/tests/unit_tests/commands_test.coffee @@ -4,12 +4,46 @@ global.Settings = {postUpdateHooks: {}, get: (-> ""), set: ->} {Commands} = require "../../background_scripts/commands.js" context "Key mappings", + setup -> + @testKeySequence = (key, expectedKeyText, expectedKeyLength) -> + keySequence = Commands.parseKeySequence key + assert.equal expectedKeyText, keySequence.join "/" + assert.equal expectedKeyLength, keySequence.length + should "lowercase keys correctly", -> - assert.equal (Commands.normalizeKey '<c-a>'), '<c-a>' - assert.equal (Commands.normalizeKey '<C-a>'), '<c-a>' - assert.equal (Commands.normalizeKey '<C-A>'), '<c-A>' - assert.equal (Commands.normalizeKey '<F12>'), '<f12>' - assert.equal (Commands.normalizeKey '<C-F12>'), '<c-f12>' + @testKeySequence "a", "a", 1 + @testKeySequence "A", "A", 1 + @testKeySequence "ab", "a/b", 2 + + should "parse keys with modifiers", -> + @testKeySequence "<c-a>", "<c-a>", 1 + @testKeySequence "<c-A>", "<c-A>", 1 + @testKeySequence "<c-a><a-b>", "<c-a>/<a-b>", 2 + @testKeySequence "<m-a>", "<m-a>", 1 + + should "normalize with modifiers", -> + # Modifiers should be in alphabetical order. + @testKeySequence "<m-c-a-A>", "<a-c-m-A>", 1 + + should "parse and normalize named keys", -> + @testKeySequence "<space>", "<space>", 1 + @testKeySequence "<Space>", "<space>", 1 + @testKeySequence "<C-Space>", "<c-space>", 1 + @testKeySequence "<f12>", "<f12>", 1 + @testKeySequence "<F12>", "<f12>", 1 + + should "handle angle brackets", -> + @testKeySequence "<", "<", 1 + @testKeySequence ">", ">", 1 + + @testKeySequence "<<", "</<", 2 + @testKeySequence ">>", ">/>", 2 + + @testKeySequence "<>", "</>", 2 + @testKeySequence "<>", "</>", 2 + + @testKeySequence "<<space>", "</<space>", 2 + @testKeySequence "<C->>", "<c->>", 1 context "Validate commands and options", should "have either noRepeat or repeatLimit, but not both", -> |
