mirror of
https://github.com/atom/atom.git
synced 2026-01-23 13:58:08 -05:00
Order multiple matching listeners by selector specificity
This commit is contained in:
@@ -31,6 +31,7 @@ describe "CommandRegistry", ->
|
||||
|
||||
it "invokes callbacks with selectors matching ancestors of the target", ->
|
||||
calls = []
|
||||
|
||||
registry.add 'command', '.child', (event) ->
|
||||
expect(this).toBe child
|
||||
expect(event.target).toBe grandchild
|
||||
@@ -45,3 +46,14 @@ describe "CommandRegistry", ->
|
||||
|
||||
grandchild.dispatchEvent(new CustomEvent('command', bubbles: true))
|
||||
expect(calls).toEqual ['child', 'parent']
|
||||
|
||||
it "orders multiple matching listeners for an element by selector specificity", ->
|
||||
child.classList.add('foo', 'bar')
|
||||
calls = []
|
||||
|
||||
registry.add 'command', '.foo.bar', -> calls.push('.foo.bar')
|
||||
registry.add 'command', '.foo', -> calls.push('.foo')
|
||||
registry.add 'command', '.bar', -> calls.push('.bar') # specificity ties favor commands added later, like CSS
|
||||
|
||||
grandchild.dispatchEvent(new CustomEvent('command', bubbles: true))
|
||||
expect(calls).toEqual ['.foo.bar', '.bar', '.foo']
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
{specificity} = require 'clear-cut'
|
||||
|
||||
SequenceCount = 0
|
||||
SpecificityCache = {}
|
||||
|
||||
module.exports =
|
||||
class CommandRegistry
|
||||
constructor: (@rootNode) ->
|
||||
@@ -8,7 +13,7 @@ class CommandRegistry
|
||||
@rootNode.addEventListener(commandName, @dispatchCommand, true)
|
||||
@listenersByCommandName[commandName] = []
|
||||
|
||||
@listenersByCommandName[commandName].push({selector, callback})
|
||||
@listenersByCommandName[commandName].push(new CommandListener(selector, callback))
|
||||
|
||||
dispatchCommand: (event) =>
|
||||
syntheticEvent = Object.create event,
|
||||
@@ -17,9 +22,22 @@ class CommandRegistry
|
||||
|
||||
currentTarget = event.target
|
||||
loop
|
||||
for listener in @listenersByCommandName[event.type]
|
||||
if currentTarget.webkitMatchesSelector(listener.selector)
|
||||
listener.callback.call(currentTarget, syntheticEvent)
|
||||
matchingListeners =
|
||||
@listenersByCommandName[event.type]
|
||||
.filter (listener) -> currentTarget.webkitMatchesSelector(listener.selector)
|
||||
.sort (a, b) -> a.compare(b)
|
||||
|
||||
for listener in matchingListeners
|
||||
listener.callback.call(currentTarget, syntheticEvent)
|
||||
|
||||
break if currentTarget is @rootNode
|
||||
currentTarget = currentTarget.parentNode
|
||||
|
||||
class CommandListener
|
||||
constructor: (@selector, @callback) ->
|
||||
@specificity = (SpecificityCache[@selector] ?= specificity(@selector))
|
||||
@sequenceNumber = SequenceCount++
|
||||
|
||||
compare: (other) ->
|
||||
other.specificity - @specificity or
|
||||
other.sequenceNumber - @sequenceNumber
|
||||
|
||||
Reference in New Issue
Block a user