From ac90db47aa2671cd663cc6a9cdf783dc30a582e9 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 10 Jan 2015 00:00:11 +0000 Subject: Modes; more changes... - Better comments. - Strip unnecessary handlers for leaving post-find mode. - Simplify passKeys. - focusInput now re-bubbles its triggering keydown event. --- lib/handler_stack.coffee | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) (limited to 'lib') diff --git a/lib/handler_stack.coffee b/lib/handler_stack.coffee index 97e189c5..44c7538b 100644 --- a/lib/handler_stack.coffee +++ b/lib/handler_stack.coffee @@ -14,6 +14,11 @@ class HandlerStack # processing should take place. @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. + @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) -> @@ -30,30 +35,26 @@ class HandlerStack # event's propagation by returning a falsy value, or stop bubbling by returning @stopBubblingAndFalse or # @stopBubblingAndTrue. bubbleEvent: (type, event) -> - # extra is passed to each handler. This allows handlers to pass information down the stack. - extra = {} - # We take a copy of the array, here, in order to avoid interference from concurrent removes (for example, - # to avoid calling the same handler twice). + # We take a copy of the array in order to avoid interference from concurrent removes (for example, to + # avoid calling the same handler twice, because elements have been spliced out of the array by remove). for handler in @stack[..].reverse() - # A handler may have been removed (handler.id == null). - if handler and handler.id + # A handler may have been removed (handler.id == null), so check. + if handler?.id and handler[type] @currentId = handler.id - # A handler can register a handler for type "all", which will be invoked on all events. Such an "all" - # handler will be invoked first. - for func in [ handler.all, handler[type] ] - if func - passThrough = func.call @, event, extra - if not passThrough - DomUtils.suppressEvent(event) if @isChromeEvent event - return false - return true if passThrough == @stopBubblingAndTrue - return false if passThrough == @stopBubblingAndFalse + result = handler[type].call @, event + if not result + DomUtils.suppressEvent(event) if @isChromeEvent event + return false + return true if result == @stopBubblingAndTrue + return false if result == @stopBubblingAndFalse + return @bubbleEvent type, event if result == @restartBubbling true remove: (id = @currentId) -> for i in [(@stack.length - 1)..0] by -1 handler = @stack[i] if handler.id == id + # Mark the handler as removed. handler.id = null @stack.splice(i, 1) break @@ -63,7 +64,9 @@ class HandlerStack isChromeEvent: (event) -> event?.preventDefault? or event?.stopImmediatePropagation? - # Convenience wrappers. + # Convenience wrappers. Handlers must return an approriate value. These are wrappers which handlers can + # use to always return the same value. This then means that the handler itself can be implemented without + # regard to its return value. alwaysContinueBubbling: (handler) -> handler() true -- cgit v1.2.3 From fdcdd0113049042c94b2b56a6b716e2da58b860e Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 10 Jan 2015 08:25:02 +0000 Subject: Modes; instrument for debugging... - Set Mode.debug to true to see mode activation/deactivation on the console. - Use Mode.log() to see a list of currently-active modes. - Use handlerStack.debugOn() to enable debugging of the handler stack. --- lib/dom_utils.coffee | 1 + lib/handler_stack.coffee | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee index 9d7ca867..322188b3 100644 --- a/lib/dom_utils.coffee +++ b/lib/dom_utils.coffee @@ -206,6 +206,7 @@ DomUtils = # Suppress the next keyup event for Escape. suppressKeyupAfterEscape: (handlerStack) -> handlerStack.push + _name: "dom_utils/suppressKeyupAfterEscape" keyup: (event) -> return true unless KeyboardUtils.isEscape event @remove() diff --git a/lib/handler_stack.coffee b/lib/handler_stack.coffee index 44c7538b..22d04941 100644 --- a/lib/handler_stack.coffee +++ b/lib/handler_stack.coffee @@ -3,6 +3,8 @@ root = exports ? window class HandlerStack constructor: -> + @debug = false + @eventNumber = 0 @stack = [] @counter = 0 @@ -22,8 +24,10 @@ class HandlerStack # 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) -> - @stack.push handler handler.id = ++@counter + handler._name ||= "anon-#{@counter}" + @stack.push handler + handler.id # Adds a handler to the bottom of the stack. Returns a unique ID for that handler that can be used to remove # it later. @@ -35,6 +39,7 @@ class HandlerStack # event's propagation by returning a falsy value, or stop bubbling by returning @stopBubblingAndFalse or # @stopBubblingAndTrue. bubbleEvent: (type, event) -> + @eventNumber += 1 # We take a copy of the array in order to avoid interference from concurrent removes (for example, to # avoid calling the same handler twice, because elements have been spliced out of the array by remove). for handler in @stack[..].reverse() @@ -42,6 +47,7 @@ class HandlerStack if handler?.id and handler[type] @currentId = handler.id result = handler[type].call @, event + @logResult type, event, handler, result if @debug if not result DomUtils.suppressEvent(event) if @isChromeEvent event return false @@ -75,5 +81,31 @@ class HandlerStack handler() false + # Debugging. + debugOn: -> @debug = true + debugOff: -> @debug = false + + 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" + label = + switch result + when @stopBubblingAndTrue then "stop/true" + when @stopBubblingAndFalse then "stop/false" + when @restartBubbling then "rebubble" + when true then "continue" + label ||= if result then "continue/truthy" else "suppress" + @log @eventNumber, type, handler._name, label + + logRecords: [] + log: (args...) -> + line = args.join " " + @logRecords.push line + console.log line + + clipLog: -> + Clipboard.copy logRecords.join "\n" + root.HandlerStack = HandlerStack root.handlerStack = new HandlerStack -- cgit v1.2.3 From c554d1fd5b6d81506864516b6f86a14f8672bec5 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 10 Jan 2015 15:05:58 +0000 Subject: Modes; reinstate key blockers: - when the selection is contentEditable - in PostFindMode Restricted to printable characters. --- lib/dom_utils.coffee | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee index 322188b3..fd2427c4 100644 --- a/lib/dom_utils.coffee +++ b/lib/dom_utils.coffee @@ -212,5 +212,8 @@ DomUtils = @remove() false + isPrintable: (event) -> + not (event.metaKey or event.ctrlKey or event.altKey) + root = exports ? window root.DomUtils = DomUtils -- cgit v1.2.3 From 704ae28629154a732e20e16d56b23af265d51b85 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 10 Jan 2015 16:01:40 +0000 Subject: Modes; better printable detection, move to keyboard_utils. --- lib/dom_utils.coffee | 3 --- lib/keyboard_utils.coffee | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee index fd2427c4..322188b3 100644 --- a/lib/dom_utils.coffee +++ b/lib/dom_utils.coffee @@ -212,8 +212,5 @@ DomUtils = @remove() false - isPrintable: (event) -> - not (event.metaKey or event.ctrlKey or event.altKey) - root = exports ? window root.DomUtils = DomUtils diff --git a/lib/keyboard_utils.coffee b/lib/keyboard_utils.coffee index d2a843f9..30d99656 100644 --- a/lib/keyboard_utils.coffee +++ b/lib/keyboard_utils.coffee @@ -55,6 +55,12 @@ KeyboardUtils = # c-[ is mapped to ESC in Vim by default. (event.keyCode == @keyCodes.ESC) || (event.ctrlKey && @getKeyChar(event) == '[') + # TODO. This is probably a poor way of detecting printable characters. However, it shouldn't incorrectly + # identify any of chrome's own keyboard shortcuts as printable. + isPrintable: (event) -> + return false if event.metaKey or event.ctrlKey or event.altKey + @getKeyChar(event)?.length == 1 + KeyboardUtils.init() root = exports ? window -- cgit v1.2.3