aboutsummaryrefslogtreecommitdiffstats
path: root/content_scripts/mode.coffee
diff options
context:
space:
mode:
Diffstat (limited to 'content_scripts/mode.coffee')
-rw-r--r--content_scripts/mode.coffee87
1 files changed, 67 insertions, 20 deletions
diff --git a/content_scripts/mode.coffee b/content_scripts/mode.coffee
index 8e37ee36..0fcab675 100644
--- a/content_scripts/mode.coffee
+++ b/content_scripts/mode.coffee
@@ -38,18 +38,21 @@
# myMode.exit() # externally triggered.
#
-# For debug only; to be stripped out.
+# For debug only.
count = 0
class Mode
- # If this is true, then we generate a trace of modes being activated and deactivated on the console.
- @debug = true
+ # If Mode.debug is true, then we generate a trace of modes being activated and deactivated on the console, along
+ # with a list of the currently active modes.
+ debug: true
+ @modes: []
- # Constants; readable shortcuts for event-handler return values.
+ # Constants; short, readable names for handlerStack event-handler return values.
continueBubbling: true
suppressEvent: false
stopBubblingAndTrue: handlerStack.stopBubblingAndTrue
stopBubblingAndFalse: handlerStack.stopBubblingAndFalse
+ restartBubbling: handlerStack.restartBubbling
constructor: (@options={}) ->
@handlers = []
@@ -59,7 +62,8 @@ class Mode
@name = @options.name || "anonymous"
@count = ++count
- console.log @count, "create:", @name if Mode.debug
+ @id = "#{@name}-#{@count}"
+ @logger "activate:", @id if @debug
@push
keydown: @options.keydown || null
@@ -79,6 +83,7 @@ class Mode
# Note. This handler ends up above the mode's own key handlers on the handler stack, so it takes
# priority.
@push
+ _name: "mode-#{@id}/exitOnEscape"
"keydown": (event) =>
return @continueBubbling unless KeyboardUtils.isEscape event
@exit event
@@ -89,6 +94,7 @@ class Mode
# loses the focus.
if @options.exitOnBlur
@push
+ _name: "mode-#{@id}/exitOnBlur"
"blur": (event) => @alwaysContinueBubbling => @exit() if event.srcElement == @options.exitOnBlur
# If @options.trackState is truthy, then the mode mainatins the current state in @enabled and @passKeys,
@@ -97,6 +103,7 @@ class Mode
@enabled = false
@passKeys = ""
@push
+ _name: "mode-#{@id}/registerStateChange"
"registerStateChange": ({ enabled: enabled, passKeys: passKeys }) =>
@alwaysContinueBubbling =>
if enabled != @enabled or passKeys != @passKeys
@@ -104,35 +111,43 @@ class Mode
@passKeys = passKeys
@registerStateChange?()
- # If @options.trapAllKeyboardEvents is truthy, then it should be an element. All keyboard events on that
- # element are suppressed *after* bubbling the event down the handler stack. This prevents such events
- # from propagating to other extensions or the host page.
- if @options.trapAllKeyboardEvents
- @unshift
- keydown: (event) => @alwaysContinueBubbling =>
- DomUtils.suppressPropagation event if event.srcElement == @options.trapAllKeyboardEvents
- keypress: (event) => @alwaysContinueBubbling =>
- DomUtils.suppressEvent event if event.srcElement == @options.trapAllKeyboardEvents
- keyup: (event) => @alwaysContinueBubbling =>
- DomUtils.suppressPropagation event if event.srcElement == @options.trapAllKeyboardEvents
+ # If @options.suppressPrintableEvents is truthy, then it should be an element. All printable keypress
+ # events on that element are suppressed, if necessary. They are suppressed *after* bubbling down the
+ # handler stack and finding no handler. This is used by PostFindMode to protect active, editable
+ # elements.
+ if @options.suppressPrintableEvents
+ @push
+ _name: "mode-#{@id}/suppressPrintableEvents"
+ keypress: (event) =>
+ @alwaysContinueBubbling =>
+ if event.srcElement == @options.suppressPrintableEvents
+ if KeyboardUtils.isPrintable(event)
+ event.vimium_suppress_event = true
Mode.updateBadge() if @badge
- # End of Mode.constructor().
+ Mode.modes.push @
+ @log() if @debug
+ # handlerStack.debugOn()
+ # End of Mode constructor.
push: (handlers) ->
+ handlers._name ||= "mode-#{@id}"
@handlers.push handlerStack.push handlers
unshift: (handlers) ->
- @handlers.unshift handlerStack.push handlers
+ handlers._name ||= "mode-#{@id}"
+ handlers._name += "/unshifted"
+ @handlers.push handlerStack.unshift handlers
onExit: (handler) ->
@exitHandlers.push handler
exit: ->
if @modeIsActive
- console.log @count, "exit:", @name if Mode.debug
+ @logger "deactivate:", @id if @debug
handler() for handler in @exitHandlers
handlerStack.remove handlerId for handlerId in @handlers
+ Mode.modes = Mode.modes.filter (mode) => mode != @
Mode.updateBadge()
@modeIsActive = false
@@ -175,11 +190,25 @@ class Mode
# suppress badge updates while exiting any existing active singleton. This prevents the badge from
# flickering in some cases.
Mode.badgeSuppressor.runSuppresed =>
- singletons[key].exit() if singletons[key]
+ if singletons[key]
+ @logger "singleton:", "deactivating #{singletons[key].id}" if @debug
+ singletons[key].exit()
singletons[key] = @
@onExit => delete singletons[key] if singletons[key] == @
+ # Debugging routines.
+ log: ->
+ if Mode.modes.length == 0
+ @logger "It looks like debugging is not enabled in modes.coffee."
+ else
+ @logger "active modes (top to bottom), current: #{@id}"
+ for mode in Mode.modes[..].reverse()
+ @logger " ", mode.id
+
+ logger: (args...) ->
+ handlerStack.log args...
+
# BadgeMode is a pseudo mode for triggering badge updates on focus changes and state updates. It sits at the
# bottom of the handler stack, and so it receives state changes *after* all other modes, and can override the
# badge choice of the other active modes.
@@ -190,7 +219,11 @@ new class BadgeMode extends Mode
name: "badge"
trackState: true
+ # FIXME(smblott) BadgeMode is currently triggering and updateBadge event on every focus event. That's a
+ # lot, considerably more than is necessary. Really, it only needs to trigger when we change frame, or
+ # when we change tab.
@push
+ _name: "mode-#{@id}/focus"
"focus": => @alwaysContinueBubbling -> Mode.updateBadge()
chooseBadge: (badge) ->
@@ -200,5 +233,19 @@ new class BadgeMode extends Mode
registerStateChange: ->
Mode.updateBadge()
+# KeySuppressor is a pseudo mode (near the bottom of the stack) which suppresses keyboard events tagged with
+# the "vimium_suppress_event" property. This allows modes higher up in the stack to tag events for
+# suppression, but only after verifying that no other mode (notably, normal mode) wants to handle the event.
+# Note. We also create the the one-and-only instance, here.
+new class KeySuppressor extends Mode
+ constructor: ->
+ super
+ name: "key-suppressor"
+ keydown: (event) => @handle event
+ keypress: (event) => @handle event
+ keyup: (event) => @handle event
+
+ handle: (event) -> if event.vimium_suppress_event then @suppressEvent else @continueBubbling
+
root = exports ? window
root.Mode = Mode