diff options
| author | Stephen Blott | 2015-01-18 06:27:38 +0000 |
|---|---|---|
| committer | Stephen Blott | 2015-01-18 10:25:31 +0000 |
| commit | 0e59b99e95e6a4fd3f64fd284e7417ba5f7e22e1 (patch) | |
| tree | 19fddb33de1e00b8024c4cb5c86dc483169da885 | |
| parent | 9cb0f2853a155e39270282e6ed224966afffc61e (diff) | |
| download | vimium-0e59b99e95e6a4fd3f64fd284e7417ba5f7e22e1.tar.bz2 | |
Modes; pre-merge clean up.
| -rw-r--r-- | background_scripts/main.coffee | 11 | ||||
| -rw-r--r-- | content_scripts/link_hints.coffee | 1 | ||||
| -rw-r--r-- | content_scripts/mode.coffee | 15 | ||||
| -rw-r--r-- | content_scripts/mode_find.coffee | 21 | ||||
| -rw-r--r-- | content_scripts/mode_insert.coffee | 38 | ||||
| -rw-r--r-- | content_scripts/vimium_frontend.coffee | 27 | ||||
| -rw-r--r-- | lib/dom_utils.coffee | 8 | ||||
| -rw-r--r-- | lib/handler_stack.coffee | 18 |
8 files changed, 72 insertions, 67 deletions
diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index 83a6b3f8..c1c8dfc8 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -347,7 +347,7 @@ chrome.browserAction.setBadgeBackgroundColor color: [82, 156, 206, 255] setBadge = do -> - current = "" + current = null timer = null updateBadge = (badge) -> -> chrome.browserAction.setBadgeText text: badge (request) -> @@ -355,13 +355,8 @@ setBadge = do -> if badge? and badge != current current = badge clearTimeout timer if timer - if badge == "" - # We set an empty badge immediately. This is the common case when changing tabs. - updateBadge(badge)() - else - # We wait a few milliseconds before setting any other badge. This avoids badge flicker when there are - # rapid changes (e.g. InsertMode is activated by find, followed almost immediately by PostFindMode). - timer = setTimeout updateBadge(badge), 50 + # We wait a few moments. This avoids badge flicker when there are rapid changes. + timer = setTimeout updateBadge(badge), 50 # Updates the browserAction icon to indicate whether Vimium is enabled or disabled on the current page. # Also propagates new enabled/disabled/passkeys state to active window, if necessary. diff --git a/content_scripts/link_hints.coffee b/content_scripts/link_hints.coffee index 3c8240c0..5e41cbbb 100644 --- a/content_scripts/link_hints.coffee +++ b/content_scripts/link_hints.coffee @@ -10,7 +10,6 @@ # # The "name" property below is a short-form name to appear in the link-hints mode name. Debugging only. The # key appears in the mode's badge. -# NOTE(smblott) The use of keys in badges is experimental. It may prove too noisy. # OPEN_IN_CURRENT_TAB = { name: "curr-tab", key: "" } OPEN_IN_NEW_BG_TAB = { name: "bg-tab", key: "B" } diff --git a/content_scripts/mode.coffee b/content_scripts/mode.coffee index 2ff71ca8..a74acfed 100644 --- a/content_scripts/mode.coffee +++ b/content_scripts/mode.coffee @@ -24,8 +24,10 @@ # responds to "focus" events, then push an additional handler: # @push # "focus": (event) => .... -# Any such handlers are removed when the mode is deactivated. +# Such handlers are removed when the mode is deactivated. # +# The following events can be handled: +# keydown, keypress, keyup, click, focus and blur # Debug only. count = 0 @@ -85,8 +87,8 @@ class Mode "click": (event) => @alwaysContinueBubbling => @exit event # Some modes are singletons: there may be at most one instance active at any time. A mode is a singleton - # if @options.singleton is truthy. The value of @options.singleton should be the key the which is - # intended to be unique. New instances deactivate existing instances with the same key. + # if @options.singleton is truthy. The value of @options.singleton should be the key which is intended to + # be unique. New instances deactivate existing instances with the same key. if @options.singleton do => singletons = Mode.singletons ||= {} @@ -99,7 +101,7 @@ class Mode # If @options.trackState is truthy, then the mode mainatins the current state in @enabled and @passKeys, # and calls @registerStateChange() (if defined) whenever the state changes. The mode also tracks the - # keyQueue in @keyQueue. + # current keyQueue in @keyQueue. if @options.trackState @enabled = false @passKeys = "" @@ -115,7 +117,7 @@ class Mode Mode.modes.push @ Mode.updateBadge() - @logStack() + @logModes() # End of Mode constructor. push: (handlers) -> @@ -124,7 +126,6 @@ class Mode unshift: (handlers) -> handlers._name ||= "mode-#{@id}" - handlers._name += "/unshifted" @handlers.push handlerStack.unshift handlers onExit: (handler) -> @@ -160,7 +161,7 @@ class Mode badge: badge.badge # Debugging routines. - logStack: -> + logModes: -> if @debug @log "active modes (top to bottom):" @log " ", mode.id for mode in Mode.modes[..].reverse() diff --git a/content_scripts/mode_find.coffee b/content_scripts/mode_find.coffee index 35352277..dff63949 100644 --- a/content_scripts/mode_find.coffee +++ b/content_scripts/mode_find.coffee @@ -33,6 +33,8 @@ class PostFindMode extends SuppressPrintable super name: "post-find" + # We show a "?" badge, but only while an Escape activates insert mode. + badge: "?" singleton: PostFindMode exitOnBlur: element exitOnClick: true @@ -42,20 +44,23 @@ class PostFindMode extends SuppressPrintable # If the very-next keydown is Escape, then exit immediately, thereby passing subsequent keys to the # underlying insert-mode instance. - self = @ @push _name: "mode-#{@id}/handle-escape" - keydown: (event) -> + keydown: (event) => if KeyboardUtils.isEscape event DomUtils.suppressKeyupAfterEscape handlerStack - self.exit() - false # Suppress event. + @exit() + @suppressEvent else - @remove() - true # Continue bubbling. + handlerStack.remove() + @badge = "" + Mode.updateBadge() + @continueBubbling - # If PostFindMode is active, then we suppress the "I" badge from insert mode. - updateBadge: (badge) -> InsertMode.suppressEvent badge # Always truthy. + updateBadge: (badge) -> + badge.badge ||= @badge + # Suppress the "I" badge from insert mode. + InsertMode.suppressEvent badge # Always truthy. root = exports ? window root.PostFindMode = PostFindMode diff --git a/content_scripts/mode_insert.coffee b/content_scripts/mode_insert.coffee index 123c72be..196f910b 100644 --- a/content_scripts/mode_insert.coffee +++ b/content_scripts/mode_insert.coffee @@ -11,11 +11,18 @@ class InsertMode extends Mode # If truthy, then we were activated by the user (with "i"). @global = options.global + handleKeyEvent = (event) => + return @continueBubbling unless @isActive event + return @stopBubblingAndTrue unless event.type == 'keydown' and KeyboardUtils.isEscape event + DomUtils.suppressKeyupAfterEscape handlerStack + @exit event, event.srcElement + @suppressEvent + defaults = name: "insert" - keydown: (event) => @handleKeydownEvent event - keypress: (event) => @handleKeyEvent event - keyup: (event) => @handleKeyEvent event + keypress: handleKeyEvent + keyup: handleKeyEvent + keydown: handleKeyEvent super extend defaults, options @@ -32,11 +39,10 @@ class InsertMode extends Mode # We can't rely on focus and blur events arriving in the expected order. When the active element # changes, we might get "focus" before "blur". We track the active element in @insertModeLock, and # exit only when that element blurs. - @exit event, target if target == @insertModeLock + @exit event, target if @insertModeLock and target == @insertModeLock "focus": (event) => @alwaysContinueBubbling => if @insertModeLock != event.target and DomUtils.isFocusable event.target - @insertModeLock = event.target - Mode.updateBadge() + @activateOnElement event.target isActive: (event) -> return false if event == InsertMode.suppressedEvent @@ -44,24 +50,18 @@ class InsertMode extends Mode # Some sites (e.g. inbox.google.com) change the contentEditable property on the fly (see #1245); and # unfortunately, the focus event fires *before* the change. Therefore, we need to re-check whether the # active element is contentEditable. - if @insertModeLock != document.activeElement and document.activeElement?.isContentEditable - @insertModeLock = document.activeElement - Mode.updateBadge() + @activateOnElement document.activeElement if document.activeElement?.isContentEditable @insertModeLock != null - handleKeydownEvent: (event) -> - return @continueBubbling unless @isActive event - return @stopBubblingAndTrue unless KeyboardUtils.isEscape event - DomUtils.suppressKeyupAfterEscape handlerStack - @exit event, event.srcElement - @suppressEvent - - # Handles keypress and keyup events. - handleKeyEvent: (event) -> - if @isActive event then @stopBubblingAndTrue else @continueBubbling + activateOnElement: (element) -> + @log "#{@id}: activating (permanent)" if @debug and @permanent + @insertModeLock = element + Mode.updateBadge() exit: (_, target) -> + # Note: target == undefined, here, is required only for tests. if (target and target == @insertModeLock) or @global or target == undefined + @log "#{@id}: deactivating (permanent)" if @debug and @permanent and @insertModeLock @insertModeLock = null if target and DomUtils.isFocusable target # Remove the focus, so the user can't just get back into insert mode by typing in the same input box. diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 0a034e28..6a26a133 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -117,10 +117,11 @@ initializePreDomReady = -> keypress: (event) => onKeypress.call @, event keyup: (event) => onKeyup.call @, event - # Install the permanent modes and handlers. The permanent insert mode operates only when focusable/editable - # elements are active. + Scroller.init settings + + # Install the permanent modes. The permanently-installed insert mode tracks focus/blur events, and + # activates/deactivates itself accordingly. new NormalMode - Scroller.init settings new PassKeysMode new InsertMode @@ -242,8 +243,6 @@ enterInsertModeIfElementIsFocused = -> enterInsertModeWithoutShowingIndicator(document.activeElement) onDOMActivate = (event) -> handlerStack.bubbleEvent 'DOMActivate', event -onFocus = (event) -> handlerStack.bubbleEvent 'focus', event -onBlur = (event) -> handlerStack.bubbleEvent 'blur', event executePageCommand = (request) -> return unless frameId == request.frameId @@ -326,8 +325,7 @@ extend window, HUD.showForDuration("Yanked URL", 1000) enterInsertMode: -> - new InsertMode - global: true + new InsertMode global: true enterVisualMode: => new VisualMode() @@ -804,17 +802,16 @@ executeFind = (query, options) -> -> document.addEventListener("selectionchange", restoreDefaultSelectionHighlight, true) 0) + # We are either in normal mode ("n"), or find mode ("/"). We are not in insert mode. Nevertheless, if a + # previous find landed in an editable element, then that element may still be activated. In this case, we + # don't want to leave it behind (see #1412). + if document.activeElement and DomUtils.isEditable document.activeElement + if not DomUtils.isSelected document.activeElement + document.activeElement.blur() + # we need to save the anchor node here because <esc> seems to nullify it, regardless of whether we do # preventDefault() findModeAnchorNode = document.getSelection().anchorNode - - # TODO(smblott). Disabled. This is the wrong test. Should be reinstated when we have the right test, which - # looks like it should be "isSelected" from #1431. - # # If the anchor node not a descendent of the active element, then blur the active element. We don't want to - # # leave behind an inappropriate active element. This fixes #1412. - # if document.activeElement and not DomUtils.isDOMDescendant document.activeElement, findModeAnchorNode - # document.activeElement.blur() - result restoreDefaultSelectionHighlight = -> document.body.classList.remove("vimiumFindMode") diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee index 322188b3..e1f1f442 100644 --- a/lib/dom_utils.coffee +++ b/lib/dom_utils.coffee @@ -166,6 +166,14 @@ DomUtils = node = node.parentNode false + # True if element contains the active selection range. + isSelected: (element) -> + if element.isContentEditable + node = document.getSelection()?.anchorNode + node and @isDOMDescendant element, node + else + element.selectionStart? and element.selectionEnd? and element.selectionStart != element.selectionEnd + simulateSelect: (element) -> element.focus() # When focusing a textbox, put the selection caret at the end of the textbox's contents. diff --git a/lib/handler_stack.coffee b/lib/handler_stack.coffee index fddc45db..76d835b7 100644 --- a/lib/handler_stack.coffee +++ b/lib/handler_stack.coffee @@ -13,25 +13,24 @@ class HandlerStack @stopBubblingAndTrue = new Object() # A handler should return this value to indicate that the event has been consumed, and no further - # processing should take place. + # processing should take place. The event does not propagate to the underlying page. @stopBubblingAndFalse = new Object() # A handler should return this value to indicate that bubbling should be restarted. Typically, this is - # used when, while bubbling an event, a new mode is pushed onto the stack. See `focusInput` for an - # example. + # used when, while bubbling an event, a new mode is pushed onto the stack. @restartBubbling = new Object() # Adds a handler to the top of the stack. Returns a unique ID for that handler that can be used to remove it # later. push: (handler) -> - handler.id = ++@counter handler._name ||= "anon-#{@counter}" @stack.push handler - handler.id + handler.id = ++@counter - # Adds a handler to the bottom of the stack. Returns a unique ID for that handler that can be used to remove - # it later. + # As above, except the new handler is added to the bottom of the stack. unshift: (handler) -> + handler._name ||= "anon-#{@counter}" + handler._name += "/unshift" @stack.unshift handler handler.id = ++@counter @@ -84,8 +83,9 @@ class HandlerStack # Debugging. logResult: (type, event, handler, result) -> # FIXME(smblott). Badge updating is too noisy, so we filter it out. However, we do need to look at how - # many badge update events are happening. It seems to be more than necessary. - return if type == "updateBadge" + # many badge update events are happening. It seems to be more than necessary. We also filter out + # registerKeyQueue as unnecessarily noisy and not particularly helpful. + return if type in [ "updateBadge", "registerKeyQueue" ] label = switch result when @stopBubblingAndTrue then "stop/true" |
