backbone-esnext-events
Version:
Separates 'events' support from Backbone in addition to adding TyphonJS extensions.
586 lines (480 loc) • 19.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof2 = require('babel-runtime/helpers/typeof');
var _typeof3 = _interopRequireDefault(_typeof2);
var _regenerator = require('babel-runtime/regenerator');
var _regenerator2 = _interopRequireDefault(_regenerator);
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
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 _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _get2 = require('babel-runtime/helpers/get');
var _get3 = _interopRequireDefault(_get2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _Events2 = require('./Events.js');
var _Events3 = _interopRequireDefault(_Events2);
var _EventProxy = require('./EventProxy.js');
var _EventProxy2 = _interopRequireDefault(_EventProxy);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* TyphonEvents adds new functionality for trigger events. The following are new trigger mechanisms:
*
* `triggerDefer` - Defers invoking `trigger`.
*
* `triggerSync` - Invokes all targets matched and passes back a single result or an array of results in an array to
* the callee.
*
* `triggerAsync` - Invokes all targets matched and adds any returned results through `Promise.all` which returns
* a single promise to the callee.
*
* Please refer to the Events documentation for all inherited functionality.
*/
var TyphonEvents = function (_Events) {
(0, _inherits3.default)(TyphonEvents, _Events);
/**
* Provides a constructor which optionally takes the eventbus name.
*
* @param {string} eventbusName - Optional eventbus name.
*/
function TyphonEvents() {
var eventbusName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : void 0;
(0, _classCallCheck3.default)(this, TyphonEvents);
var _this = (0, _possibleConstructorReturn3.default)(this, (TyphonEvents.__proto__ || (0, _getPrototypeOf2.default)(TyphonEvents)).call(this));
_this.setEventbusName(eventbusName);
return _this;
}
/**
* Creates an EventProxy wrapping this events instance. An EventProxy proxies events allowing all listeners added
* to be easily removed from the wrapped Events instance.
*
* @returns {EventProxy}
*/
(0, _createClass3.default)(TyphonEvents, [{
key: 'createEventProxy',
value: function createEventProxy() {
return new _EventProxy2.default(this);
}
/**
* Iterates over all stored 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) {
/* istanbul ignore if */
if (!this._events) {
return;
}
/* istanbul ignore if */
if (typeof callback !== 'function') {
throw new TypeError('\'callback\' is not a \'function\'.');
}
for (var name in this._events) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(this._events[name]), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var event = _step.value;
callback(name, event.callback, event.ctx);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
}
/**
* Returns the current proxied event count.
*
* @returns {Number}
*/
}, {
key: 'getEventbusName',
/**
* Returns the current eventbusName.
*
* @returns {string|*}
*/
value: function getEventbusName() {
return this._eventbusName;
}
/**
* Returns the event names of registered event listeners.
*
* @returns {string[]}
*/
}, {
key: 'getEventNames',
value: function getEventNames() {
/* istanbul ignore if */
if (!this._events) {
return [];
}
return (0, _keys2.default)(this._events);
}
/**
* Sets the eventbus name.
*
* @param {string} name - The name for this eventbus.
*
* @return {TyphonEvents}
*/
}, {
key: 'setEventbusName',
value: function setEventbusName(name) {
/**
* Stores the name of this eventbus.
* @type {string}
* @private
*/
this._eventbusName = name;
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.
*
* @param {string} name - Event name(s)
* @returns {Promise}
*/
}, {
key: 'triggerAsync',
value: function () {
var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(name) {
var length,
args,
i,
promise,
_args = arguments;
return _regenerator2.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
/* istanbul ignore if */
if (!this._events) {
_promise2.default.all([]);
}
length = Math.max(0, _args.length - 1);
args = new Array(length);
for (i = 0; i < length; i++) {
args[i] = _args[i + 1];
}
promise = s_EVENTS_API(s_TRIGGER_API, s_TRIGGER_ASYNC_EVENTS, this._events, name, void 0, args);
return _context.abrupt('return', promise !== void 0 ? promise : _promise2.default.resolve());
case 6:
case 'end':
return _context.stop();
}
}
}, _callee, this);
}));
function triggerAsync(_x2) {
return _ref.apply(this, arguments);
}
return triggerAsync;
}()
/**
* Defers invoking `trigger`. This is useful for triggering events in the next clock tick.
*
* @returns {TyphonEvents}
*/
}, {
key: 'triggerDefer',
value: function triggerDefer() {
var _this2 = this,
_arguments = arguments;
setTimeout(function () {
(0, _get3.default)(TyphonEvents.prototype.__proto__ || (0, _getPrototypeOf2.default)(TyphonEvents.prototype), 'trigger', _this2).apply(_this2, _arguments);
}, 0);
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.
*
* @param {string} name - Event name(s)
* @returns {*|Array<*>}
*/
}, {
key: 'triggerSync',
value: function triggerSync(name) {
/* istanbul ignore if */
if (!this._events) {
return void 0;
}
var start = 1;
var length = Math.max(0, arguments.length - 1);
var args = new Array(length);
for (var i = 0; i < length; i++) {
args[i] = arguments[i + start];
}
return s_EVENTS_API(s_TRIGGER_API, s_TRIGGER_SYNC_EVENTS, this._events, name, void 0, args);
}
}, {
key: 'eventCount',
get: function get() {
var count = 0;
for (var name in this._events) {
count += this._events[name].length;
}
return count;
}
}]);
return TyphonEvents;
}(_Events3.default);
// Private / internal methods ---------------------------------------------------------------------------------------
/**
* Regular expression used to split event strings.
* @type {RegExp}
*/
exports.default = TyphonEvents;
var s_EVENT_SPLITTER = /\s+/;
/**
* Iterates over the standard `event, callback` (as well as the fancy multiple space-separated events `"change blur",
* callback` and jQuery-style event maps `{event: callback}`).
*
* @param {function} iteratee - Trigger API
* @param {function} iterateeTarget - Internal function which is dispatched to.
* @param {Array<*>} events - Array of stored event callback data.
* @param {string} name - Event name(s)
* @param {function} callback - callback
* @param {Object} opts - Optional parameters
* @returns {*}
*/
var s_EVENTS_API = function s_EVENTS_API(iteratee, iterateeTarget, events, name, callback, opts) {
var i = 0,
names = void 0;
if (name && (typeof name === 'undefined' ? 'undefined' : (0, _typeof3.default)(name)) === 'object') {
// Handle event maps.
if (callback !== void 0 && 'context' in opts && opts.context === void 0) {
opts.context = callback;
}
for (names = (0, _keys2.default)(name); i < names.length; i++) {
events = s_EVENTS_API(iteratee, iterateeTarget, events, names[i], name[names[i]], opts);
}
} else if (name && s_EVENT_SPLITTER.test(name)) {
// Handle space-separated event names by delegating them individually.
for (names = name.split(s_EVENT_SPLITTER); i < names.length; i++) {
events = iteratee(iterateeTarget, events, names[i], callback, opts);
}
} else {
// Finally, standard events.
events = iteratee(iterateeTarget, events, name, callback, opts);
}
return events;
};
/**
* Handles triggering the appropriate event callbacks.
*
* @param {function} iterateeTarget - Internal function which is dispatched to.
* @param {Array<*>} objEvents - Array of stored event callback data.
* @param {string} name - Event name(s)
* @param {function} cb - callback
* @param {Array<*>} args - Arguments supplied to a trigger method.
* @returns {*}
*/
var s_TRIGGER_API = function s_TRIGGER_API(iterateeTarget, objEvents, name, cb, args) {
var result = void 0;
if (objEvents) {
var events = objEvents[name];
var allEvents = objEvents.all;
if (events && allEvents) {
allEvents = allEvents.slice();
}
if (events) {
result = iterateeTarget(events, args);
}
if (allEvents) {
result = iterateeTarget(allEvents, [name].concat(args));
}
}
return result;
};
/**
* A difficult-to-believe, but optimized internal dispatch function for triggering events. Tries to keep the usual
* cases speedy (most internal Backbone events have 3 arguments). This dispatch method uses ES6 Promises and adds
* any returned results to an array which is added to a Promise.all construction which passes back a Promise which
* waits until all Promises complete. Any target invoked may return a Promise or any result. This is very useful to
* use for any asynchronous operations.
*
* @param {Array<*>} events - Array of stored event callback data.
* @param {Array<*>} args - Arguments supplied to `triggerAsync`.
* @returns {Promise}
*/
var s_TRIGGER_ASYNC_EVENTS = function () {
var _ref2 = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee2(events, args) {
var ev, i, a1, a2, a3, l, result, results;
return _regenerator2.default.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
ev = void 0, i = -1;
a1 = args[0], a2 = args[1], a3 = args[2], l = events.length;
result = void 0;
results = [];
_context2.prev = 4;
_context2.t0 = args.length;
_context2.next = _context2.t0 === 0 ? 8 : _context2.t0 === 1 ? 10 : _context2.t0 === 2 ? 12 : _context2.t0 === 3 ? 14 : 16;
break;
case 8:
while (++i < l) {
result = (ev = events[i]).callback.call(ev.ctx);
// If we received a valid result add it to the promises array.
if (result !== null || typeof result !== 'undefined') {
results.push(result);
}
}
return _context2.abrupt('break', 18);
case 10:
while (++i < l) {
result = (ev = events[i]).callback.call(ev.ctx, a1);
// If we received a valid result add it to the promises array.
if (result !== null || typeof result !== 'undefined') {
results.push(result);
}
}
return _context2.abrupt('break', 18);
case 12:
while (++i < l) {
result = (ev = events[i]).callback.call(ev.ctx, a1, a2);
// If we received a valid result add it to the promises array.
if (result !== null || typeof result !== 'undefined') {
results.push(result);
}
}
return _context2.abrupt('break', 18);
case 14:
while (++i < l) {
result = (ev = events[i]).callback.call(ev.ctx, a1, a2, a3);
// If we received a valid result add it to the promises array.
if (result !== null || typeof result !== 'undefined') {
results.push(result);
}
}
return _context2.abrupt('break', 18);
case 16:
while (++i < l) {
result = (ev = events[i]).callback.apply(ev.ctx, args);
// If we received a valid result add it to the promises array.
if (result !== null || typeof result !== 'undefined') {
results.push(result);
}
}
return _context2.abrupt('break', 18);
case 18:
_context2.next = 23;
break;
case 20:
_context2.prev = 20;
_context2.t1 = _context2['catch'](4);
return _context2.abrupt('return', _promise2.default.reject(_context2.t1));
case 23:
return _context2.abrupt('return', results.length > 1 ? _promise2.default.all(results) : _promise2.default.resolve(result));
case 24:
case 'end':
return _context2.stop();
}
}
}, _callee2, undefined, [[4, 20]]);
}));
return function s_TRIGGER_ASYNC_EVENTS(_x3, _x4) {
return _ref2.apply(this, arguments);
};
}();
/**
* A difficult-to-believe, but optimized internal dispatch function for triggering events. Tries to keep the usual
* cases speedy (most internal Backbone events have 3 arguments). This dispatch method synchronously passes back a
* single value or an array with all results returned by any invoked targets.
*
* @param {Array<*>} events - Array of stored event callback data.
* @param {Array<*>} args - Arguments supplied to `triggerSync`.
* @returns {*|Array<*>}
*/
var s_TRIGGER_SYNC_EVENTS = function s_TRIGGER_SYNC_EVENTS(events, args) {
var ev = void 0,
i = -1;
var a1 = args[0],
a2 = args[1],
a3 = args[2],
l = events.length;
var result = void 0;
var results = [];
switch (args.length) {
case 0:
while (++i < l) {
result = (ev = events[i]).callback.call(ev.ctx);
// If we received a valid result return immediately.
if (result !== null || typeof result !== 'undefined') {
results.push(result);
}
}
break;
case 1:
while (++i < l) {
result = (ev = events[i]).callback.call(ev.ctx, a1);
// If we received a valid result return immediately.
if (result !== null || typeof result !== 'undefined') {
results.push(result);
}
}
break;
case 2:
while (++i < l) {
result = (ev = events[i]).callback.call(ev.ctx, a1, a2);
// If we received a valid result return immediately.
if (result !== null || typeof result !== 'undefined') {
results.push(result);
}
}
break;
case 3:
while (++i < l) {
result = (ev = events[i]).callback.call(ev.ctx, a1, a2, a3);
// If we received a valid result return immediately.
if (result !== null || typeof result !== 'undefined') {
results.push(result);
}
}
break;
default:
while (++i < l) {
result = (ev = events[i]).callback.apply(ev.ctx, args);
// If we received a valid result return immediately.
if (result !== null || typeof result !== 'undefined') {
results.push(result);
}
}
break;
}
// Return the results array if there are more than one or just a single result.
return results.length > 1 ? results : result;
};
module.exports = exports['default'];