Continue to dispatch CustomEvent instances in CommandRegistry

This changes the approach used to synthesize an event to dispatch. Since
we can no longer point to the original event as our prototype, we build
a new CustomEvent instance and override select properties with
Object.defineProperty. This preserves the contract with event handlers
by giving them a true CustomEvent instance.
This commit is contained in:
Nathan Sobo 2015-08-24 16:15:48 -06:00 committed by Thomas Johansen
parent 27f1af380e
commit aed785924d

View File

@ -175,17 +175,8 @@ class CommandRegistry
# * `commandName` {String} indicating the name of the command to dispatch.
dispatch: (target, commandName, detail) ->
event = new CustomEvent(commandName, {bubbles: true, detail})
eventWithTarget = Object.create {},
target: value: target
preventDefault: value: ->
stopPropagation: value: ->
stopImmediatePropagation: value: ->
# NOTE: In Chrome 43, Object.create doesn't work well with CustomEvent;
# However utilizing _.defaults here doesn't really do anything since all properties have been
# moved from "own" to the prototype, so we should update this to fully shadowing the properties
# from Event.prototype (+ detail property from CustomEvent).
eventWithTarget = _.defaults(eventWithTarget, event)
@handleCommandEvent(eventWithTarget)
Object.defineProperty(event, 'target', value: target)
@handleCommandEvent(event)
# Public: Invoke the given callback before dispatching a command event.
#
@ -213,37 +204,36 @@ class CommandRegistry
@selectorBasedListenersByCommandName[commandName] = listeners.slice()
return
handleCommandEvent: (originalEvent) =>
handleCommandEvent: (event) =>
propagationStopped = false
immediatePropagationStopped = false
matched = false
currentTarget = originalEvent.target
currentTarget = event.target
{preventDefault, stopPropagation, stopImmediatePropagation, abortKeyBinding} = event
syntheticEvent = Object.create {},
eventPhase: value: Event.BUBBLING_PHASE
currentTarget: get: -> currentTarget
target: value: currentTarget
preventDefault: value: ->
originalEvent.preventDefault()
stopPropagation: value: ->
originalEvent.stopPropagation()
dispatchedEvent = new CustomEvent(event.type, {bubbles: true, detail: event.detail})
Object.defineProperty dispatchedEvent, 'eventPhase', value: Event.BUBBLING_PHASE
Object.defineProperty dispatchedEvent, 'currentTarget', get: -> currentTarget
Object.defineProperty dispatchedEvent, 'target', value: currentTarget
Object.defineProperty dispatchedEvent, 'preventDefault', value: ->
event.preventDefault()
Object.defineProperty dispatchedEvent, 'stopPropagation', value: ->
event.stopPropagation()
propagationStopped = true
stopImmediatePropagation: value: ->
originalEvent.stopImmediatePropagation()
Object.defineProperty dispatchedEvent, 'stopImmediatePropagation', value: ->
event.stopImmediatePropagation()
propagationStopped = true
immediatePropagationStopped = true
abortKeyBinding: value: ->
originalEvent.abortKeyBinding?()
# NOTE: See similar scenario in ::dispatch.
syntheticEvent = _.defaults(syntheticEvent, originalEvent)
Object.defineProperty dispatchedEvent, 'abortKeyBinding', value: ->
event.abortKeyBinding?()
@emitter.emit 'will-dispatch', syntheticEvent
@emitter.emit 'will-dispatch', dispatchedEvent
loop
listeners = @inlineListenersByCommandName[originalEvent.type]?.get(currentTarget) ? []
listeners = @inlineListenersByCommandName[event.type]?.get(currentTarget) ? []
if currentTarget.webkitMatchesSelector?
selectorBasedListeners =
(@selectorBasedListenersByCommandName[originalEvent.type] ? [])
(@selectorBasedListenersByCommandName[event.type] ? [])
.filter (listener) -> currentTarget.webkitMatchesSelector(listener.selector)
.sort (a, b) -> a.compare(b)
listeners = listeners.concat(selectorBasedListeners)
@ -252,7 +242,7 @@ class CommandRegistry
for listener in listeners
break if immediatePropagationStopped
listener.callback.call(currentTarget, syntheticEvent)
listener.callback.call(currentTarget, dispatchedEvent)
break if currentTarget is window
break if propagationStopped