diff options
Diffstat (limited to 'content_scripts/mode_insert.coffee')
| -rw-r--r-- | content_scripts/mode_insert.coffee | 53 |
1 files changed, 27 insertions, 26 deletions
diff --git a/content_scripts/mode_insert.coffee b/content_scripts/mode_insert.coffee index 9a2d5ce1..144b0be6 100644 --- a/content_scripts/mode_insert.coffee +++ b/content_scripts/mode_insert.coffee @@ -1,5 +1,5 @@ -# This mode is installed when insert mode is active. +# This mode is installed only when insert mode is active. class InsertMode extends Mode constructor: (options = {}) -> defaults = @@ -11,10 +11,10 @@ class InsertMode extends Mode keyup: (event) => @stopBubblingAndTrue exitOnEscape: true blurOnExit: true + targetElement: null - options = extend defaults, options - options.exitOnBlur = options.targetElement || null - super options + options.exitOnBlur ||= options.targetElement + super extend defaults, options triggerSuppressor.suppress() exit: (event = null) -> @@ -34,8 +34,8 @@ class InsertMode extends Mode # - On a keydown event in a contentEditable element. # - When a focusable element receives the focus. # -# The trigger can be suppressed via triggerSuppressor; see InsertModeBlocker, below. -# This mode is permanently installed fairly low down on the handler stack. +# The trigger can be suppressed via triggerSuppressor; see InsertModeBlocker, below. This mode is permanently +# installed (just above normal mode and passkeys mode) on the handler stack. class InsertModeTrigger extends Mode constructor: -> super @@ -44,7 +44,7 @@ class InsertModeTrigger extends Mode triggerSuppressor.unlessSuppressed => # Some sites (e.g. inbox.google.com) change the contentEditable attribute on the fly (see #1245); # and unfortunately, the focus event happens *before* the change is made. Therefore, we need to - # check again whether the active element is contentEditable. + # check (on every keydown) whether the active element is contentEditable. return @continueBubbling unless document.activeElement?.isContentEditable new InsertMode targetElement: document.activeElement @@ -58,7 +58,7 @@ class InsertModeTrigger extends Mode new InsertMode targetElement: event.target - # We may already have focussed an input, so check. + # We may have already focussed an input element, so check. if document.activeElement and DomUtils.isEditable document.activeElement new InsertMode targetElement: document.activeElement @@ -66,12 +66,13 @@ class InsertModeTrigger extends Mode # Used by InsertModeBlocker to suppress InsertModeTrigger; see below. triggerSuppressor = new Utils.Suppressor true # Note: true == @continueBubbling -# Suppresses InsertModeTrigger. This is used by various modes (usually by inheritance) to prevent +# Suppresses InsertModeTrigger. This is used by various modes (usually via inheritance) to prevent # unintentionally dropping into insert mode on focusable elements. class InsertModeBlocker extends Mode constructor: (options = {}) -> triggerSuppressor.suppress() options.name ||= "insert-blocker" + # See "click" handler below for an explanation of options.onClickMode. options.onClickMode ||= InsertMode super options @onExit -> triggerSuppressor.unsuppress() @@ -79,27 +80,29 @@ class InsertModeBlocker extends Mode @push "click": (event) => @alwaysContinueBubbling => - # The user knows best; so, if the user clicks on something, we get out of the way. + # The user knows best; so, if the user clicks on something, the insert-mode blocker gets out of the + # way. @exit event - # However, there's a corner case. If the active element is focusable, then we would have been in - # insert mode had we not been blocking the trigger. Now, clicking on the element will not generate - # a new focus event, so the insert-mode trigger will not fire. We have to handle this case - # specially. @options.onClickMode is the mode to use. + # However, there's a corner case. If the active element is focusable, then, had we not been + # blocking the trigger, we would already have been in insert mode. Now, a click on that element + # will not generate a new focus event, so the insert-mode trigger will not fire. We have to handle + # this case specially. @options.onClickMode specifies the mode to use (by default, insert mode). if document.activeElement and - event.target == document.activeElement and DomUtils.isEditable document.activeElement + event.target == document.activeElement and DomUtils.isEditable document.activeElement new @options.onClickMode targetElement: document.activeElement # There's some unfortunate feature interaction with chrome's content editable handling. If the selection is # content editable and a descendant of the active element, then chrome focuses it on any unsuppressed keyboard -# events. This has the unfortunate effect of dropping us unintentally into insert mode. See #1415. -# This mode sits near the bottom of the handler stack and suppresses keyboard events if: +# event. This has the unfortunate effect of dropping us unintentally into insert mode. See #1415. +# A single instance of this mode sits near the bottom of the handler stack and suppresses keyboard events if: # - they haven't been handled by any other mode (so not by normal mode, passkeys mode, insert mode, and so -# on), and +# on), # - the selection is content editable, and # - the selection is a descendant of the active element. # This should rarely fire, typically only on fudged keypresses in normal mode. And, even then, only in the -# circumstances outlined above. So it shouldn't normally block other extensions or the page itself from +# circumstances outlined above. So, we shouldn't usually be blocking keyboard events for other extensions or +# the page itself. # handling keyboard events. new class ContentEditableTrap extends Mode constructor: -> @@ -109,17 +112,15 @@ new class ContentEditableTrap extends Mode keypress: (event) => @handle => @suppressEvent keyup: (event) => @handle => @suppressEvent - # True if the selection is content editable and a descendant of the active element. In this situation, - # chrome unilaterally focuses the element containing the anchor, dropping us into insert mode. + handle: (func) -> if @isContentEditableFocused() then func() else @continueBubbling + + # True if the selection is content editable and a descendant of the active element. isContentEditableFocused: -> element = document.getSelection()?.anchorNode?.parentElement - return element?.isContentEditable? and - document.activeElement? and + return element?.isContentEditable and + document.activeElement and DomUtils.isDOMDescendant document.activeElement, element - handle: (func) -> - if @isContentEditableFocused() then func() else @continueBubbling - root = exports ? window root.InsertMode = InsertMode root.InsertModeTrigger = InsertModeTrigger |
