aboutsummaryrefslogtreecommitdiffstats
path: root/content_scripts/mode_insert.coffee
diff options
context:
space:
mode:
Diffstat (limited to 'content_scripts/mode_insert.coffee')
-rw-r--r--content_scripts/mode_insert.coffee171
1 files changed, 83 insertions, 88 deletions
diff --git a/content_scripts/mode_insert.coffee b/content_scripts/mode_insert.coffee
index 5a0ac9eb..32994aef 100644
--- a/content_scripts/mode_insert.coffee
+++ b/content_scripts/mode_insert.coffee
@@ -1,107 +1,102 @@
-class InsertMode extends Mode
- insertModeActive: false
- insertModeLock: null
-
- # Input or text elements are considered focusable and able to receieve their own keyboard events, and will
- # enter insert mode if focused. Also note that the "contentEditable" attribute can be set on any element
- # which makes it a rich text editor, like the notes on jjot.com.
- isEditable: (element) ->
- return true if element.isContentEditable
- nodeName = element.nodeName?.toLowerCase()
- # Use a blacklist instead of a whitelist because new form controls are still being implemented for html5.
- if nodeName == "input" and element.type not in ["radio", "checkbox"]
- return true
- nodeName in ["textarea", "select"]
-
- # Embedded elements like Flash and quicktime players can obtain focus.
- isEmbed: (element) ->
- element.nodeName?.toLowerCase() in ["embed", "object"]
-
- isFocusable: (element) ->
- @isEditable(element) or @isEmbed element
-
- # Check whether insert mode is active. Also, activate insert mode if the current element is content
- # editable (and the event is not suppressed).
- isActiveOrActivate: (event) ->
- return true if @insertModeActive
- return false if event.suppressKeydownTrigger
- # Some sites (e.g. inbox.google.com) change the contentEditable attribute on the fly (see #1245); and
- # unfortunately, isEditable() is called *before* the change is made. Therefore, we need to re-check
- # whether the active element is contentEditable.
- @activate() if document.activeElement?.isContentEditable
- @insertModeActive
-
- activate: (target=null) ->
- unless @insertModeActive
- @insertModeActive = true
- @insertModeLock = target
- @badge = "I"
- Mode.updateBadge()
-
- deactivate: ->
- if @insertModeActive
- @insertModeActive = false
- @insertModeLock = null
- @badge = ""
- Mode.updateBadge()
-
- exit: (event) ->
- if event?.source == ExitOnEscapeMode
- element = event?.event?.srcElement
- if element? and @isFocusable element
+# Input or text elements are considered focusable and able to receieve their own keyboard events, and will
+# enter insert mode if focused. Also note that the "contentEditable" attribute can be set on any element
+# which makes it a rich text editor, like the notes on jjot.com.
+isEditable =(element) ->
+ return true if element.isContentEditable
+ nodeName = element.nodeName?.toLowerCase()
+ # Use a blacklist instead of a whitelist because new form controls are still being implemented for html5.
+ if nodeName == "input" and element.type not in ["radio", "checkbox"]
+ return true
+ nodeName in ["textarea", "select"]
+
+# Embedded elements like Flash and quicktime players can obtain focus.
+isEmbed =(element) ->
+ element.nodeName?.toLowerCase() in ["embed", "object"]
+
+isFocusable =(element) ->
+ isEditable(element) or isEmbed element
+
+class InsertMode extends ConstrainedMode
+
+ constructor: (@insertModeLock=null) ->
+ super @insertModeLock, InsertMode,
+ name: "insert"
+ badge: "I"
+ keydown: (event) => @stopBubblingAndTrue
+ keypress: (event) => @stopBubblingAndTrue
+ keyup: (event) => @stopBubblingAndTrue
+
+ @push
+ focus: (event, extra) =>
+ handlerStack.alwaysContinueBubbling =>
+ # Inform InsertModeTrigger that InsertMode is already active.
+ extra.insertModeActive = true
+
+ Mode.updateBadge()
+
+ exit: (event=null) ->
+ if event?.source == ExitOnEscapeMode and event?.event?.srcElement?
+ element = event.event.srcElement
+ if isFocusable element
# Remove the focus so the user can't just get himself back into insert mode by typing in the same
# input box.
# NOTE(smblott, 2014/12/22) Including embeds for .blur() here is experimental. It appears to be the
# right thing to do for most common use cases. However, it could also cripple flash-based sites and
# games. See discussion in #1211 and #1194.
element.blur()
- @deactivate()
+ super()
+# Trigger insert mode:
+# - On keydown event in a contentEditable element.
+# - When a focusable element receives the focus.
+# Can be suppressed by setting extra.suppressInsertModeTrigger.
+class InsertModeTrigger extends Mode
constructor: ->
super
- name: "insert"
- keydown: (event) =>
- return @continueBubbling unless @isActiveOrActivate event
- return @stopBubblingAndTrue unless KeyboardUtils.isEscape event
- # We're in insert mode, and now exiting.
- if event.srcElement? and @isFocusable event.srcElement
- # Remove the focus so the user can't just get himself back into insert mode by typing in the same
- # input box.
- # NOTE(smblott, 2014/12/22) Including embeds for .blur() here is experimental. It appears to be the
- # right thing to do for most common use cases. However, it could also cripple flash-based sites and
- # games. See discussion in #1211 and #1194.
- event.srcElement.blur()
- @deactivate()
- @suppressEvent
- keypress: => if @insertModeActive then @stopBubblingAndTrue else @continueBubbling
- keyup: => if @insertModeActive then @stopBubblingAndTrue else @continueBubbling
+ name: "insert-trigger"
+ keydown: (event, extra) =>
+ handlerStack.alwaysContinueBubbling =>
+ unless extra.suppressInsertModeTrigger?
+ # Some sites (e.g. inbox.google.com) change the contentEditable attribute on the fly (see #1245); and
+ # unfortunately, isEditable() is called *before* the change is made. Therefore, we need to check
+ # whether the active element is contentEditable.
+ new InsertMode() if document.activeElement?.isContentEditable
@push
- focus: (event) =>
- handlerStack.alwaysContinueBubbling =>
- if not @insertModeActive and @isFocusable event.target
- @activate event.target
- blur: (event) =>
+ focus: (event, extra) =>
handlerStack.alwaysContinueBubbling =>
- if @insertModeActive and event.target == @insertModeLock
- @deactivate()
+ unless extra.suppressInsertModeTrigger?
+ new InsertMode event.target if isFocusable event.target
- # We may already have focussed something, so check, so check.
- @activate document.activeElement if document.activeElement and @isFocusable document.activeElement
+ # We may already have focussed something, so check.
+ new InsertMode document.activeElement if document.activeElement and isFocusable document.activeElement
- # Used to prevent keydown events from triggering insert mode (following find).
- # FIXME(smblott) This is a hack.
- @suppressKeydownTrigger: (event) ->
- event.suppressKeydownTrigger = true
+ @suppress: (extra) ->
+ extra.suppressInsertModeTrigger = true
-# Activate this mode to prevent a focused, editable element from triggering insert mode.
-class InsertModeSuppressFocusTrigger extends Mode
- constructor: ->
- super {name: "suppress-insert-mode-focus-trigger"}
- @push
- focus: => @suppressEvent
+# Disables InsertModeTrigger. Used by find mode to prevent unintentionally dropping into insert mode on
+# focusable elements.
+# If @element is provided, then don't block focus events, and block keydown events only on the indicated
+# element.
+class InsertModeBlocker extends SingletonMode
+ constructor: (singleton=InsertModeBlocker, @element=null, options={}) ->
+ options.name ||= "insert-blocker"
+ super singleton, options
+
+ unless @element?
+ @push
+ focus: (event, extra) =>
+ handlerStack.alwaysContinueBubbling =>
+ InsertModeTrigger.suppress extra
+
+ if @element?.isContentEditable
+ @push
+ keydown: (event, extra) =>
+ handlerStack.alwaysContinueBubbling =>
+ InsertModeTrigger.suppress extra if event.srcElement == @element
root = exports ? window
root.InsertMode = InsertMode
-root.InsertModeSuppressFocusTrigger = InsertModeSuppressFocusTrigger
+root.InsertModeTrigger = InsertModeTrigger
+root.InsertModeBlocker = InsertModeBlocker