UNPKG

backbone-esnext-events

Version:

Separates 'events' support from Backbone in addition to adding TyphonJS extensions.

586 lines (480 loc) 19.4 kB
'use strict'; 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'];