Skip to content

Commit

Permalink
Switch to using Evented for rtlTextPlugin
Browse files Browse the repository at this point in the history
 - Evented.once() listeners can now be removed with off()
 - Evented.listens() now returns false if all listeners removed
  • Loading branch information
ChrisLoer committed Feb 22, 2017
1 parent a89e122 commit c5e23e1
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 36 deletions.
21 changes: 6 additions & 15 deletions src/source/rtl_text_plugin.js
@@ -1,30 +1,23 @@
'use strict';

const ajax = require('../util/ajax');
const Evented = require('../util/evented');
const window = require('../util/window');

const pluginAvailableCallbacks = [];
let pluginRequested = false;
let pluginBlobURL = null;

module.exports.evented = new Evented();

module.exports.registerForPluginAvailability = function(callback) {
if (pluginBlobURL) {
callback(pluginBlobURL);
callback(pluginBlobURL, module.exports.errorCallback);
} else {
pluginAvailableCallbacks.push(callback);
module.exports.evented.once('pluginAvailable', callback);
}
return callback;
};

module.exports.deregisterPluginCallback = function(callback) {
const i = pluginAvailableCallbacks.indexOf(callback);
if (i >= 0) {
pluginAvailableCallbacks.splice(i, 1);
}
};

module.exports.errorCallback = null;

module.exports.setRTLTextPlugin = function(pluginURL, callback) {
if (pluginRequested) {
throw new Error('setRTLTextPlugin cannot be called multiple times.');
Expand All @@ -38,9 +31,7 @@ module.exports.setRTLTextPlugin = function(pluginURL, callback) {
pluginBlobURL =
window.URL.createObjectURL(new window.Blob([response.data]), {type: "text/javascript"});

while (pluginAvailableCallbacks.length > 0) {
pluginAvailableCallbacks.shift()(pluginBlobURL);
}
module.exports.evented.fire('pluginAvailable', { pluginBlobURL: pluginBlobURL, errorCallback: callback });
}
});
};
6 changes: 3 additions & 3 deletions src/style/style.js
Expand Up @@ -78,8 +78,8 @@ class Style extends Evented {
this.fire('dataloading', {dataType: 'style'});

const self = this;
this.rtlTextPluginCallback = rtlTextPlugin.registerForPluginAvailability((pluginBlobURL) => {
self.dispatcher.broadcast('loadRTLTextPlugin', pluginBlobURL, rtlTextPlugin.errorCallback);
this._rtlTextPluginCallback = rtlTextPlugin.registerForPluginAvailability((args) => {
self.dispatcher.broadcast('loadRTLTextPlugin', args.pluginBlobURL, args.errorCallback);
for (const id in self.sourceCaches) {
self.sourceCaches[id].reload(); // Should be a no-op if the plugin loads before any tiles load
}
Expand Down Expand Up @@ -824,7 +824,7 @@ class Style extends Evented {
}

_remove() {
rtlTextPlugin.deregisterPluginCallback(this.rtlTextPluginCallback);
rtlTextPlugin.evented.off('pluginAvailable', this._rtlTextPluginCallback);
for (const id in this.sourceCaches) {
this.sourceCaches[id].clearTiles();
}
Expand Down
43 changes: 29 additions & 14 deletions src/util/evented.js
Expand Up @@ -2,6 +2,20 @@

const util = require('./util');

function _addEventListener(type, listener, listenerList) {
listenerList[type] = listenerList[type] || [];
listenerList[type].push(listener);
}

function _removeEventListener(type, listener, listenerList) {
if (listenerList && listenerList[type]) {
const index = listenerList[type].indexOf(listener);
if (index !== -1) {
listenerList[type].splice(index, 1);
}
}
}

/**
* Methods mixed in to other classes for event capabilities.
*
Expand All @@ -20,8 +34,7 @@ class Evented {
*/
on(type, listener) {
this._listeners = this._listeners || {};
this._listeners[type] = this._listeners[type] || [];
this._listeners[type].push(listener);
_addEventListener(type, listener, this._listeners);

return this;
}
Expand All @@ -34,12 +47,8 @@ class Evented {
* @returns {Object} `this`
*/
off(type, listener) {
if (this._listeners && this._listeners[type]) {
const index = this._listeners[type].indexOf(listener);
if (index !== -1) {
this._listeners[type].splice(index, 1);
}
}
_removeEventListener(type, listener, this._listeners);
_removeEventListener(type, listener, this._oneTimeListeners);

return this;
}
Expand All @@ -54,11 +63,9 @@ class Evented {
* @returns {Object} `this`
*/
once(type, listener) {
const wrapper = (data) => {
this.off(type, wrapper);
listener.call(this, data);
};
this.on(type, wrapper);
this._oneTimeListeners = this._oneTimeListeners || {};
_addEventListener(type, listener, this._oneTimeListeners);

return this;
}

Expand All @@ -81,6 +88,13 @@ class Evented {
listeners[i].call(this, data);
}

const oneTimeListeners = this._oneTimeListeners && this._oneTimeListeners[type] ? this._oneTimeListeners[type].slice() : [];

for (let i = 0; i < oneTimeListeners.length; i++) {
oneTimeListeners[i].call(this, data);
_removeEventListener(type, oneTimeListeners[i], this._oneTimeListeners);
}

if (this._eventedParent) {
this._eventedParent.fire(type, util.extend({}, data, this._eventedParentData));
}
Expand All @@ -102,7 +116,8 @@ class Evented {
*/
listens(type) {
return (
(this._listeners && this._listeners[type]) ||
(this._listeners && this._listeners[type] && this._listeners[type].length > 0) ||
(this._oneTimeListeners && this._oneTimeListeners[type] && this._oneTimeListeners[type].length > 0) ||
(this._eventedParent && this._eventedParent.listens(type))
);
}
Expand Down
21 changes: 17 additions & 4 deletions test/unit/style/style.test.js
Expand Up @@ -56,6 +56,19 @@ test('Style', (t) => {
new Style(createStyleJSON(), eventedParent);
});

t.test('registers plugin listener', (t) => {
t.spy(rtlTextPlugin, 'registerForPluginAvailability');
const style = new Style(createStyleJSON());
t.spy(style.dispatcher, 'broadcast');
t.ok(rtlTextPlugin.registerForPluginAvailability.calledOnce);

style.on('style.load', () => {
rtlTextPlugin.evented.fire('pluginAvailable');
t.ok(style.dispatcher.broadcast.calledWith('loadRTLTextPlugin'));
t.end();
});
});

t.test('can be constructed from a URL', (t) => {
window.useFakeXMLHttpRequest();
window.server.respondWith('/style.json', JSON.stringify(require('../../fixtures/style')));
Expand Down Expand Up @@ -195,14 +208,14 @@ test('Style#_remove', (t) => {

t.test('deregisters plugin listener', (t) => {
t.spy(rtlTextPlugin, 'registerForPluginAvailability');
t.spy(rtlTextPlugin, 'deregisterPluginCallback');
const style = new Style(createStyleJSON());
t.ok(rtlTextPlugin.registerForPluginAvailability.calledOnce);
t.notOk(rtlTextPlugin.deregisterPluginCallback.called);
t.spy(style.dispatcher, 'broadcast');

style.on('style.load', () => {
style._remove();
t.ok(rtlTextPlugin.deregisterPluginCallback.calledOnce);

rtlTextPlugin.evented.fire('pluginAvailable');
t.notOk(style.dispatcher.broadcast.calledWith('loadRTLTextPlugin'));
t.end();
});
});
Expand Down
20 changes: 20 additions & 0 deletions test/unit/util/evented.test.js
Expand Up @@ -22,6 +22,7 @@ test('Evented', (t) => {
evented.fire('a');
evented.fire('a');
t.ok(listener.calledOnce);
t.notOk(evented.listens('a'));
t.end();
});

Expand Down Expand Up @@ -62,6 +63,16 @@ test('Evented', (t) => {
t.end();
});

t.test('removes one-time listeners with "off"', (t) => {
const evented = new Evented();
const listener = t.spy();
evented.once('a', listener);
evented.off('a', listener);
evented.fire('a');
t.ok(listener.notCalled);
t.end();
});

t.test('reports if an event has listeners with "listens"', (t) => {
const evented = new Evented();
evented.on('a', () => {});
Expand All @@ -70,6 +81,15 @@ test('Evented', (t) => {
t.end();
});

t.test('does not report true to "listens" if all listeners have been removed', (t) => {
const evented = new Evented();
const listener = () => {};
evented.on('a', listener);
evented.off('a', listener);
t.notOk(evented.listens('a'));
t.end();
});

t.test('does not immediately call listeners added within another listener', (t) => {
const evented = new Evented();
evented.on('a', () => {
Expand Down

0 comments on commit c5e23e1

Please sign in to comment.