diff --git a/h5p.classes.php b/h5p.classes.php index 827903a..d0fd2c8 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1534,6 +1534,7 @@ class H5PCore { public static $scripts = array( 'js/jquery.js', 'js/h5p.js', + 'js/h5p-event-dispatcher.js', ); public static $adminScripts = array( 'js/jquery.js', diff --git a/js/h5p-event-dispatcher.js b/js/h5p-event-dispatcher.js new file mode 100644 index 0000000..147024d --- /dev/null +++ b/js/h5p-event-dispatcher.js @@ -0,0 +1,135 @@ +/** @namespace H5P */ +var H5P = H5P || {}; + +H5P.EventDispatcher = (function () { + + /** + * The base of the event system. + * Inherit this class if you want your H5P to dispatch events. + * @class + */ + function EventDispatcher() { + var self = this; + + /** + * Keep track of events and listeners for each event. + * @private + * @type {Object} + */ + var events = {}; + + /** + * Add new event listener. + * + * @public + * @throws {TypeError} listener must be a function + * @param {String} type Event type + * @param {Function} listener Event listener + */ + self.on = function (type, listener) { + if (!(listener instanceof Function)) { + throw TypeError('listener must be a function'); + } + + // Trigger event before adding to avoid recursion + self.trigger('newListener', type, listener); + + if (!events[type]) { + // First + events[type] = [listener]; + } + else { + // Append + events[type].push(listener); + } + }; + + /** + * Add new event listener that will be fired only once. + * + * @public + * @throws {TypeError} listener must be a function + * @param {String} type Event type + * @param {Function} listener Event listener + */ + self.once = function (type, listener) { + if (!(listener instanceof Function)) { + throw TypeError('listener must be a function'); + } + + var once = function () { + self.off(type, once); + listener.apply(self, arguments); + }; + + self.on(type, once); + }; + + /** + * Remove event listener. + * If no listener is specified, all listeners will be removed. + * + * @public + * @throws {TypeError} listener must be a function + * @param {String} type Event type + * @param {Function} [listener] Event listener + */ + self.off = function (type, listener) { + if (listener !== undefined && !(listener instanceof Function)) { + throw TypeError('listener must be a function'); + } + + if (events[type] === undefined) { + return; + } + + if (listener === undefined) { + // Remove all listeners + delete events[type]; + self.trigger('removeListener', type); + return; + } + + // Find specific listener + for (var i = 0; i < events[type].length; i++) { + if (events[type][i] === listener) { + events[type].unshift(i, 1); + self.trigger('removeListener', type, listener); + break; + } + } + + // Clean up empty arrays + if (!events[type].length) { + delete events[type]; + } + }; + + /** + * Dispatch event. + * + * @public + * @param {String} type Event type + * @param {...*} args + */ + self.trigger = function (type, args) { + if (events[type] === undefined) { + return; + } + + // Copy all arguments except the first + args = []; + var i; + for (i = 1; i < arguments.length; i++) { + args.push(arguments[i]); + } + + // Call all listeners + for (i = 0; i < events[type].length; i++) { + events[type][i].apply(self, args); + } + }; + } + + return EventDispatcher; +})();