Simplify events and store listeners only in one place

This commit is contained in:
ahocevar
2019-09-04 16:39:32 +02:00
parent d416866108
commit ebfb20440a
52 changed files with 224 additions and 599 deletions

View File

@@ -1,98 +1,18 @@
import {listen, listenOnce, bindListener, unlisten, unlistenAll, unlistenByKey, findListener, getListeners} from '../../../src/ol/events.js';
import {listen, listenOnce, unlistenByKey} from '../../../src/ol/events.js';
import EventTarget from '../../../src/ol/events/Target.js';
describe('ol.events', function() {
let add, remove, target;
let add, target;
beforeEach(function() {
add = sinon.spy();
remove = sinon.spy();
target = {
addEventListener: add,
removeEventListener: remove
};
target = new EventTarget();
add = sinon.spy(target, 'addEventListener');
});
describe('bindListener()', function() {
it('binds a listener and returns a bound listener function', function() {
const listenerObj = {
listener: sinon.spy(),
bindTo: {id: 1}
};
const boundListener = bindListener(listenerObj);
expect(listenerObj.boundListener).to.equal(boundListener);
boundListener();
expect(listenerObj.listener.thisValues[0]).to.equal(listenerObj.bindTo);
});
it('binds to the target when bindTo is not provided', function(done) {
const target = new EventTarget();
const listenerObj = listen(target, 'foo', function() {
expect(this).to.equal(target);
done();
});
expect(listenerObj.boundListener).to.equal(listenerObj.listener);
target.dispatchEvent('foo');
});
it('binds a self-unregistering listener when callOnce is true', function() {
const bindTo = {id: 1};
const listenerObj = {
type: 'foo',
target: target,
bindTo: bindTo,
callOnce: true
};
listenerObj.listener = function() {
expect(this).to.equal(bindTo);
};
const boundListener = bindListener(listenerObj);
expect(listenerObj.boundListener).to.equal(boundListener);
boundListener();
});
afterEach(function() {
target.addEventListener.restore();
});
describe('findListener()', function() {
let listener, listenerObj, listeners;
beforeEach(function() {
listener = function() {};
listenerObj = {
type: 'foo',
target: target,
listener: listener
};
listeners = [listenerObj];
});
it('searches a listener array for a specific listener', function() {
const bindTo = {id: 1};
let result = findListener(listeners, listener);
expect(result).to.be(listenerObj);
result = findListener(listeners, listener, bindTo);
expect(result).to.be(undefined);
listenerObj.bindTo = bindTo;
result = findListener(listeners, listener);
expect(result).to.be(undefined);
result = findListener(listeners, listener, bindTo);
expect(result).to.be(listenerObj);
});
it('marks the delete index on a listener object', function() {
const result = findListener(listeners, listener, undefined, true);
expect(result).to.be(listenerObj);
expect(listenerObj.deleteIndex).to.be(0);
});
});
describe('getListeners()', function() {
it('returns listeners for a target and type', function() {
const foo = listen(target, 'foo', function() {});
const bar = listen(target, 'bar', function() {});
expect (getListeners(target, 'foo')).to.eql([foo]);
expect (getListeners(target, 'bar')).to.eql([bar]);
});
it('returns undefined when no listeners are registered', function() {
expect (getListeners(target, 'foo')).to.be(undefined);
});
});
describe('listen()', function() {
it('calls addEventListener on the target', function() {
@@ -105,17 +25,16 @@ describe('ol.events', function() {
});
it('does not add the same listener twice', function() {
const listener = function() {};
const key1 = listen(target, 'foo', listener);
const key2 = listen(target, 'foo', listener);
expect(key1).to.equal(key2);
expect(add.callCount).to.be(1);
listen(target, 'foo', listener);
listen(target, 'foo', listener);
expect(target.listeners_['foo'].length).to.be(1);
});
it('only treats listeners as same when all args are equal', function() {
const listener = function() {};
listen(target, 'foo', listener, {});
listen(target, 'foo', listener, {});
listen(target, 'foo', listener, undefined);
expect(add.callCount).to.be(3);
expect(target.listeners_['foo'].length).to.be(3);
});
});
@@ -123,95 +42,56 @@ describe('ol.events', function() {
it('creates a one-off listener', function() {
const target = new EventTarget();
const listener = sinon.spy();
const key = listenOnce(target, 'foo', listener);
expect(key.callOnce).to.be(true);
listenOnce(target, 'foo', listener);
target.dispatchEvent('foo');
expect(listener.callCount).to.be(1);
target.dispatchEvent('foo');
expect(listener.callCount).to.be(1);
});
it('does not add the same listener twice', function() {
const listener = function() {};
const key1 = listenOnce(target, 'foo', listener);
const key2 = listenOnce(target, 'foo', listener);
expect(key1).to.equal(key2);
expect(add.callCount).to.be(1);
expect(key1.callOnce).to.be(true);
});
it('listen() can turn a one-off listener into a permanent one', function() {
it('Adds the same listener twice', function() {
const listener = sinon.spy();
let key = listenOnce(target, 'foo', listener);
expect(key.callOnce).to.be(true);
key = listen(target, 'foo', listener);
expect(add.callCount).to.be(1);
expect(key.callOnce).to.be(false);
key.boundListener();
expect(remove.callCount).to.be(0);
listenOnce(target, 'foo', listener);
listenOnce(target, 'foo', listener);
target.dispatchEvent('foo');
target.dispatchEvent('foo');
target.dispatchEvent('foo');
expect(listener.callCount).to.be(2);
});
});
describe('unlisten()', function() {
it('unregisters previously registered listeners', function() {
const listener = function() {};
listen(target, 'foo', listener);
unlisten(target, 'foo', listener);
expect(getListeners(target, 'foo')).to.be(undefined);
});
it('works with multiple types', function() {
const listener = function() {};
listen(target, ['foo', 'bar'], listener);
unlisten(target, ['bar', 'foo'], listener);
expect(getListeners(target, 'foo')).to.be(undefined);
expect(getListeners(target, 'bar')).to.be(undefined);
});
});
describe('unlistenByKey()', function() {
it('unregisters previously registered listeners', function() {
const key = listen(target, 'foo', function() {});
unlistenByKey(key);
expect(getListeners(target, 'foo')).to.be(undefined);
expect(target.listeners_['foo']).to.be(undefined);
});
it('works with multiple types', function() {
const key = listen(target, ['foo', 'bar'], function() {});
unlistenByKey(key);
expect(getListeners(target, 'foo')).to.be(undefined);
expect(getListeners(target, 'bar')).to.be(undefined);
expect(target.listeners_['foo']).to.be(undefined);
expect(target.listeners_['bar']).to.be(undefined);
});
});
describe('unlistenAll()', function() {
it('unregisters all listeners registered for a target', function() {
const keys = [
listen(target, 'foo', function() {}),
listen(target, 'bar', function() {})
];
unlistenAll(target);
expect(getListeners(target, 'foo')).to.be(undefined);
expect(getListeners(target, 'bar')).to.be(undefined);
expect('ol_lm' in target).to.be(false);
expect(keys).to.eql([{}, {}]);
});
});
describe('Compatibility with ol.events.EventTarget', function() {
describe('Listener keys', function() {
it('does not register duplicated listeners', function() {
const target = new EventTarget();
const listener = function() {};
const key1 = listen(target, 'foo', listener);
expect(target.getListeners('foo')).to.eql([key1.boundListener]);
expect(target.listeners_['foo']).to.eql([listener]);
const key2 = listen(target, 'foo', listener);
expect(key2.boundListener).to.equal(key1.boundListener);
expect(target.getListeners('foo')).to.eql([key1.boundListener]);
expect(target.listeners_['foo']).to.eql([listener]);
expect(key1.listener).to.equal(key2.listener);
});
it('registers multiple listeners if this object is different', function() {
const target = new EventTarget();
const listener = function() {};
const key1 = listen(target, 'foo', listener, {});
const key2 = listen(target, 'foo', listener, {});
expect(key1.boundListener).to.not.equal(key2.boundListener);
expect(target.getListeners('foo')).to.eql(
[key1.boundListener, key2.boundListener]);
expect(key1.listener).to.not.equal(key2.listener);
expect(target.listeners_['foo']).to.eql([key1.listener, key2.listener]);
});
});

View File

@@ -43,16 +43,6 @@ describe('ol.events.EventTarget', function() {
});
});
describe('#getListeners', function() {
it('returns listeners for a type or undefined if none', function() {
expect(eventTarget.getListeners('foo')).to.be(undefined);
const 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);
@@ -72,7 +62,7 @@ describe('ol.events.EventTarget', function() {
eventTarget.addEventListener('foo', spy1);
eventTarget.addEventListener('foo', spy2);
eventTarget.removeEventListener('foo', spy1, false);
expect(eventTarget.getListeners('foo')).to.have.length(1);
expect(eventTarget.listeners_['foo']).to.have.length(1);
});
});
@@ -131,7 +121,7 @@ describe('ol.events.EventTarget', function() {
eventTarget.dispatchEvent('foo');
}).not.to.throwException();
expect(called).to.eql([3]);
expect(eventTarget.getListeners('foo')).to.have.length(1);
expect(eventTarget.listeners_['foo']).to.have.length(1);
});
it('is safe to do weird things in listeners', function() {
eventTarget.addEventListener('foo', spy2);
@@ -148,7 +138,7 @@ describe('ol.events.EventTarget', function() {
eventTarget.dispatchEvent('foo');
}).not.to.throwException();
expect(called).to.eql([2, 2]);
expect(eventTarget.getListeners('foo')).to.be(undefined);
expect(eventTarget.listeners_['foo']).to.be(undefined);
});
});

View File

@@ -3,7 +3,6 @@ import Feature from '../../../../src/ol/Feature.js';
import Map from '../../../../src/ol/Map.js';
import MapBrowserPointerEvent from '../../../../src/ol/MapBrowserPointerEvent.js';
import View from '../../../../src/ol/View.js';
import {getListeners} from '../../../../src/ol/events.js';
import {doubleClick} from '../../../../src/ol/events/condition.js';
import Circle from '../../../../src/ol/geom/Circle.js';
import LineString from '../../../../src/ol/geom/LineString.js';
@@ -13,6 +12,7 @@ import Modify, {ModifyEvent} from '../../../../src/ol/interaction/Modify.js';
import VectorLayer from '../../../../src/ol/layer/Vector.js';
import VectorSource from '../../../../src/ol/source/Vector.js';
import Event from '../../../../src/ol/events/Event.js';
import {getValues} from '../../../../src/ol/obj.js';
describe('ol.interaction.Modify', function() {
@@ -607,10 +607,10 @@ describe('ol.interaction.Modify', function() {
beforeEach(function() {
getModifyListeners = function(feature, modify) {
const listeners = getListeners(
feature, 'change');
const listeners = feature.listeners_['change'];
const candidates = getValues(modify);
return listeners.filter(function(listener) {
return listener.bindTo === modify;
return candidates.indexOf(listener) !== -1;
});
};
});

View File

@@ -130,7 +130,7 @@ describe('ol.interaction.MouseWheelZoom', function() {
map.handleMapBrowserEvent(event);
});
it.only('works on all browsers (wheel)', function(done) {
it('works on all browsers (wheel)', function(done) {
map.once('postrender', function() {
const call = view.animate.getCall(0);
expect(call.args[0].resolution).to.be(2);

View File

@@ -1,4 +1,3 @@
import {listen, unlisten} from '../../../../../src/ol/events.js';
import {clear} from '../../../../../src/ol/obj.js';
import * as render from '../../../../../src/ol/render/canvas.js';
@@ -22,11 +21,11 @@ describe('ol.render.canvas', function() {
it('does not clear label cache and measurements for unavailable fonts', function(done) {
this.timeout(4000);
const spy = sinon.spy();
listen(render.labelCache, 'clear', spy);
render.labelCache.addEventListener('clear', spy);
const interval = setInterval(function() {
if (render.checkedFonts['normal\nnormal\nfoo'] == retries && render.checkedFonts['normal\nnormal\nsans-serif'] == retries) {
clearInterval(interval);
unlisten(render.labelCache, 'clear', spy);
render.labelCache.removeEventListener('clear', spy);
expect(spy.callCount).to.be(0);
expect(render.textHeights).to.not.eql({});
done();
@@ -37,11 +36,11 @@ describe('ol.render.canvas', function() {
it('does not clear label cache and measurements for available fonts', function(done) {
const spy = sinon.spy();
listen(render.labelCache, 'clear', spy);
render.labelCache.addEventListener('clear', spy);
const interval = setInterval(function() {
if (render.checkedFonts['normal\nnormal\nsans-serif'] == retries) {
clearInterval(interval);
unlisten(render.labelCache, 'clear', spy);
render.labelCache.removeEventListener('clear', spy);
expect(spy.callCount).to.be(0);
expect(render.textHeights).to.not.eql({});
done();
@@ -52,11 +51,11 @@ describe('ol.render.canvas', function() {
it('does not clear label cache and measurements for the \'monospace\' font', function(done) {
const spy = sinon.spy();
listen(render.labelCache, 'clear', spy);
render.labelCache.addEventListener('clear', spy);
const interval = setInterval(function() {
if (render.checkedFonts['normal\nnormal\nmonospace'] == retries) {
clearInterval(interval);
unlisten(render.labelCache, 'clear', spy);
render.labelCache.removeEventListener('clear', spy);
expect(spy.callCount).to.be(0);
expect(render.textHeights).to.not.eql({});
done();
@@ -68,7 +67,7 @@ describe('ol.render.canvas', function() {
it('clears label cache and measurements for fonts that become available', function(done) {
head.appendChild(font);
render.labelCache.set('dummy', {});
listen(render.labelCache, 'clear', function() {
render.labelCache.addEventListener('clear', function() {
expect(render.textHeights).to.eql({});
done();
});

View File

@@ -40,7 +40,7 @@ describe('ol.renderer.Layer', function() {
it('registers a listener', function() {
renderer.loadImage(image);
const listeners = image.getListeners(eventType, false);
const listeners = image.listeners_[eventType];
expect(listeners).to.have.length(1);
});
@@ -76,7 +76,7 @@ describe('ol.renderer.Layer', function() {
it('does not register a new listener', function() {
renderer.loadImage(image);
const listeners = image.getListeners(eventType, false);
const listeners = image.listeners_[eventType];
expect(listeners).to.have.length(1);
});

View File

@@ -1,5 +1,4 @@
import {VOID} from '../../../../src/ol/functions.js';
import {getListeners} from '../../../../src/ol/events.js';
import LineString from '../../../../src/ol/geom/LineString.js';
import Point from '../../../../src/ol/geom/Point.js';
import Polygon from '../../../../src/ol/geom/Polygon.js';
@@ -54,8 +53,7 @@ describe('ol.renderer.vector', function() {
style, squaredTolerance, listener, listenerThis);
expect(iconStyleLoadSpy.calledOnce).to.be.ok();
listeners = getListeners(
iconStyle.iconImage_, 'change');
listeners = iconStyle.iconImage_.listeners_['change'];
expect(listeners.length).to.eql(1);
// call #2
@@ -63,8 +61,7 @@ describe('ol.renderer.vector', function() {
style, squaredTolerance, listener, listenerThis);
expect(iconStyleLoadSpy.calledOnce).to.be.ok();
listeners = getListeners(
iconStyle.iconImage_, 'change');
listeners = iconStyle.iconImage_.listeners_['change'];
expect(listeners.length).to.eql(1);
});