Add tests and documentation of ol.events.EventTarget
This commit is contained in:
@@ -5,6 +5,25 @@ goog.require('ol.events');
|
|||||||
goog.require('ol.events.Event');
|
goog.require('ol.events.Event');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @classdesc
|
||||||
|
* A simplified implementation of the W3C DOM Level 2 EventTarget interface.
|
||||||
|
* @see {@link https://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html#Events-EventTarget}
|
||||||
|
*
|
||||||
|
* There are two important simplifications compared to the specification:
|
||||||
|
*
|
||||||
|
* 1. The handling of `useCapture` in `addEventListener` and
|
||||||
|
* `removeEventListener`. There is no real capture model. Instead, when
|
||||||
|
* adding a listener, `useCapture` means that it will be added as first
|
||||||
|
* listener, causing it to be called before other listeners. When removing a
|
||||||
|
* listener, the `useCapture` argument will be ignored, and the listener will
|
||||||
|
* be removed regardless of whether it was added with `useCapture` set to
|
||||||
|
* true or false.
|
||||||
|
* 2. The handling of `stopPropagation` and `preventDefault` on `dispatchEvent`.
|
||||||
|
* There is no event target hierarchy. When a listener calls
|
||||||
|
* `stopPropagation` or `preventDefault` on an event object, it means that no
|
||||||
|
* more listeners after this one will be called. Same as when the listener
|
||||||
|
* returns false.
|
||||||
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
* @extends {goog.Disposable}
|
* @extends {goog.Disposable}
|
||||||
*/
|
*/
|
||||||
@@ -28,7 +47,8 @@ goog.inherits(ol.events.EventTarget, goog.Disposable);
|
|||||||
* @param {boolean=} opt_capture Call listener before already registered
|
* @param {boolean=} opt_capture Call listener before already registered
|
||||||
* listeners. Default is false.
|
* listeners. Default is false.
|
||||||
*/
|
*/
|
||||||
ol.events.EventTarget.prototype.addEventListener = function(type, listener, opt_capture) {
|
ol.events.EventTarget.prototype.addEventListener = function(
|
||||||
|
type, listener, opt_capture) {
|
||||||
var listeners = this.listeners_[type];
|
var listeners = this.listeners_[type];
|
||||||
if (!listeners) {
|
if (!listeners) {
|
||||||
listeners = this.listeners_[type] = [];
|
listeners = this.listeners_[type] = [];
|
||||||
@@ -76,6 +96,9 @@ ol.events.EventTarget.prototype.disposeInternal = function() {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Get the listeners for a specified event type. Listeners are returned in the
|
||||||
|
* opposite order that they will be called in.
|
||||||
|
*
|
||||||
* @param {ol.events.EventType|string} type Type.
|
* @param {ol.events.EventType|string} type Type.
|
||||||
* @return {Array.<ol.events.ListenerFunctionType>} Listeners.
|
* @return {Array.<ol.events.ListenerFunctionType>} Listeners.
|
||||||
*/
|
*/
|
||||||
@@ -99,10 +122,10 @@ ol.events.EventTarget.prototype.hasListener = function(opt_type) {
|
|||||||
/**
|
/**
|
||||||
* @param {ol.events.EventType|string} type Type.
|
* @param {ol.events.EventType|string} type Type.
|
||||||
* @param {ol.events.ListenerFunctionType} listener Listener.
|
* @param {ol.events.ListenerFunctionType} listener Listener.
|
||||||
* @param {boolean=} opt_capture Call listener before already registered
|
* @param {boolean=} opt_capture Ignored. For W3C compatibility only.
|
||||||
* listeners. Default is false.
|
|
||||||
*/
|
*/
|
||||||
ol.events.EventTarget.prototype.removeEventListener = function(type, listener, opt_capture) {
|
ol.events.EventTarget.prototype.removeEventListener = function(
|
||||||
|
type, listener, opt_capture) {
|
||||||
var listeners = this.listeners_[type];
|
var listeners = this.listeners_[type];
|
||||||
if (listeners) {
|
if (listeners) {
|
||||||
var index = listeners.indexOf(listener);
|
var index = listeners.indexOf(listener);
|
||||||
|
|||||||
@@ -0,0 +1,158 @@
|
|||||||
|
goog.provide('ol.test.events.EventTarget');
|
||||||
|
|
||||||
|
|
||||||
|
describe('ol.events.EventTarget', function() {
|
||||||
|
var called, events, eventTarget, spy1, spy2, spy3;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
called = [];
|
||||||
|
events = [];
|
||||||
|
function spy(evt) {
|
||||||
|
called.push(this.id);
|
||||||
|
events.push(evt);
|
||||||
|
}
|
||||||
|
spy1 = spy.bind({id: 1});
|
||||||
|
spy2 = spy.bind({id: 2});
|
||||||
|
spy3 = spy.bind({id: 3});
|
||||||
|
eventTarget = new ol.events.EventTarget();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('constructor', function() {
|
||||||
|
it('creates an instance', function() {
|
||||||
|
expect(eventTarget).to.be.a(ol.events.EventTarget);
|
||||||
|
});
|
||||||
|
it('creates an empty listeners_ object', function() {
|
||||||
|
expect(Object.keys(eventTarget.listeners_)).to.have.length(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#hasListener', function() {
|
||||||
|
it('reports any listeners when called without argument', function() {
|
||||||
|
expect(eventTarget.hasListener()).to.be(false);
|
||||||
|
eventTarget.listeners_['foo'] = [function() {}];
|
||||||
|
expect(eventTarget.hasListener()).to.be(true);
|
||||||
|
});
|
||||||
|
it('reports listeners for the type passed as argument', function() {
|
||||||
|
eventTarget.listeners_['foo'] = [function() {}];
|
||||||
|
expect(eventTarget.hasListener('foo')).to.be(true);
|
||||||
|
expect(eventTarget.hasListener('bar')).to.be(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getListeners', function() {
|
||||||
|
it('returns listeners for a type or undefined if none', function() {
|
||||||
|
expect(eventTarget.getListeners('foo')).to.be(undefined);
|
||||||
|
var listeners = [function() {}];
|
||||||
|
eventTarget.listeners_['foo'] = listeners;
|
||||||
|
expect(eventTarget.getListeners('foo')).to.equal(listeners);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('#addEventListener()', function() {
|
||||||
|
it('has listeners for each registered type', function() {
|
||||||
|
eventTarget.addEventListener('foo', spy1, false);
|
||||||
|
eventTarget.addEventListener('bar', spy2, false);
|
||||||
|
expect(eventTarget.hasListener('foo')).to.be(true);
|
||||||
|
expect(eventTarget.hasListener('bar')).to.be(true);
|
||||||
|
});
|
||||||
|
it('registers listeners in the order determined by useCapture', function() {
|
||||||
|
eventTarget.addEventListener('foo', spy1, false);
|
||||||
|
eventTarget.addEventListener('foo', spy2, false);
|
||||||
|
eventTarget.addEventListener('foo', spy3, true);
|
||||||
|
expect(eventTarget.getListeners('foo')).to.eql([spy2, spy1, spy3]);
|
||||||
|
});
|
||||||
|
it('does not re-add existing listeners, ignoring useCapture', function() {
|
||||||
|
eventTarget.addEventListener('foo', spy1, false);
|
||||||
|
eventTarget.addEventListener('foo', spy2, false);
|
||||||
|
eventTarget.addEventListener('foo', spy3, true);
|
||||||
|
eventTarget.addEventListener('foo', spy2);
|
||||||
|
eventTarget.addEventListener('foo', spy1, true);
|
||||||
|
eventTarget.addEventListener('foo', spy3, false);
|
||||||
|
expect(eventTarget.getListeners('foo')).to.eql([spy2, spy1, spy3]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#removeEventListener()', function() {
|
||||||
|
it('keeps the listeners registry clean', function() {
|
||||||
|
eventTarget.addEventListener('foo', spy1, false);
|
||||||
|
eventTarget.removeEventListener('foo', spy1, false);
|
||||||
|
expect(eventTarget.hasListener('foo')).to.be(false);
|
||||||
|
});
|
||||||
|
it('removes added listeners from the listeners registry', function() {
|
||||||
|
eventTarget.addEventListener('foo', spy1, false);
|
||||||
|
eventTarget.addEventListener('foo', spy2, false);
|
||||||
|
eventTarget.removeEventListener('foo', spy1, false);
|
||||||
|
expect(eventTarget.getListeners('foo')).to.have.length(1);
|
||||||
|
});
|
||||||
|
it('ignores the useCapture setting when removing listeners', function() {
|
||||||
|
eventTarget.addEventListener('foo', spy1, false);
|
||||||
|
eventTarget.addEventListener('foo', spy2, false);
|
||||||
|
eventTarget.addEventListener('foo', spy3, true);
|
||||||
|
eventTarget.removeEventListener('foo', spy1, true);
|
||||||
|
eventTarget.removeEventListener('foo', spy2);
|
||||||
|
eventTarget.removeEventListener('foo', spy3, false);
|
||||||
|
expect(eventTarget.getListeners('foo')).to.be(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#dispatchEvent()', function() {
|
||||||
|
it('calls listeners in the correct order', function() {
|
||||||
|
eventTarget.addEventListener('foo', spy1, false);
|
||||||
|
eventTarget.addEventListener('foo', spy2, false);
|
||||||
|
eventTarget.addEventListener('foo', spy3, true);
|
||||||
|
eventTarget.dispatchEvent('foo');
|
||||||
|
expect(called).to.eql([3, 1, 2]);
|
||||||
|
});
|
||||||
|
it('stops propagation when listeners return false', function() {
|
||||||
|
eventTarget.addEventListener('foo', spy1, false);
|
||||||
|
eventTarget.addEventListener('foo', function(evt) {
|
||||||
|
spy2();
|
||||||
|
return false;
|
||||||
|
}, false);
|
||||||
|
eventTarget.addEventListener('foo', spy3, false);
|
||||||
|
eventTarget.dispatchEvent('foo');
|
||||||
|
expect(called).to.eql([1, 2]);
|
||||||
|
});
|
||||||
|
it('stops propagation when listeners call preventDefault()', function() {
|
||||||
|
eventTarget.addEventListener('foo', spy1, false);
|
||||||
|
eventTarget.addEventListener('foo', function(evt) {
|
||||||
|
spy2();
|
||||||
|
evt.preventDefault();
|
||||||
|
}, true);
|
||||||
|
eventTarget.addEventListener('foo', spy3, false);
|
||||||
|
eventTarget.dispatchEvent('foo');
|
||||||
|
expect(called).to.eql([2]);
|
||||||
|
});
|
||||||
|
it('passes a default ol.events.Event object to listeners', function() {
|
||||||
|
eventTarget.addEventListener('foo', spy1, false);
|
||||||
|
eventTarget.dispatchEvent('foo');
|
||||||
|
expect(events[0]).to.be.a(ol.events.Event);
|
||||||
|
expect(events[0].type).to.be('foo');
|
||||||
|
expect(events[0].target).to.equal(eventTarget);
|
||||||
|
});
|
||||||
|
it('passes a custom event object with target to listeners', function() {
|
||||||
|
eventTarget.addEventListener('foo', spy1, false);
|
||||||
|
var event = {
|
||||||
|
type: 'foo'
|
||||||
|
};
|
||||||
|
eventTarget.dispatchEvent(event);
|
||||||
|
expect(events[0]).to.equal(event);
|
||||||
|
expect(events[0].target).to.equal(eventTarget);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#dispose()', function() {
|
||||||
|
it('cleans up foreign references', function() {
|
||||||
|
ol.events.listen(eventTarget, 'foo', spy1, false, document);
|
||||||
|
expect(eventTarget.hasListener('foo')).to.be(true);
|
||||||
|
eventTarget.dispose();
|
||||||
|
expect(eventTarget.hasListener('foo')).to.be(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
goog.require('ol.events');
|
||||||
|
goog.require('ol.events.Event');
|
||||||
|
goog.require('ol.events.EventTarget');
|
||||||
Reference in New Issue
Block a user