aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/handler_stack.coffee32
1 files changed, 19 insertions, 13 deletions
diff --git a/lib/handler_stack.coffee b/lib/handler_stack.coffee
index f05413d3..c8e9a035 100644
--- a/lib/handler_stack.coffee
+++ b/lib/handler_stack.coffee
@@ -14,24 +14,24 @@ class HandlerStack
# processing should take place.
@stopBubblingAndFalse = new Object()
- genId: -> @counter = ++@counter
-
# Adds a handler to the stack. Returns a unique ID for that handler that can be used to remove it later.
push: (handler) ->
- handler.id = @genId()
@stack.push handler
- handler.id
+ handler.id = ++@counter
- # Called whenever we receive a key event. Each individual handler has the option to stop the event's
- # propagation by returning a falsy value.
+ # Called whenever we receive a key or other event. Each individual handler has the option to stop the
+ # 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 = {}
for i in [(@stack.length - 1)..0] by -1
handler = @stack[i]
# We need to check for existence of handler because the last function call may have caused the release
# of more than one handler.
- if handler && handler[type]
+ if handler and handler.id and handler[type]
@currentId = handler.id
- passThrough = handler[type].call(@, event)
+ passThrough = handler[type].call @, event, extra
if not passThrough
DomUtils.suppressEvent(event) if @isChromeEvent event
return false
@@ -40,11 +40,17 @@ class HandlerStack
true
remove: (id = @currentId) ->
- for i in [(@stack.length - 1)..0] by -1
- handler = @stack[i]
- if handler.id == id
- @stack.splice(i, 1)
- break
+ if 0 < @stack.length and @stack[@stack.length-1].id == id
+ # A common case is to remove the handler at the top of the stack. And we can this very efficiently.
+ # Tests suggest that this case arises more than half of the time.
+ @stack.pop().id = null
+ else
+ # Otherwise, we'll build a new stack. This is better than splicing the existing stack since at can't
+ # interfere with any concurrent bubbleEvent.
+ @stack = @stack.filter (handler) ->
+ # Mark this handler as removed (for any active bubbleEvent call).
+ handler.id = null if handler.id == id
+ handler?.id?
# The handler stack handles chrome events (which may need to be suppressed) and internal (fake) events.
# This checks whether that the event at hand is a chrome event.