backbone-esnext-events
Version:
Separates 'events' support from Backbone in addition to adding TyphonJS extensions.
448 lines (371 loc) • 14.6 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _TyphonEvents = require('./TyphonEvents.js');
var _TyphonEvents2 = _interopRequireDefault(_TyphonEvents);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* EventProxy provides a protected proxy of another TyphonEvents / eventbus instance.
*
* The main use case of EventProxy is to allow indirect access to an eventbus. This is handy when it comes to managing
* the event lifecycle for a plugin system. When a plugin is added it could receive a callback, perhaps named
* `onPluginLoaded`, which contains an EventProxy instance rather than the direct eventbus. This EventProxy instance is
* associated in the management system controlling plugin lifecycle. When a plugin is removed / unloaded the management
* system can automatically unregister all events for the plugin without requiring the plugin author doing it correctly
* if they had full control. IE This allows to plugin system to guarantee no dangling listeners.
*
* EventProxy provides the on / off, once, and trigger methods with the same signatures as found in
* TyphonEvents / Events. However, the proxy tracks all added event bindings which is used to proxy between the target
* eventbus which is passed in from the constructor. All registration methods (on / off / once) proxy. In addition
* there is a `destroy` method which will unregister all of proxied events and remove references to the managed
* eventbus. Any further usage of a destroyed EventProxy instance results in a ReferenceError thrown.
*/
var EventProxy = function () {
/**
* Creates the event proxy with an existing instance of TyphonEvents.
*
* @param {TyphonEvents} eventbus - The target eventbus instance.
*/
function EventProxy(eventbus) {
(0, _classCallCheck3.default)(this, EventProxy);
if (!(eventbus instanceof _TyphonEvents2.default)) {
throw new TypeError('\'eventbus\' is not an instance of TyphonEvents.');
}
/**
* Stores the target eventbus.
* @type {TyphonEvents}
* @private
*/
this._eventbus = eventbus;
/**
* Stores all proxied event bindings.
* @type {Array<{name: string, callback: function, context: *}>}
* @private
*/
this._events = [];
}
/**
* Creates a new EventProxy from the target eventbus of this proxy.
*
* @returns {EventProxy}
*/
(0, _createClass3.default)(EventProxy, [{
key: 'createEventProxy',
value: function createEventProxy() {
return new EventProxy(this._eventbus);
}
/**
* Unregisters all proxied events from the target eventbus and removes any local references. All subsequent calls
* after `destroy` has been called result in a ReferenceError thrown.
*/
}, {
key: 'destroy',
value: function destroy() {
if (this._eventbus === null) {
throw new ReferenceError('This EventProxy instance has been destroyed.');
}
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(this._events), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var event = _step.value;
this._eventbus.off(event.name, event.callback, event.context);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
this._events = [];
this._eventbus = null;
}
/**
* Returns the current proxied event count.
*
* @returns {Number}
*/
}, {
key: 'getEventNames',
/**
* Returns the event names of proxied event listeners.
*
* @returns {string[]}
*/
value: function getEventNames() {
if (!this._events) {
return [];
}
var eventNames = {};
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = (0, _getIterator3.default)(this._events), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var event = _step2.value;
eventNames[event.name] = true;
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
return (0, _keys2.default)(eventNames);
}
/**
* Iterates over all proxied events invoking a callback with the event data stored.
*
* @param {function} callback - Callback invoked for each proxied event stored.
*/
}, {
key: 'forEachEvent',
value: function forEachEvent(callback) {
if (typeof callback !== 'function') {
throw new TypeError('\'callback\' is not a \'function\'.');
}
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = (0, _getIterator3.default)(this._events), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var event = _step3.value;
callback(event.name, event.callback, event.context);
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
}
/**
* Returns the target eventbus name.
*
* @returns {string|*}
*/
}, {
key: 'getEventbusName',
value: function getEventbusName() {
if (this._eventbus === null) {
throw new ReferenceError('This EventProxy instance has been destroyed.');
}
return this._eventbus.getEventbusName();
}
/**
* Remove a previously-bound proxied event binding.
*
* Please see {@link Events#off}.
*
* @param {string} [name] - Event name(s)
*
* @param {function} [callback] - Event callback function
*
* @param {object} [context] - Event context
*
* @returns {EventProxy}
*/
}, {
key: 'off',
value: function off() {
var name = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : void 0;
var callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : void 0;
var context = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : void 0;
if (this._eventbus === null) {
throw new ReferenceError('This EventProxy instance has been destroyed.');
}
var hasName = typeof name !== 'undefined' && name !== null;
var hasCallback = typeof callback !== 'undefined' && callback !== null;
var hasContext = typeof context !== 'undefined' && context !== null;
// Remove all events if `off()` is invoked.
if (!hasName && !hasCallback && !hasContext) {
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = (0, _getIterator3.default)(this._events), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var event = _step4.value;
this._eventbus.off(event.name, event.callback, event.context);
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
this._events = [];
} else {
var values = {};
if (hasName) {
values.name = name;
}
if (hasCallback) {
values.callback = callback;
}
if (hasContext) {
values.context = context;
}
for (var cntr = this._events.length; --cntr >= 0;) {
var _event = this._events[cntr];
var foundMatch = true;
for (var key in values) {
if (_event[key] !== values[key]) {
foundMatch = false;break;
}
}
if (foundMatch) {
this._eventbus.off(values.name, values.callback, values.context);
this._events.splice(cntr, 1);
}
}
}
return this;
}
/**
* Bind a callback function to an object. The callback will be invoked whenever the event is fired. If you have a
* large number of different events on a page, the convention is to use colons to namespace them: "poll:start", or
* "change:selection".
*
* This is proxied through `listenTo` of an internal Events instance instead of directly modifying the target
* eventbus.
*
* Please see {@link Events#on}.
*
* @param {string} name - Event name(s)
* @param {function} callback - Event callback function
* @param {object} context - Event context
* @returns {EventProxy}
*/
}, {
key: 'on',
value: function on(name, callback) {
var context = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : void 0;
if (this._eventbus === null) {
throw new ReferenceError('This EventProxy instance has been destroyed.');
}
this._eventbus.on(name, callback, context);
this._events.push({ name: name, callback: callback, context: context });
return this;
}
/**
* Trigger callbacks for the given event, or space-delimited list of events. Subsequent arguments to trigger will be
* passed along to the event callbacks.
*
* Please see {@link Events#trigger}.
*
* @returns {EventProxy}
*/
}, {
key: 'trigger',
value: function trigger() {
var _eventbus;
if (this._eventbus === null) {
throw new ReferenceError('This EventProxy instance has been destroyed.');
}
(_eventbus = this._eventbus).trigger.apply(_eventbus, arguments);
return this;
}
/**
* Provides `trigger` functionality, but collects any returned Promises from invoked targets and returns a
* single Promise generated by `Promise.resolve` for a single value or `Promise.all` for multiple results. This is
* a very useful mechanism to invoke asynchronous operations over an eventbus.
*
* Please see {@link TyphonEvents#triggerAsync}.
*
* @returns {Promise}
*/
}, {
key: 'triggerAsync',
value: function triggerAsync() {
var _eventbus2;
if (this._eventbus === null) {
throw new ReferenceError('This EventProxy instance has been destroyed.');
}
return (_eventbus2 = this._eventbus).triggerAsync.apply(_eventbus2, arguments);
}
/**
* Defers invoking `trigger`. This is useful for triggering events in the next clock tick.
*
* Please see {@link TyphonEvents#triggerDefer}.
*
* @returns {EventProxy}
*/
}, {
key: 'triggerDefer',
value: function triggerDefer() {
var _eventbus3;
if (this._eventbus === null) {
throw new ReferenceError('This EventProxy instance has been destroyed.');
}
(_eventbus3 = this._eventbus).triggerDefer.apply(_eventbus3, arguments);
return this;
}
/**
* Provides `trigger` functionality, but collects any returned result or results from invoked targets as a single
* value or in an array and passes it back to the callee in a synchronous manner.
*
* Please see {@link TyphonEvents#triggerSync}.
*
* @returns {*|Array.<*>}
*/
}, {
key: 'triggerSync',
value: function triggerSync() {
var _eventbus4;
if (this._eventbus === null) {
throw new ReferenceError('This EventProxy instance has been destroyed.');
}
return (_eventbus4 = this._eventbus).triggerSync.apply(_eventbus4, arguments);
}
}, {
key: 'eventCount',
get: function get() {
return this._events.length;
}
}]);
return EventProxy;
}();
exports.default = EventProxy;
module.exports = exports['default'];