Commit d56bf99e authored by Robert Knight's avatar Robert Knight

Remove unused code for listening to DOM events in `Delegator`

The `Delegator` events system was used for handling two kinds of event:

 - DOM events on highights and document elements
 - Custom events internal to the "annotator" module

The handling of DOM events has been moved into the `Guest` class, so
`Delegator` is now used only to subscribe to and publish internal
events. This commit removes the DOM event handling code.

There is an existing issue, noted in this commit, that the `destroy`
method only removed event handlers declared via the `events` property
and not those added later by `subscribe`.
parent 4e2fba09
...@@ -16,10 +16,6 @@ $ = require('jquery') ...@@ -16,10 +16,6 @@ $ = require('jquery')
# from. It provides basic functionality such as instance options, event # from. It provides basic functionality such as instance options, event
# delegation and pub/sub methods. # delegation and pub/sub methods.
module.exports = class Delegator module.exports = class Delegator
# Public: Events object. This contains a key/pair hash of events/methods that
# should be bound. See Delegator#addEvents() for usage.
events: {}
# Public: Options object. Extended on initialisation. # Public: Options object. Extended on initialisation.
options: {} options: {}
...@@ -44,116 +40,13 @@ module.exports = class Delegator ...@@ -44,116 +40,13 @@ module.exports = class Delegator
@options = $.extend(true, {}, @options, config) @options = $.extend(true, {}, @options, config)
@element = $(element) @element = $(element)
# Delegator creates closures for each event it binds. This is a private
# registry of created closures, used to enable event unbinding.
@_closures = {}
this.on = this.subscribe this.on = this.subscribe
this.addEvents()
# Public: Destroy the instance, unbinding all events. # Public: Destroy the instance, unbinding all events.
# #
# Returns nothing. # Returns nothing.
destroy: -> destroy: ->
this.removeEvents() # FIXME - This should unbind any event handlers registered via `subscribe`.
# Public: binds the function names in the @events Object to their events.
#
# The @events Object should be a set of key/value pairs where the key is the
# event name with optional CSS selector. The value should be a String method
# name on the current class.
#
# This is called by the default Delegator constructor and so shouldn't usually
# need to be called by the user.
#
# Examples
#
# # This will bind the clickedElement() method to the click event on @element.
# @options = {"click": "clickedElement"}
#
# # This will delegate the submitForm() method to the submit event on the
# # form within the @element.
# @options = {"form submit": "submitForm"}
#
# # This will bind the updateAnnotationStore() method to the custom
# # annotation:save event. NOTE: Because this is a custom event the
# # Delegator#subscribe() method will be used and updateAnnotationStore()
# # will not receive an event parameter like the previous two examples.
# @options = {"annotation:save": "updateAnnotationStore"}
#
# Returns nothing.
addEvents: ->
for event in Delegator._parseEvents(@events)
this._addEvent event.selector, event.event, event.functionName
# Public: unbinds functions previously bound to events by addEvents().
#
# The @events Object should be a set of key/value pairs where the key is the
# event name with optional CSS selector. The value should be a String method
# name on the current class.
#
# Returns nothing.
removeEvents: ->
for event in Delegator._parseEvents(@events)
this._removeEvent event.selector, event.event, event.functionName
# Binds an event to a callback function represented by a String. A selector
# can be provided in order to watch for events on a child element.
#
# The event can be any standard event supported by jQuery or a custom String.
# If a custom string is used the callback function will not recieve an
# event object as it's first parameter.
#
# selector - Selector String matching child elements. (default: '')
# event - The event to listen for.
# functionName - A String function name to bind to the event.
#
# Examples
#
# # Listens for all click events on instance.element.
# instance._addEvent('', 'click', 'onClick')
#
# # Delegates the instance.onInputFocus() method to focus events on all
# # form inputs within instance.element.
# instance._addEvent('form :input', 'focus', 'onInputFocus')
#
# Returns itself.
_addEvent: (selector, event, functionName) ->
closure = => this[functionName].apply(this, arguments)
if selector == '' and Delegator._isCustomEvent(event)
this.subscribe(event, closure)
else
@element.delegate(selector, event, closure)
@_closures["#{selector}/#{event}/#{functionName}"] = closure
this
# Unbinds a function previously bound to an event by the _addEvent method.
#
# Takes the same arguments as _addEvent(), and an event will only be
# successfully unbound if the arguments to removeEvent() are exactly the same
# as the original arguments to _addEvent(). This would usually be called by
# _removeEvents().
#
# selector - Selector String matching child elements. (default: '')
# event - The event to listen for.
# functionName - A String function name to bind to the event.
#
# Returns itself.
_removeEvent: (selector, event, functionName) ->
closure = @_closures["#{selector}/#{event}/#{functionName}"]
if selector == '' and Delegator._isCustomEvent(event)
this.unsubscribe(event, closure)
else
@element.undelegate(selector, event, closure)
delete @_closures["#{selector}/#{event}/#{functionName}"]
this
# Public: Fires an event and calls all subscribed callbacks with any parameters # Public: Fires an event and calls all subscribed callbacks with any parameters
# provided. This is essentially an alias of @element.triggerHandler() but # provided. This is essentially an alias of @element.triggerHandler() but
...@@ -221,45 +114,3 @@ module.exports = class Delegator ...@@ -221,45 +114,3 @@ module.exports = class Delegator
unsubscribe: -> unsubscribe: ->
@element.unbind.apply @element, arguments @element.unbind.apply @element, arguments
this this
# Parse the @events object of a Delegator into an array of objects containing
# string-valued "selector", "event", and "func" keys.
Delegator._parseEvents = (eventsObj) ->
events = []
for sel, functionName of eventsObj
[selector..., event] = sel.split ' '
events.push({
selector: selector.join(' '),
event: event,
functionName: functionName
})
return events
# Native jQuery events that should recieve an event object. Plugins can
# add their own methods to this if required.
Delegator.natives = do ->
specials = (key for own key, val of $.event.special)
"""
blur focus focusin focusout load resize scroll unload click dblclick
mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave
change select submit keydown keypress keyup error touchstart
""".split(/[^a-z]+/).concat(specials)
# Checks to see if the provided event is a DOM event supported by jQuery or
# a custom user event.
#
# event - String event name.
#
# Examples
#
# Delegator._isCustomEvent('click') # => false
# Delegator._isCustomEvent('mousedown') # => false
# Delegator._isCustomEvent('annotation:created') # => true
#
# Returns true if event is a custom user event.
Delegator._isCustomEvent = (event) ->
[event] = event.split('.')
$.inArray(event, Delegator.natives) == -1
...@@ -25,15 +25,12 @@ class FakeAdder ...@@ -25,15 +25,12 @@ class FakeAdder
class FakePlugin extends Plugin class FakePlugin extends Plugin
instance: null instance: null
events:
'customEvent': 'customEventHandler'
constructor: -> constructor: ->
FakePlugin::instance = this FakePlugin::instance = this
super super
pluginInit: sinon.stub() pluginInit: sinon.stub()
customEventHandler: sinon.stub()
# A little helper which returns a promise that resolves after a timeout # A little helper which returns a promise that resolves after a timeout
timeoutPromise = (millis = 0) -> timeoutPromise = (millis = 0) ->
...@@ -121,10 +118,6 @@ describe 'Guest', -> ...@@ -121,10 +118,6 @@ describe 'Guest', ->
it 'hold reference to instance', -> it 'hold reference to instance', ->
assert.equal(fakePlugin.annotator, guest) assert.equal(fakePlugin.annotator, guest)
it 'subscribe to events', ->
guest.publish('customEvent', ['1', '2'])
assert.calledWith(fakePlugin.customEventHandler, '1', '2')
it 'destroy when instance is destroyed', -> it 'destroy when instance is destroyed', ->
sandbox.spy(fakePlugin, 'destroy') sandbox.spy(fakePlugin, 'destroy')
guest.destroy() guest.destroy()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment