UNPKG

quick-event

Version:

quick-event is a TypeScript event library that provides tools that enable your application components to communicate with each other by dispatching events and listening for them. With eventpp you can easily implement signal/slot mechanism, or observer pat

1,276 lines (1,102 loc) 47.8 kB
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } /*! * quick-event v0.1.4 * https://github.com/ArcherGu/quick-event.git * @license MIT */ (function (global, factory) { (typeof exports === "undefined" ? "undefined" : _typeof(exports)) === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.QuickEvent = {})); })(this, function (exports) { 'use strict'; /** * CallbackNode is used to record the structure of callbacks.<br/> * The return value(`handle`) of some methods refers to CallbackNode. * * **Note:** Do not directly modify the structure of CallbackNode! * * @export * @class CallbackNode */ var CallbackNode = function CallbackNode(callback, counter) { _classCallCheck(this, CallbackNode); this.callback = callback; this.counter = counter; this.previous = null; this.next = null; }; /** * CallbackList is the fundamental class in quick-event. The other classes [EventDispatcher](https://archergu.github.io/quick-event/classes/event_dispatcher.eventdispatcher.html) and [EventQueue](https://archergu.github.io/quick-event/classes/event_queue.eventqueue.html) are built on CallbackList. * * CallbackList holds a list of callbacks. At the time of the call, CallbackList simply invokes each callback one by one. Consider CallbackList as the signal/slot system in Qt, or the callback function pointer in some Windows APIs (such as lpCompletionRoutine in ReadFileEx). * The *callback* can be any functions. * * ## Nested callback safety * 1. If a callback adds another callback to the callback list during a invoking, the new callback is guaranteed not to be triggered within the same invoking. This is guaranteed by an integer counter. This rule will be broken is the counter is overflowed to zero in a invoking, but this rule will continue working on the subsequence invoking.<br/> * 2. Any callbacks that are removed during a invoking are guaranteed not triggered.<br/> * * @export * @class CallbackList */ var CallbackList = /*#__PURE__*/function () { function CallbackList(params) { _classCallCheck(this, CallbackList); this._head = null; this._tail = null; this._currentCounter = 0; params = params || {}; this._canContinueInvoking = params.hasOwnProperty('canContinueInvoking') ? params.canContinueInvoking : null; this._argumentsAsArray = params.hasOwnProperty('argumentsAsArray') ? !!params.argumentsAsArray : false; if (this._argumentsAsArray) { this._dispatch = this._dispatchArgumentsAsArray; this._applyDispatch = this._applyDispatchArgumentsAsArray; } else { this._dispatch = this._dispatchNotArgumentsAsArray; this._applyDispatch = this._applyDispatchNotArgumentsAsArray; } } /** * The first [CallbackNode](https://archergu.github.io/quick-event/classes/callback_node.callbacknode.html) * * @readonly * @memberof CallbackList */ _createClass(CallbackList, [{ key: "head", get: function get() { return this._head; } /** * The last [CallbackNode](https://archergu.github.io/quick-event/classes/callback_node.callbacknode.html) * * @readonly * @memberof CallbackList */ }, { key: "tail", get: function get() { return this._tail; } /** * Add the *callback* to the callback list.<br/> * The callback is added to the end of the callback list.<br/> * Return a handle object that represents the callback. The handle can be used to remove this callback or to insert additional callbacks before this callback.<br/> * If `append` is called in another callback during the invoking of the callback list, the new callback is guaranteed not to be triggered during the same callback list invoking.<br/> * The time complexity is O(1). * * @param {Callback} callback * @returns {CallbackNode} * @memberof CallbackList */ }, { key: "append", value: function append(callback) { var node = new CallbackNode(callback, this._getNextCounter()); if (this._tail) { node.previous = this._tail; this._tail.next = node; this._tail = node; } else { this._head = node; this._tail = node; } return node; } /** * Add the *callback* to the callback list.<br/> * The callback is added to the beginning of the callback list.<br/> * Return a handle object that represents the callback. The handle can be used to remove this callback or to insert additional callbacks before this callback.<br/> * If `prepend` is called in another callback during the invoking of the callback list, the new callback is guaranteed not to be triggered during the same callback list invoking.<br/> * The time complexity is O(1). * * @param {Callback} callback * @returns {CallbackNode} * @memberof CallbackList */ }, { key: "prepend", value: function prepend(callback) { var node = new CallbackNode(callback, this._getNextCounter()); if (this._head) { node.next = this._head; this._head.previous = node; this._head = node; } else { this._head = node; this._tail = node; } return node; } /** * Insert the *callback* to the callback list before the callback *before*. If *before* is not found, *callback* is added at the end of the callback list.<br/> * *before* can be a callback function, or a handle object.<br/> * Return a handle object that represents the callback. The handle can be used to remove this callback or to insert additional callbacks before this callback.<br/> * If `insert` is called in another callback during the invoking of the callback list, the new callback is guaranteed not to be triggered during the same callback list invoking. * The time complexity is O(1). * * @param {Callback} callback * @param {(Callback | CallbackNode | null | undefined)} before * @returns {CallbackNode} * @memberof CallbackList */ }, { key: "insert", value: function insert(callback, before) { var beforeNode = this._doFindNode(before); if (!beforeNode) { return this.append(callback); } var node = new CallbackNode(callback, this._getNextCounter()); node.previous = beforeNode.previous; node.next = beforeNode; if (beforeNode.previous) { beforeNode.previous.next = node; } beforeNode.previous = node; if (beforeNode === this._head) { this._head = node; } return node; } /** * Remove the callback from the callback list.<br/> * *callback* can be a callback function, or a handle object.<br/> * Return true if the callback is removed successfully, false if the callback is not found.<br/> * The time complexity is O(1). * * @param {(Callback | CallbackNode | null | undefined)} handle * @returns {boolean} * @memberof CallbackList */ }, { key: "remove", value: function remove(handle) { var node = this._doFindNode(handle); if (!node) { return false; } if (node.next) { node.next.previous = node.previous; } if (node.previous) { node.previous.next = node.next; } if (this._head === node) { this._head = node.next; } if (this._tail === node) { this._tail = node.previous; } // Mark it as deleted node.counter = 0; return true; } /** * Return true if the callback list is empty. * * @returns {boolean} * @memberof CallbackList */ }, { key: "empty", value: function empty() { return !this._head; } /** * Return true if the callback list contains *callback*.<br/> * *callback* can be a callback function, or a handle object. * * @param {(Callback | CallbackNode | null | undefined)} handle * @returns {boolean} * @memberof CallbackList */ }, { key: "has", value: function has(handle) { return !!this._doFindNode(handle); } /** * Return true if the callback list contains any callback. * * @returns * @memberof CallbackList */ }, { key: "hasAny", value: function hasAny() { return !!this._head; } /** * Apply func to all callbacks.<br/> * The `func` receives one parameter which is the callback.<br/> * **Note**: the func can remove any callbacks, or add other callbacks, safely. * * @param {(callback: Callback) => any} func * @memberof CallbackList */ }, { key: "forEach", value: function forEach(func) { var node = this._head; var counter = this._currentCounter; while (node) { if (node.counter !== 0 && counter >= node.counter) { func(node.callback); } node = node.next; } } /** * Apply func to all callbacks. func must return a boolean value, and if the return value is false, forEachIf stops the looping immediately.<br/> * Return true if all callbacks are invoked, or event is not found, false if func returns false. * * @param {(callback: Callback) => any} func * @returns {boolean} * @memberof CallbackList */ }, { key: "forEachIf", value: function forEachIf(func) { var node = this._head; var counter = this._currentCounter; while (node) { if (node.counter !== 0 && counter >= node.counter) { if (!func(node.callback)) { return false; } } node = node.next; } return true; } /** * Invoke each callbacks in the callback list.<br/> * The callbacks are called with arguments `arg1`, `arg2`, etc. * * @param {...any[]} args * @memberof CallbackList */ }, { key: "dispatch", value: function dispatch() { this._dispatch.apply(this, arguments); } /** * Invoke each callbacks in the callback list.<br/> * The callbacks are called with arguments `arg1`, `arg2`, etc.<br/> * Note the arguments are passed in an array, similar to Function.prototype.apply. * * @param {...any[]} args * @memberof CallbackList */ }, { key: "applyDispatch", value: function applyDispatch() { this._applyDispatch.apply(this, arguments); } }, { key: "_getNextCounter", value: function _getNextCounter() { var result = ++this._currentCounter; if (result === 0) { // overflow, let's reset all nodes' counters. var node = this._head; while (node) { node.counter = 1; node = node.next; } result = ++this._currentCounter; } return result; } }, { key: "_doFindNode", value: function _doFindNode(handle) { var node = this._head; while (node) { if (node === handle || node.callback === handle) { return node; } node = node.next; } return null; } }, { key: "_dispatchArgumentsAsArray", value: function _dispatchArgumentsAsArray() { var counter = this._currentCounter; var node = this._head; var canContinueInvoking = this._canContinueInvoking; for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } while (node) { if (node.counter !== 0 && counter >= node.counter) { node.callback.call(this, args); if (canContinueInvoking && !canContinueInvoking.call(this, args)) { break; } } node = node.next; } } }, { key: "_dispatchNotArgumentsAsArray", value: function _dispatchNotArgumentsAsArray() { var counter = this._currentCounter; var node = this._head; var canContinueInvoking = this._canContinueInvoking; for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } while (node) { if (node.counter !== 0 && counter >= node.counter) { node.callback.apply(this, args); if (canContinueInvoking && !canContinueInvoking.apply(this, args)) { break; } } node = node.next; } } }, { key: "_applyDispatchArgumentsAsArray", value: function _applyDispatchArgumentsAsArray() { var counter = this._currentCounter; var node = this._head; var canContinueInvoking = this._canContinueInvoking; for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { args[_key3] = arguments[_key3]; } while (node) { if (node.counter !== 0 && counter >= node.counter) { node.callback.call(this, args); if (canContinueInvoking && !canContinueInvoking.call(this, args)) { break; } } node = node.next; } } }, { key: "_applyDispatchNotArgumentsAsArray", value: function _applyDispatchNotArgumentsAsArray() { var counter = this._currentCounter; var node = this._head; var canContinueInvoking = this._canContinueInvoking; for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { args[_key4] = arguments[_key4]; } while (node) { if (node.counter !== 0 && counter >= node.counter) { node.callback.apply(this, args); if (canContinueInvoking && !canContinueInvoking.apply(this, args)) { break; } } node = node.next; } } }]); return CallbackList; }(); /** * MixinFilter allows all events are filtered or modified before dispatching. * * `MixinFilter.appendFilter(filter)` adds an event filter to the dispatcher. The `filter` receives an array same as mixinBeforeDispatch receives. * * The event filters are invoked for all events, and invoked before any listeners are invoked. * The event filters can modify the arguments since the arguments are passed in the array. * * Event filter is a powerful and useful technology, below is some sample use cases, though the real world use cases are unlimited. * * 1, Capture and block all interested events. For example, in a GUI window system, all windows can receive mouse events. However, when a window is under mouse dragging, only the window under dragging should receive the mouse events even when the mouse is moving on other window. So when the dragging starts, the window can add a filter. The filter redirects all mouse events to the window and prevent other listeners from the mouse events, and bypass all other events. * * 2, Setup catch-all event listener. For example, in a phone book system, the system sends events based on the actions, such as adding a phone number, remove a phone number, look up a phone number, etc. A module may be only interested in special area code of a phone number, not the actions. One approach is the module can listen to all possible events (add, remove, look up), but this is very fragile -- how about a new action event is added and the module forgets to listen on it? The better approach is the module add a filter and check the area code in the filter. * @export * @class MixinFilter * @example ```javascript * let dispatcher = new EventDispatcher({ * mixins: [ new MixinFilter() ], * argumentPassingMode: EventDispatcher.argumentPassingIncludeEvent * }); * * dispatcher.appendListener(3, (e, i, s) => { * console.log("Got event 3, i was 1 but actural is", i, "s was Hello but actural is", s); * }); * dispatcher.appendListener(5, () => { * console.log("Shout not got event 5"); * }); * * // Add three event filters. * * // The first filter modifies the input arguments to other values, then the subsequence filters * // and listeners will see the modified values. * dispatcher.appendFilter((args) => { * console.log("Filter 1, e is", args[0], "passed in i is", args[1], "s is", args[2]); * args[1] = 38; * args[2] = "Hi"; * console.log("Filter 1, changed i is", args[1], "s is", args[2]); * return true; * }); * * // The second filter filters out all event of 5. So no listeners on event 5 can be triggered. * // The third filter is not invoked on event 5 also. * dispatcher.appendFilter((args) => { * console.log("Filter 2, e is", args[0], "passed in i is", args[1], "s is", args[2]); * if(args[0] === 5) { * return false; * } * return true; * }); * * // The third filter just prints the input arguments. * dispatcher.appendFilter((args) => { * console.log("Filter 3, e is", args[0], "passed in i is", args[1], "s is", args[2]); * return true; * }); * * // Dispatch the events, the first argument is always the event type. * dispatcher.dispatch(3, 1, "Hello"); * dispatcher.dispatch(5, 2, "World"); * * // Output * // > Filter 1, e is 3 passed in i is 1 s is Hello * // > Filter 1, changed i is 38 s is Hi * // > Filter 2, e is 3 passed in i is 38 s is Hi * // > Filter 3, e is 3 passed in i is 38 s is Hi * // > Got event 3, i was 1 but actural is 38 s was Hello but actural is Hi * // > Filter 1, e is 5 passed in i is 2 s is World * // > Filter 1, changed i is 38 s is Hi * // > Filter 2, e is 5 passed in i is 38 s is Hi * ``` */ var MixinFilter = /*#__PURE__*/function () { function MixinFilter(params) { _classCallCheck(this, MixinFilter); this.filterList = new CallbackList(params); } /** * Add the *filter* to the dispatcher.<br/> * Return a handle which can be used in removeFilter. * * @param {Filter} filter * @returns {CallbackNode} * @memberof MixinFilter */ _createClass(MixinFilter, [{ key: "appendFilter", value: function appendFilter(filter) { return this.filterList.append(filter); } /** * Remove a filter from the dispatcher.<br/> * `filter` can be either the filter callback or the handle returned by `appendFilter`.<br/> * Return true if the filter is removed successfully. * * @param {(Filter | CallbackNode)} handle * @returns {boolean} * @memberof MixinFilter */ }, { key: "removeFilter", value: function removeFilter(handle) { return this.filterList.remove(handle); } }, { key: "mixinBeforeDispatch", value: function mixinBeforeDispatch(args) { if (!this.filterList.empty()) { if (!this.filterList.forEachIf(function (callback) { return callback.call(callback, args); })) { return false; } } return true; } }]); return MixinFilter; }(); var SimplePropertyRetriever = /*#__PURE__*/function () { function SimplePropertyRetriever() { _classCallCheck(this, SimplePropertyRetriever); } _createClass(SimplePropertyRetriever, null, [{ key: "getOwnEnumerables", value: function getOwnEnumerables(obj) { return this._getPropertyNames(obj, true, false, this._enumerable); } }, { key: "getOwnNonenumerables", value: function getOwnNonenumerables(obj) { return this._getPropertyNames(obj, true, false, this._notEnumerable); } }, { key: "getOwnEnumerablesAndNonenumerables", value: function getOwnEnumerablesAndNonenumerables(obj) { return this._getPropertyNames(obj, true, false, this._enumerableAndNotEnumerable); } }, { key: "getPrototypeEnumerables", value: function getPrototypeEnumerables(obj) { return this._getPropertyNames(obj, false, true, this._enumerable); } }, { key: "getPrototypeNonenumerables", value: function getPrototypeNonenumerables(obj) { return this._getPropertyNames(obj, false, true, this._notEnumerable); } }, { key: "getPrototypeEnumerablesAndNonenumerables", value: function getPrototypeEnumerablesAndNonenumerables(obj) { return this._getPropertyNames(obj, false, true, this._enumerableAndNotEnumerable); } }, { key: "getOwnAndPrototypeEnumerables", value: function getOwnAndPrototypeEnumerables(obj) { return this._getPropertyNames(obj, true, true, this._enumerable); } }, { key: "getOwnAndPrototypeNonenumerables", value: function getOwnAndPrototypeNonenumerables(obj) { return this._getPropertyNames(obj, true, true, this._notEnumerable); } }, { key: "getOwnAndPrototypeEnumerablesAndNonenumerables", value: function getOwnAndPrototypeEnumerablesAndNonenumerables(obj) { return this._getPropertyNames(obj, true, true, this._enumerableAndNotEnumerable); } }, { key: "_enumerable", value: function _enumerable(obj, prop) { return obj.propertyIsEnumerable(prop); } }, { key: "_notEnumerable", value: function _notEnumerable(obj, prop) { return !obj.propertyIsEnumerable(prop); } }, { key: "_enumerableAndNotEnumerable", value: function _enumerableAndNotEnumerable(obj, prop) { return true; } }, { key: "_getPropertyNames", value: function _getPropertyNames(obj, iterateSelfBool, iteratePrototypeBool, includePropCb) { var props = []; do { if (iterateSelfBool) { Object.getOwnPropertyNames(obj).forEach(function (prop) { if (props.indexOf(prop) === -1 && includePropCb(obj, prop)) { props.push(prop); } }); } if (!iteratePrototypeBool) { break; } iterateSelfBool = true; // tslint:disable-next-line:no-conditional-assignment } while (obj = Object.getPrototypeOf(obj)); return props; } }]); return SimplePropertyRetriever; }(); exports.ArgumentPassingMode = void 0; (function (ArgumentPassingMode) { ArgumentPassingMode[ArgumentPassingMode["IncludeEvent"] = 1] = "IncludeEvent"; ArgumentPassingMode[ArgumentPassingMode["ExcludeEvent"] = 2] = "ExcludeEvent"; })(exports.ArgumentPassingMode || (exports.ArgumentPassingMode = {})); function _extend(destination, source) { var allSourceProperty = SimplePropertyRetriever.getOwnAndPrototypeEnumerablesAndNonenumerables(source); var allObjectProperty = SimplePropertyRetriever.getOwnAndPrototypeEnumerablesAndNonenumerables(new Object()); var customSourceProperty = allSourceProperty.filter(function (val) { return allObjectProperty.indexOf(val) === -1; }); var _iterator = _createForOfIteratorHelper(customSourceProperty), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var k = _step.value; destination[k] = source[k]; } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return destination; } /** * EventDispatcher is something like a map between the `EventType` and `CallbackList`. * * EventDispatcher holds a map of `<EventType, CallbackList>` pairs. On dispatching, EventDispatcher finds the CallbackList of the event type, then invoke the callback list. The invocation is always synchronous. The listeners are triggered when [EventDispatcher.dispatch](https://archergu.github.io/quick-event/classes/event_dispatcher.eventdispatcher.html#dispatch) is called. * * ## Nested listener safety * 1. If a listener adds another listener of the same event to the dispatcher during a dispatching, the new listener is guaranteed not to be triggered within the same dispatching. This is guaranteed by an unsigned 64 bits integer counter. This rule will be broken is the counter is overflowed to zero in a dispatching, but this rule will continue working on the subsequence dispatching.<br/> * 2. Any listeners that are removed during a dispatching are guaranteed not triggered. * @export * @class EventDispatcher */ var EventDispatcher = /*#__PURE__*/function () { function EventDispatcher(params) { _classCallCheck(this, EventDispatcher); this._eventCallbackListMap = {}; params = params || {}; this._params = params; this._getEvent = typeof params.getEvent === 'function' ? params.getEvent : null; this._argumentPassingMode = params.argumentPassingMode ? params.argumentPassingMode : EventDispatcher.defaultArgumentPassingMode; this.argumentsAsArray = params.argumentsAsArray ? !!params.argumentsAsArray : false; this._mixins = params.mixins ? params.mixins : []; var _iterator2 = _createForOfIteratorHelper(this._mixins), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var mixin = _step2.value; _extend(this, mixin); } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } } /** * Add the *callback* to the dispatcher to listen to *event*.<br/> * The listener is added to the end of the listener list.<br/> * Return a handle object which represents the listener. The handle can be used to remove this listener or insert other listener before this listener.<br/> * If `appendListener` is called in another listener during a dispatching, the new listener is guaranteed not triggered during the same dispatching.<br/> * If the same callback is added twice, it results duplicated listeners.<br/> * The time complexity is O(1). * * @param {(string | number)} event * @param {Callback} callback * @returns {CallbackNode} * @memberof EventDispatcher */ _createClass(EventDispatcher, [{ key: "appendListener", value: function appendListener(event, callback) { return this._doGetCallbackList(event, true).append(callback); } /** * Add the *callback* to the dispatcher to listen to *event*.<br/> * The listener is added to the beginning of the listener list.<br/> * Return a handle object which represents the listener. The handle can be used to remove this listener or insert other listener before this listener.<br/> * If `prependListener` is called in another listener during a dispatching, the new listener is guaranteed not triggered during the same dispatching.<br/> * The time complexity is O(1). * * @param {(string | number)} event * @param {Callback} callback * @returns {CallbackNode} * @memberof EventDispatcher */ }, { key: "prependListener", value: function prependListener(event, callback) { return this._doGetCallbackList(event, true).prepend(callback); } /** * Insert the *callback* to the dispatcher to listen to *event* before the listener handle *before*. If *before* is not found, *callback* is added at the end of the listener list.<br/> * *before* can be a callback function, or a handle object.<br/> * Return a handle object which represents the listener. The handle can be used to remove this listener or insert other listener before this listener.<br/> * If `insertListener` is called in another listener during a dispatching, the new listener is guaranteed not triggered during the same dispatching.<br/> * The time complexity is O(1). * * @param {(string | number)} event * @param {Callback} callback * @param {(Callback | CallbackNode | null | undefined)} before * @returns {CallbackNode} * @memberof EventDispatcher */ }, { key: "insertListener", value: function insertListener(event, callback, before) { return this._doGetCallbackList(event, true).insert(callback, before); } /** * Remove the listener *callback* which listens to *event* from the dispatcher.<br/> * *callback* can be a callback function, or a handle object.<br/> * Return true if the listener is removed successfully, false if the listener is not found.<br/> * The time complexity is O(1). * * @param {(string | number)} event * @param {(Callback | CallbackNode | null | undefined)} handle * @returns {boolean} * @memberof EventDispatcher */ }, { key: "removeListener", value: function removeListener(event, handle) { var cbList = this._doGetCallbackList(event, false); if (cbList) { return cbList.remove(handle); } return false; } /** * Return true if the dispatcher contains *callback*.<br/> * *callback* can be a callback function, or a handle object. * * @param {(string | number)} event * @param {(Callback | CallbackNode | null | undefined)} handle * @returns {boolean} * @memberof EventDispatcher */ }, { key: "hasListener", value: function hasListener(event, handle) { var cbList = this._doGetCallbackList(event, false); if (cbList) { return cbList.has(handle); } return false; } /** * Return true if the dispatcher contains any callback. * * @param {(string | number)} event * @returns {boolean} * @memberof EventDispatcher */ }, { key: "hasAnyListener", value: function hasAnyListener(event) { var cbList = this._doGetCallbackList(event, false); if (cbList) { return cbList.hasAny(); } return false; } /** * Dispatch an event.<br/> * The listeners are called with arguments `arg1`, `arg2`, etc. * * @param {...any[]} args * @memberof EventDispatcher */ }, { key: "dispatch", value: function dispatch() { for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { args[_key5] = arguments[_key5]; } this.applyDispatch(args); } /** * Dispatch an event.<br/> * The listeners are called with arguments `arg1`, `arg2`, etc.<br/> * Note the arguments are passed in an array, similar to Function.prototype.apply. * * @param {any[]} args * @returns * @memberof EventDispatcher */ }, { key: "applyDispatch", value: function applyDispatch(args) { var _iterator3 = _createForOfIteratorHelper(this._mixins), _step3; try { for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { var mixin = _step3.value; if (mixin.mixinBeforeDispatch && !mixin.mixinBeforeDispatch.call(this, args)) { return; } } } catch (err) { _iterator3.e(err); } finally { _iterator3.f(); } if (this._getEvent) { var event; if (this.argumentsAsArray) { event = this._getEvent.call(this, args); } else { event = this._getEvent.apply(this, args); } var cbList = this._doGetCallbackList(event, false); if (cbList) { if (this._argumentPassingMode === EventDispatcher.argumentPassingIncludeEvent) { args = [event].concat(_toConsumableArray(args)); } cbList.dispatch.apply(cbList, args); } } else { var _cbList = this._doGetCallbackList(args[0], false); if (_cbList) { if (this._argumentPassingMode === EventDispatcher.argumentPassingExcludeEvent) { args = Array.prototype.slice.call(args, 1); } _cbList.dispatch.apply(_cbList, args); } } } /** * Apply `func` to all listeners of `event`.<br/> * The `func` receives one parameter which is the callback.<br/> * **Note**: the `func` can remove any listeners, or add other listeners, safely. * * @param {(string | number)} event * @param {(callback: Callback) => any} func * @memberof EventDispatcher */ }, { key: "forEach", value: function forEach(event, func) { var cbList = this._doGetCallbackList(event, false); if (cbList) { cbList.forEach(func); } } /** * Apply `func` to all listeners of `event`. `func` must return a boolean value, and if the return value is false, forEachIf stops the looping immediately.<br/> * Return `true` if all listeners are invoked, or `event` is not found, `false` if `func` returns `false`. * * @param {(string | number)} event * @param {(callback: Callback) => any} func * @returns {boolean} * @memberof EventDispatcher */ }, { key: "forEachIf", value: function forEachIf(event, func) { var cbList = this._doGetCallbackList(event, false); if (cbList) { return cbList.forEachIf(func); } return true; } }, { key: "_doGetCallbackList", value: function _doGetCallbackList(event, createOnNotFound) { if (this._eventCallbackListMap.hasOwnProperty(event)) { return this._eventCallbackListMap[event]; } if (createOnNotFound) { var cbList = new CallbackList(this._params); this._eventCallbackListMap[event] = cbList; return cbList; } return null; } }]); return EventDispatcher; }(); /** * ArgumentPassingMode: IncludeEvent * @static * @readonly * @memberof EventDispatcher */ EventDispatcher.argumentPassingIncludeEvent = exports.ArgumentPassingMode.IncludeEvent; /** * ArgumentPassingMode: ExcludeEvent * @static * @readonly * @memberof EventDispatcher */ EventDispatcher.argumentPassingExcludeEvent = exports.ArgumentPassingMode.ExcludeEvent; /** * ArgumentPassingMode: ExcludeEvent * @static * @readonly * @memberof EventDispatcher */ EventDispatcher.defaultArgumentPassingMode = exports.ArgumentPassingMode.ExcludeEvent; /** * EventQueue includes all features of [EventDispatcher](https://archergu.github.io/quick-event/classes/event_dispatcher.eventdispatcher.html) and adds event queue features.<br/> * EventQueue is asynchronous. Events are cached in the queue when [EventQueue.enqueue](https://archergu.github.io/quick-event/classes/event_queue.eventqueue.html#enqueue) is called, and dispatched later when [EventQueue.process](https://archergu.github.io/quick-event/classes/event_queue.eventqueue.html#process) is called.<br/> * EventQueue is equivalent to the event system (QEvent) in Qt, or the message processing in Windows API. * @export * @class EventQueue * @extends {EventDispatcher} */ var EventQueue = /*#__PURE__*/function (_EventDispatcher) { _inherits(EventQueue, _EventDispatcher); var _super = _createSuper(EventQueue); function EventQueue(params) { var _this; _classCallCheck(this, EventQueue); _this = _super.call(this, params); _this._queueList = []; return _this; } /** * Put an event into the event queue.<br/> * All arguments are copied to internal data structure.<br/> * The time complexity is O(1). * * @param {...any[]} args * @memberof EventQueue */ _createClass(EventQueue, [{ key: "enqueue", value: function enqueue() { for (var _len6 = arguments.length, args = new Array(_len6), _key6 = 0; _key6 < _len6; _key6++) { args[_key6] = arguments[_key6]; } this._queueList.push(args); } /** * Process the event queue. All events in the event queue are dispatched once and then removed from the queue.<br/> * The function returns true if any events were processed, false if no event was processed.<br/> * Any new events added to the queue during `process()` are not dispatched during current `process()`. * * @memberof EventQueue */ }, { key: "process", value: function process() { var list = this._queueList; this._queueList = []; var _iterator4 = _createForOfIteratorHelper(list), _step4; try { for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) { var item = _step4.value; this.applyDispatch(item); } } catch (err) { _iterator4.e(err); } finally { _iterator4.f(); } } /** * Process one event in the event queue. The first event in the event queue is dispatched once and then removed from the queue.<br/> * The function returns true if one event was processed, false if no event was processed.<br/> * Any new events added to the queue during `processOne()` are not dispatched during current `processOne()`. * * @memberof EventQueue */ }, { key: "processOne", value: function processOne() { if (this._queueList.length > 0) { this.applyDispatch(this._queueList.shift()); } } /** * Process the event queue. Before processing an event, the event is passed to `func` and the event will be processed only if `func` returns true.<br/> * `func` takes exactly the same arguments as `EventQueue.enqueue`, and returns a boolean value.<br/> * `processIf` returns true if any event was dispatched, false if no event was dispatched.<br/> * `processIf` has some good use scenarios:<br/> * 1. Process certain events. For example, in a GUI application, the UI related events may be only desired to processed by one module.<br/> * 2. Process the events until certain time. For example, in a game engine, the event process may be limited to only several milliseconds, the remaining events will be process in next game loop. * * @param {(...args: any[]) => boolean} func * @memberof EventQueue */ }, { key: "processIf", value: function processIf(func) { var list = this._queueList; this._queueList = []; var unprocessedList = []; var _iterator5 = _createForOfIteratorHelper(list), _step5; try { for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) { var item = _step5.value; var ok = false; if (this.argumentsAsArray) { ok = func.call(this, item); } else { ok = func.apply(this, item); } if (ok) { this.applyDispatch(item); } else { unprocessedList.push(item); } } } catch (err) { _iterator5.e(err); } finally { _iterator5.f(); } if (unprocessedList.length > 0) { this._queueList = this._queueList.concat(unprocessedList); } } /** * Return true if there is no any event in the event queue, false if there are any events in the event queue. * * @returns * @memberof EventQueue */ }, { key: "empty", value: function empty() { return this._queueList.length === 0; } /** * Clear all queued events without dispatching them. * * @memberof EventQueue */ }, { key: "clearEvents", value: function clearEvents() { this._queueList.length = 0; } /** * Return a queued event from the queue.<br/> * A queued event is an array with all arguments passed to [EventQueue.enqueue](https://archergu.github.io/quick-event/classes/event_queue.eventqueue.html#enqueue).<br/> * If the queue is empty, the function returns null.<br/> * After the function returns, the original even is still in the queue. * @returns * @memberof EventQueue */ }, { key: "peekEvent", value: function peekEvent() { return this._queueList[0]; } /** * Return an event from the queue and remove the original event from the queue.<br/> * If the queue is empty, the function returns null.<br/> * After the function returns, the original even is removed from the queue. * * @returns * @memberof EventQueue */ }, { key: "takeEvent", value: function takeEvent() { if (this._queueList.length > 0) { return this._queueList.shift(); } return null; } }]); return EventQueue; }(EventDispatcher); exports.CallbackList = CallbackList; exports.CallbackNode = CallbackNode; exports.EventDispatcher = EventDispatcher; exports.EventQueue = EventQueue; exports.MixinFilter = MixinFilter; Object.defineProperty(exports, '__esModule', { value: true }); });