diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 0000000..c88f1fd --- /dev/null +++ b/.bowerrc @@ -0,0 +1,4 @@ +{ + "registry": "https://bower.herokuapp.com" + +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1a0a316 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,23 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[*.json] +indent_size = 2 +insert_final_newline = false + +[src/**/*] +trim_trailing_whitespace = false + +[.npmrc] +insert_final_newline = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d6dcc2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,115 @@ +# Created by .ignore support plugin (hsz.mobi) +### OSX template +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml +.idea/ +.idea/* + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +### Node template +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +/.idea/ +/bower_components/ diff --git a/.jscsrc b/.jscsrc new file mode 100644 index 0000000..f8bf9ba --- /dev/null +++ b/.jscsrc @@ -0,0 +1,6 @@ +{ + "requireCamelCaseOrUpperCaseIdentifiers": true, + "requireCapitalizedConstructors": true, + "requireParenthesesAroundIIFE": true, + "validateQuoteMarks": "'" +} diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..9797d31 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,13 @@ +{ + "bitwise": true, + "browser": true, + "curly": true, + "eqeqeq": true, + "esnext": true, + "latedef": true, + "noarg": true, + "node": true, + "strict": true, + "undef": true, + "unused": true +} diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..7f1e1f9 --- /dev/null +++ b/.npmignore @@ -0,0 +1,120 @@ +# Created by .ignore support plugin (hsz.mobi) +### OSX template +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml +.idea/ +.idea/* + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +### Node template +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +/.idea/ +/bower_components/ + +dist_browser/ +bower.json +.bowerrc +bin/EventEmitter.browser.js diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..e1531a3 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +registry = https://registry.npmjs.org \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..94c36d0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2016, Lilian Saget-Lethias + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4c6f4d1 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# eventemitter-promise + +Event Emitter with lazy promise ! (see http://github.com/bios21/superlazypromise) + +## Installation + +Then install it via npm: + +``` bash +$ npm install --save eventemitter-promise +``` + +Or via bower: + +``` bash +$ bower install --save eventemitter-promise +``` + +## Example Usage + +Each listener are stored to be fired later on a `SuperLazyPromise` + +| emit\on | event | event.* | event.context | event.context! | +|:--------------:|:-----:|:-------:|:-------------:|:--------------:| +| event | ✓ | ✓ | ✓ | ✓ | +| event.context | ✓ | | ✓ | ✓ | +| event.context! | | | ✓ | ✓ | + +**!** : Firing an event on strict context (`event.context!`) is not yet implemented. + +``` js +const EventEmitter = require('eventemitter-promise'); + +class Clazz extends EventEmitter { + emit() { + } + on() { + } +} +``` + + +License +---- + +ISC License. See the `LICENSE` file. diff --git a/bin/EventEmitter.browser.js b/bin/EventEmitter.browser.js new file mode 100644 index 0000000..3713aee --- /dev/null +++ b/bin/EventEmitter.browser.js @@ -0,0 +1,189 @@ +'use strict'; + +var _createClass = function () { 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); } } return function (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"); } } + +var _module = null, + _exports = null; +if (module && module.exports) { + _exports = module.exports; + _module = { + SuperLazyPromise: require('superlazypromise').SuperLazyPromise + }; +} else { + _exports = window; + _module = _exports; +} + +(function (module, exports) { + var SuperLazyPromise = module.SuperLazyPromise; + + var EMPTY_FN = function EMPTY_FN() {}, + NS_SEPARATOR = '.'; + + var EventEmitter = function () { + function EventEmitter() { + _classCallCheck(this, EventEmitter); + + this._promises = new Map(); + } + + _createClass(EventEmitter, [{ + key: 'on', + value: function on(event) { + var SLP = this._promises.get(event), + hasContext = event.includes(NS_SEPARATOR), + splitEvent = event.split(NS_SEPARATOR), + promiseMainEvent = this._promises.get(splitEvent[0]); + + if (hasContext) { + if (SLP) { + if (!promiseMainEvent) { + promiseMainEvent = new SuperLazyPromise(SLP.fn); + promiseMainEvent.updatePromise(SLP.promise); + this._promises.set(splitEvent[0], promiseMainEvent); + } + } else { + if (promiseMainEvent) { + SLP = new SuperLazyPromise(promiseMainEvent.fn); + SLP.updatePromise(promiseMainEvent.promise); + this._promises.set(event, SLP); + } else { + this._promises.set(splitEvent[0], new SuperLazyPromise(EMPTY_FN)); + this._promises.set(event, new SuperLazyPromise(EMPTY_FN)); + } + } + } else { + if (!SLP) { + this._promises.set(event, new SuperLazyPromise(EMPTY_FN)); + } + } + return this._promises.get(event); + } + }, { + key: 'off', + value: function off(event) { + if (!event.includes(NS_SEPARATOR)) { + this._destroyPromise(event); + } else { + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = this._promises[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var keyVal = _step.value; + + if (keyVal[0].split(NS_SEPARATOR)[0] === event) { + this._destroyPromise(keyVal[0]); + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + } + } + }, { + key: '_destroyPromise', + value: function _destroyPromise(event) { + this._promises[event].kill(); + delete this._promises[event]; + } + }, { + key: 'emit', + value: function emit(event) { + for (var _len = arguments.length, data = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + data[_key - 1] = arguments[_key]; + } + + var executor = function executor(ok, ko) { + return ok.apply(undefined, data); + }; + + var SLP = this._promises.get(event), + hasContext = event.includes(NS_SEPARATOR), + splitEvent = event.split(NS_SEPARATOR), + promiseMainEvent = this._promises.get(splitEvent[0]); + + if (hasContext) { + if (SLP) { + SLP.awake(executor); + if (promiseMainEvent) { + promiseMainEvent.awake(executor); + } else { + promiseMainEvent = new SuperLazyPromise(EMPTY_FN); + promiseMainEvent.updatePromise(SLP.promise); + this._promises.set(splitEvent[0], promiseMainEvent); + } + } else { + if (promiseMainEvent) { + promiseMainEvent.awake(executor); + SLP = new SuperLazyPromise(EMPTY_FN); + SLP.updatePromise(promiseMainEvent.promise); + } else { + promiseMainEvent = new SuperLazyPromise(executor); + promiseMainEvent.awake(); + SLP = new SuperLazyPromise(EMPTY_FN); + SLP.updatePromise(promiseMainEvent.promise); + } + } + } else { + if (SLP) { + SLP.awake(executor); + } else { + SLP = new SuperLazyPromise(executor); + SLP.awake(); + this._promises.set(event, SLP); + } + + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = this._promises[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + var keyVal = _step2.value; + + if (keyVal[0].includes(NS_SEPARATOR) && keyVal[0].split(NS_SEPARATOR)[0] === event) { + var subEvent = keyVal[0], + subPromise = this._promises.get(subEvent); + subPromise.updatePromise(SLP.promise); + subPromise.awake(); + } + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2.return) { + _iterator2.return(); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } + } + } + }]); + + return EventEmitter; + }(); + + exports.EventEmitter = EventEmitter; +})(_module, _exports); +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkV2ZW50RW1pdHRlci5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6Ii4uL2Jpbi9FdmVudEVtaXR0ZXIuYnJvd3Nlci5qcyIsInNvdXJjZXNDb250ZW50IjpbIid1c2Ugc3RyaWN0JztcblxudmFyIF9jcmVhdGVDbGFzcyA9IGZ1bmN0aW9uICgpIHsgZnVuY3Rpb24gZGVmaW5lUHJvcGVydGllcyh0YXJnZXQsIHByb3BzKSB7IGZvciAodmFyIGkgPSAwOyBpIDwgcHJvcHMubGVuZ3RoOyBpKyspIHsgdmFyIGRlc2NyaXB0b3IgPSBwcm9wc1tpXTsgZGVzY3JpcHRvci5lbnVtZXJhYmxlID0gZGVzY3JpcHRvci5lbnVtZXJhYmxlIHx8IGZhbHNlOyBkZXNjcmlwdG9yLmNvbmZpZ3VyYWJsZSA9IHRydWU7IGlmIChcInZhbHVlXCIgaW4gZGVzY3JpcHRvcikgZGVzY3JpcHRvci53cml0YWJsZSA9IHRydWU7IE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0YXJnZXQsIGRlc2NyaXB0b3Iua2V5LCBkZXNjcmlwdG9yKTsgfSB9IHJldHVybiBmdW5jdGlvbiAoQ29uc3RydWN0b3IsIHByb3RvUHJvcHMsIHN0YXRpY1Byb3BzKSB7IGlmIChwcm90b1Byb3BzKSBkZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLnByb3RvdHlwZSwgcHJvdG9Qcm9wcyk7IGlmIChzdGF0aWNQcm9wcykgZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvciwgc3RhdGljUHJvcHMpOyByZXR1cm4gQ29uc3RydWN0b3I7IH07IH0oKTtcblxuZnVuY3Rpb24gX2NsYXNzQ2FsbENoZWNrKGluc3RhbmNlLCBDb25zdHJ1Y3RvcikgeyBpZiAoIShpbnN0YW5jZSBpbnN0YW5jZW9mIENvbnN0cnVjdG9yKSkgeyB0aHJvdyBuZXcgVHlwZUVycm9yKFwiQ2Fubm90IGNhbGwgYSBjbGFzcyBhcyBhIGZ1bmN0aW9uXCIpOyB9IH1cblxudmFyIF9tb2R1bGUgPSBudWxsLFxuICAgIF9leHBvcnRzID0gbnVsbDtcbmlmIChtb2R1bGUgJiYgbW9kdWxlLmV4cG9ydHMpIHtcbiAgICBfZXhwb3J0cyA9IG1vZHVsZS5leHBvcnRzO1xuICAgIF9tb2R1bGUgPSB7XG4gICAgICAgIFN1cGVyTGF6eVByb21pc2U6IHJlcXVpcmUoJ3N1cGVybGF6eXByb21pc2UnKS5TdXBlckxhenlQcm9taXNlXG4gICAgfTtcbn0gZWxzZSB7XG4gICAgX2V4cG9ydHMgPSB3aW5kb3c7XG4gICAgX21vZHVsZSA9IF9leHBvcnRzO1xufVxuXG4oZnVuY3Rpb24gKG1vZHVsZSwgZXhwb3J0cykge1xuICAgIHZhciBTdXBlckxhenlQcm9taXNlID0gbW9kdWxlLlN1cGVyTGF6eVByb21pc2U7XG5cbiAgICB2YXIgRU1QVFlfRk4gPSBmdW5jdGlvbiBFTVBUWV9GTigpIHt9LFxuICAgICAgICBOU19TRVBBUkFUT1IgPSAnLic7XG5cbiAgICB2YXIgRXZlbnRFbWl0dGVyID0gZnVuY3Rpb24gKCkge1xuICAgICAgICBmdW5jdGlvbiBFdmVudEVtaXR0ZXIoKSB7XG4gICAgICAgICAgICBfY2xhc3NDYWxsQ2hlY2sodGhpcywgRXZlbnRFbWl0dGVyKTtcblxuICAgICAgICAgICAgdGhpcy5fcHJvbWlzZXMgPSBuZXcgTWFwKCk7XG4gICAgICAgIH1cblxuICAgICAgICBfY3JlYXRlQ2xhc3MoRXZlbnRFbWl0dGVyLCBbe1xuICAgICAgICAgICAga2V5OiAnb24nLFxuICAgICAgICAgICAgdmFsdWU6IGZ1bmN0aW9uIG9uKGV2ZW50KSB7XG4gICAgICAgICAgICAgICAgdmFyIFNMUCA9IHRoaXMuX3Byb21pc2VzLmdldChldmVudCksXG4gICAgICAgICAgICAgICAgICAgIGhhc0NvbnRleHQgPSBldmVudC5pbmNsdWRlcyhOU19TRVBBUkFUT1IpLFxuICAgICAgICAgICAgICAgICAgICBzcGxpdEV2ZW50ID0gZXZlbnQuc3BsaXQoTlNfU0VQQVJBVE9SKSxcbiAgICAgICAgICAgICAgICAgICAgcHJvbWlzZU1haW5FdmVudCA9IHRoaXMuX3Byb21pc2VzLmdldChzcGxpdEV2ZW50WzBdKTtcblxuICAgICAgICAgICAgICAgIGlmIChoYXNDb250ZXh0KSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChTTFApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICghcHJvbWlzZU1haW5FdmVudCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb21pc2VNYWluRXZlbnQgPSBuZXcgU3VwZXJMYXp5UHJvbWlzZShTTFAuZm4pO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb21pc2VNYWluRXZlbnQudXBkYXRlUHJvbWlzZShTTFAucHJvbWlzZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5fcHJvbWlzZXMuc2V0KHNwbGl0RXZlbnRbMF0sIHByb21pc2VNYWluRXZlbnQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHByb21pc2VNYWluRXZlbnQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTTFAgPSBuZXcgU3VwZXJMYXp5UHJvbWlzZShwcm9taXNlTWFpbkV2ZW50LmZuKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTTFAudXBkYXRlUHJvbWlzZShwcm9taXNlTWFpbkV2ZW50LnByb21pc2UpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuX3Byb21pc2VzLnNldChldmVudCwgU0xQKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5fcHJvbWlzZXMuc2V0KHNwbGl0RXZlbnRbMF0sIG5ldyBTdXBlckxhenlQcm9taXNlKEVNUFRZX0ZOKSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5fcHJvbWlzZXMuc2V0KGV2ZW50LCBuZXcgU3VwZXJMYXp5UHJvbWlzZShFTVBUWV9GTikpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCFTTFApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuX3Byb21pc2VzLnNldChldmVudCwgbmV3IFN1cGVyTGF6eVByb21pc2UoRU1QVFlfRk4pKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5fcHJvbWlzZXMuZ2V0KGV2ZW50KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSwge1xuICAgICAgICAgICAga2V5OiAnb2ZmJyxcbiAgICAgICAgICAgIHZhbHVlOiBmdW5jdGlvbiBvZmYoZXZlbnQpIHtcbiAgICAgICAgICAgICAgICBpZiAoIWV2ZW50LmluY2x1ZGVzKE5TX1NFUEFSQVRPUikpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fZGVzdHJveVByb21pc2UoZXZlbnQpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBfaXRlcmF0b3JOb3JtYWxDb21wbGV0aW9uID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIF9kaWRJdGVyYXRvckVycm9yID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgICAgIHZhciBfaXRlcmF0b3JFcnJvciA9IHVuZGVmaW5lZDtcblxuICAgICAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICAgICAgZm9yICh2YXIgX2l0ZXJhdG9yID0gdGhpcy5fcHJvbWlzZXNbU3ltYm9sLml0ZXJhdG9yXSgpLCBfc3RlcDsgIShfaXRlcmF0b3JOb3JtYWxDb21wbGV0aW9uID0gKF9zdGVwID0gX2l0ZXJhdG9yLm5leHQoKSkuZG9uZSk7IF9pdGVyYXRvck5vcm1hbENvbXBsZXRpb24gPSB0cnVlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGtleVZhbCA9IF9zdGVwLnZhbHVlO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGtleVZhbFswXS5zcGxpdChOU19TRVBBUkFUT1IpWzBdID09PSBldmVudCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9kZXN0cm95UHJvbWlzZShrZXlWYWxbMF0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBfZGlkSXRlcmF0b3JFcnJvciA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgICAgICBfaXRlcmF0b3JFcnJvciA9IGVycjtcbiAgICAgICAgICAgICAgICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFfaXRlcmF0b3JOb3JtYWxDb21wbGV0aW9uICYmIF9pdGVyYXRvci5yZXR1cm4pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgX2l0ZXJhdG9yLnJldHVybigpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZmluYWxseSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKF9kaWRJdGVyYXRvckVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRocm93IF9pdGVyYXRvckVycm9yO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSwge1xuICAgICAgICAgICAga2V5OiAnX2Rlc3Ryb3lQcm9taXNlJyxcbiAgICAgICAgICAgIHZhbHVlOiBmdW5jdGlvbiBfZGVzdHJveVByb21pc2UoZXZlbnQpIHtcbiAgICAgICAgICAgICAgICB0aGlzLl9wcm9taXNlc1tldmVudF0ua2lsbCgpO1xuICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLl9wcm9taXNlc1tldmVudF07XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sIHtcbiAgICAgICAgICAgIGtleTogJ2VtaXQnLFxuICAgICAgICAgICAgdmFsdWU6IGZ1bmN0aW9uIGVtaXQoZXZlbnQpIHtcbiAgICAgICAgICAgICAgICBmb3IgKHZhciBfbGVuID0gYXJndW1lbnRzLmxlbmd0aCwgZGF0YSA9IEFycmF5KF9sZW4gPiAxID8gX2xlbiAtIDEgOiAwKSwgX2tleSA9IDE7IF9rZXkgPCBfbGVuOyBfa2V5KyspIHtcbiAgICAgICAgICAgICAgICAgICAgZGF0YVtfa2V5IC0gMV0gPSBhcmd1bWVudHNbX2tleV07XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdmFyIGV4ZWN1dG9yID0gZnVuY3Rpb24gZXhlY3V0b3Iob2ssIGtvKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBvay5hcHBseSh1bmRlZmluZWQsIGRhdGEpO1xuICAgICAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgICAgICB2YXIgU0xQID0gdGhpcy5fcHJvbWlzZXMuZ2V0KGV2ZW50KSxcbiAgICAgICAgICAgICAgICAgICAgaGFzQ29udGV4dCA9IGV2ZW50LmluY2x1ZGVzKE5TX1NFUEFSQVRPUiksXG4gICAgICAgICAgICAgICAgICAgIHNwbGl0RXZlbnQgPSBldmVudC5zcGxpdChOU19TRVBBUkFUT1IpLFxuICAgICAgICAgICAgICAgICAgICBwcm9taXNlTWFpbkV2ZW50ID0gdGhpcy5fcHJvbWlzZXMuZ2V0KHNwbGl0RXZlbnRbMF0pO1xuXG4gICAgICAgICAgICAgICAgaWYgKGhhc0NvbnRleHQpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKFNMUCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgU0xQLmF3YWtlKGV4ZWN1dG9yKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChwcm9taXNlTWFpbkV2ZW50KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvbWlzZU1haW5FdmVudC5hd2FrZShleGVjdXRvcik7XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb21pc2VNYWluRXZlbnQgPSBuZXcgU3VwZXJMYXp5UHJvbWlzZShFTVBUWV9GTik7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvbWlzZU1haW5FdmVudC51cGRhdGVQcm9taXNlKFNMUC5wcm9taXNlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9wcm9taXNlcy5zZXQoc3BsaXRFdmVudFswXSwgcHJvbWlzZU1haW5FdmVudCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAocHJvbWlzZU1haW5FdmVudCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb21pc2VNYWluRXZlbnQuYXdha2UoZXhlY3V0b3IpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNMUCA9IG5ldyBTdXBlckxhenlQcm9taXNlKEVNUFRZX0ZOKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTTFAudXBkYXRlUHJvbWlzZShwcm9taXNlTWFpbkV2ZW50LnByb21pc2UpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9taXNlTWFpbkV2ZW50ID0gbmV3IFN1cGVyTGF6eVByb21pc2UoZXhlY3V0b3IpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb21pc2VNYWluRXZlbnQuYXdha2UoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTTFAgPSBuZXcgU3VwZXJMYXp5UHJvbWlzZShFTVBUWV9GTik7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgU0xQLnVwZGF0ZVByb21pc2UocHJvbWlzZU1haW5FdmVudC5wcm9taXNlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChTTFApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFNMUC5hd2FrZShleGVjdXRvcik7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBTTFAgPSBuZXcgU3VwZXJMYXp5UHJvbWlzZShleGVjdXRvcik7XG4gICAgICAgICAgICAgICAgICAgICAgICBTTFAuYXdha2UoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuX3Byb21pc2VzLnNldChldmVudCwgU0xQKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIHZhciBfaXRlcmF0b3JOb3JtYWxDb21wbGV0aW9uMiA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHZhciBfZGlkSXRlcmF0b3JFcnJvcjIgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIF9pdGVyYXRvckVycm9yMiA9IHVuZGVmaW5lZDtcblxuICAgICAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICAgICAgZm9yICh2YXIgX2l0ZXJhdG9yMiA9IHRoaXMuX3Byb21pc2VzW1N5bWJvbC5pdGVyYXRvcl0oKSwgX3N0ZXAyOyAhKF9pdGVyYXRvck5vcm1hbENvbXBsZXRpb24yID0gKF9zdGVwMiA9IF9pdGVyYXRvcjIubmV4dCgpKS5kb25lKTsgX2l0ZXJhdG9yTm9ybWFsQ29tcGxldGlvbjIgPSB0cnVlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGtleVZhbCA9IF9zdGVwMi52YWx1ZTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChrZXlWYWxbMF0uaW5jbHVkZXMoTlNfU0VQQVJBVE9SKSAmJiBrZXlWYWxbMF0uc3BsaXQoTlNfU0VQQVJBVE9SKVswXSA9PT0gZXZlbnQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHN1YkV2ZW50ID0ga2V5VmFsWzBdLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ViUHJvbWlzZSA9IHRoaXMuX3Byb21pc2VzLmdldChzdWJFdmVudCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YlByb21pc2UudXBkYXRlUHJvbWlzZShTTFAucHJvbWlzZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YlByb21pc2UuYXdha2UoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgICAgICAgICAgICAgX2RpZEl0ZXJhdG9yRXJyb3IyID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIF9pdGVyYXRvckVycm9yMiA9IGVycjtcbiAgICAgICAgICAgICAgICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFfaXRlcmF0b3JOb3JtYWxDb21wbGV0aW9uMiAmJiBfaXRlcmF0b3IyLnJldHVybikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBfaXRlcmF0b3IyLnJldHVybigpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZmluYWxseSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKF9kaWRJdGVyYXRvckVycm9yMikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aHJvdyBfaXRlcmF0b3JFcnJvcjI7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XSk7XG5cbiAgICAgICAgcmV0dXJuIEV2ZW50RW1pdHRlcjtcbiAgICB9KCk7XG5cbiAgICBleHBvcnRzLkV2ZW50RW1pdHRlciA9IEV2ZW50RW1pdHRlcjtcbn0pKF9tb2R1bGUsIF9leHBvcnRzKTsiXSwic291cmNlUm9vdCI6Ii9zb3VyY2UvIn0= diff --git a/bin/EventEmitter.node.js b/bin/EventEmitter.node.js new file mode 100644 index 0000000..3cba73c --- /dev/null +++ b/bin/EventEmitter.node.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('../dist_node/EventEmitter').EventEmitter; diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..8544c04 --- /dev/null +++ b/bower.json @@ -0,0 +1,26 @@ +{ + "name": "eventemitter-promise", + "description": "Event Emitter with lazy promise.", + "main": "bin/EventEmitter.browser.js", + "authors": [ + "Lilian Saget-Lethias (http://github.com/bios21)" + ], + "license": "ISC", + "keywords": [ + "event", + "eventemitter", + "lazy", + "promise", + "a+", + "then", + "thenable" + ], + "homepage": "https://github.com/bios21/eventemitter-promise#readme", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/dist_node/EventEmitter.js b/dist_node/EventEmitter.js new file mode 100644 index 0000000..75a6d6e --- /dev/null +++ b/dist_node/EventEmitter.js @@ -0,0 +1,131 @@ +'use strict'; + +var _module = null, + _exports = null; +if (module && module.exports) { + _exports = module.exports; + _module = { + SuperLazyPromise: require('superlazypromise').SuperLazyPromise + }; +} else { + _exports = window; + _module = _exports; +} + +((module, exports) => { + const SuperLazyPromise = module.SuperLazyPromise; + + const EMPTY_FN = function EMPTY_FN() {}, + NS_SEPARATOR = '.'; + + class EventEmitter { + constructor() { + this._promises = new Map(); + } + + on(event) { + let SLP = this._promises.get(event), + hasContext = event.includes(NS_SEPARATOR), + splitEvent = event.split(NS_SEPARATOR), + promiseMainEvent = this._promises.get(splitEvent[0]); + + if (hasContext) { + if (SLP) { + if (!promiseMainEvent) { + promiseMainEvent = new SuperLazyPromise(SLP.fn); + promiseMainEvent.updatePromise(SLP.promise); + this._promises.set(splitEvent[0], promiseMainEvent); + } + } else { + if (promiseMainEvent) { + SLP = new SuperLazyPromise(promiseMainEvent.fn); + SLP.updatePromise(promiseMainEvent.promise); + this._promises.set(event, SLP); + } else { + this._promises.set(splitEvent[0], new SuperLazyPromise(EMPTY_FN)); + this._promises.set(event, new SuperLazyPromise(EMPTY_FN)); + } + } + } else { + if (!SLP) { + this._promises.set(event, new SuperLazyPromise(EMPTY_FN)); + } + } + return this._promises.get(event); + } + + off(event) { + if (!event.includes(NS_SEPARATOR)) { + this._destroyPromise(event); + } else { + for (let keyVal of this._promises) { + if (keyVal[0].split(NS_SEPARATOR)[0] === event) { + this._destroyPromise(keyVal[0]); + } + } + } + } + + _destroyPromise(event) { + this._promises[event].kill(); + delete this._promises[event]; + } + + emit(event) { + for (var _len = arguments.length, data = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + data[_key - 1] = arguments[_key]; + } + + const executor = (ok, ko) => ok.apply(undefined, data); + + let SLP = this._promises.get(event), + hasContext = event.includes(NS_SEPARATOR), + splitEvent = event.split(NS_SEPARATOR), + promiseMainEvent = this._promises.get(splitEvent[0]); + + if (hasContext) { + if (SLP) { + SLP.awake(executor); + if (promiseMainEvent) { + promiseMainEvent.awake(executor); + } else { + promiseMainEvent = new SuperLazyPromise(EMPTY_FN); + promiseMainEvent.updatePromise(SLP.promise); + this._promises.set(splitEvent[0], promiseMainEvent); + } + } else { + if (promiseMainEvent) { + promiseMainEvent.awake(executor); + SLP = new SuperLazyPromise(EMPTY_FN); + SLP.updatePromise(promiseMainEvent.promise); + } else { + promiseMainEvent = new SuperLazyPromise(executor); + promiseMainEvent.awake(); + SLP = new SuperLazyPromise(EMPTY_FN); + SLP.updatePromise(promiseMainEvent.promise); + } + } + } else { + if (SLP) { + SLP.awake(executor); + } else { + SLP = new SuperLazyPromise(executor); + SLP.awake(); + this._promises.set(event, SLP); + } + + for (let keyVal of this._promises) { + if (keyVal[0].includes(NS_SEPARATOR) && keyVal[0].split(NS_SEPARATOR)[0] === event) { + let subEvent = keyVal[0], + subPromise = this._promises.get(subEvent); + subPromise.updatePromise(SLP.promise); + subPromise.awake(); + } + } + } + } + } + + exports.EventEmitter = EventEmitter; +})(_module, _exports); +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIiwic291cmNlcyI6WyJFdmVudEVtaXR0ZXIuanMiXSwic291cmNlc0NvbnRlbnQiOlsiJ3VzZSBzdHJpY3QnO1xuXG52YXIgX21vZHVsZSA9IG51bGwsXG4gICAgX2V4cG9ydHMgPSBudWxsO1xuaWYgKG1vZHVsZSAmJiBtb2R1bGUuZXhwb3J0cykge1xuICAgIF9leHBvcnRzID0gbW9kdWxlLmV4cG9ydHM7XG4gICAgX21vZHVsZSA9IHtcbiAgICAgICAgU3VwZXJMYXp5UHJvbWlzZTogcmVxdWlyZSgnc3VwZXJsYXp5cHJvbWlzZScpLlN1cGVyTGF6eVByb21pc2VcbiAgICB9O1xufSBlbHNlIHtcbiAgICBfZXhwb3J0cyA9IHdpbmRvdztcbiAgICBfbW9kdWxlID0gX2V4cG9ydHM7XG59XG5cbigobW9kdWxlLCBleHBvcnRzKSA9PiB7XG4gICAgY29uc3QgU3VwZXJMYXp5UHJvbWlzZSA9IG1vZHVsZS5TdXBlckxhenlQcm9taXNlO1xuXG4gICAgY29uc3QgRU1QVFlfRk4gPSBmdW5jdGlvbiBFTVBUWV9GTigpIHt9LFxuICAgICAgICAgIE5TX1NFUEFSQVRPUiA9ICcuJztcblxuICAgIGNsYXNzIEV2ZW50RW1pdHRlciB7XG4gICAgICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICAgICAgdGhpcy5fcHJvbWlzZXMgPSBuZXcgTWFwKCk7XG4gICAgICAgIH1cblxuICAgICAgICBvbihldmVudCkge1xuICAgICAgICAgICAgbGV0IFNMUCA9IHRoaXMuX3Byb21pc2VzLmdldChldmVudCksXG4gICAgICAgICAgICAgICAgaGFzQ29udGV4dCA9IGV2ZW50LmluY2x1ZGVzKE5TX1NFUEFSQVRPUiksXG4gICAgICAgICAgICAgICAgc3BsaXRFdmVudCA9IGV2ZW50LnNwbGl0KE5TX1NFUEFSQVRPUiksXG4gICAgICAgICAgICAgICAgcHJvbWlzZU1haW5FdmVudCA9IHRoaXMuX3Byb21pc2VzLmdldChzcGxpdEV2ZW50WzBdKTtcblxuICAgICAgICAgICAgaWYgKGhhc0NvbnRleHQpIHtcbiAgICAgICAgICAgICAgICBpZiAoU0xQKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmICghcHJvbWlzZU1haW5FdmVudCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcHJvbWlzZU1haW5FdmVudCA9IG5ldyBTdXBlckxhenlQcm9taXNlKFNMUC5mbik7XG4gICAgICAgICAgICAgICAgICAgICAgICBwcm9taXNlTWFpbkV2ZW50LnVwZGF0ZVByb21pc2UoU0xQLnByb21pc2UpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5fcHJvbWlzZXMuc2V0KHNwbGl0RXZlbnRbMF0sIHByb21pc2VNYWluRXZlbnQpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHByb21pc2VNYWluRXZlbnQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFNMUCA9IG5ldyBTdXBlckxhenlQcm9taXNlKHByb21pc2VNYWluRXZlbnQuZm4pO1xuICAgICAgICAgICAgICAgICAgICAgICAgU0xQLnVwZGF0ZVByb21pc2UocHJvbWlzZU1haW5FdmVudC5wcm9taXNlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuX3Byb21pc2VzLnNldChldmVudCwgU0xQKTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuX3Byb21pc2VzLnNldChzcGxpdEV2ZW50WzBdLCBuZXcgU3VwZXJMYXp5UHJvbWlzZShFTVBUWV9GTikpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5fcHJvbWlzZXMuc2V0KGV2ZW50LCBuZXcgU3VwZXJMYXp5UHJvbWlzZShFTVBUWV9GTikpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBpZiAoIVNMUCkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLl9wcm9taXNlcy5zZXQoZXZlbnQsIG5ldyBTdXBlckxhenlQcm9taXNlKEVNUFRZX0ZOKSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX3Byb21pc2VzLmdldChldmVudCk7XG4gICAgICAgIH1cblxuICAgICAgICBvZmYoZXZlbnQpIHtcbiAgICAgICAgICAgIGlmICghZXZlbnQuaW5jbHVkZXMoTlNfU0VQQVJBVE9SKSkge1xuICAgICAgICAgICAgICAgIHRoaXMuX2Rlc3Ryb3lQcm9taXNlKGV2ZW50KTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgZm9yIChsZXQga2V5VmFsIG9mIHRoaXMuX3Byb21pc2VzKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChrZXlWYWxbMF0uc3BsaXQoTlNfU0VQQVJBVE9SKVswXSA9PT0gZXZlbnQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuX2Rlc3Ryb3lQcm9taXNlKGtleVZhbFswXSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBfZGVzdHJveVByb21pc2UoZXZlbnQpIHtcbiAgICAgICAgICAgIHRoaXMuX3Byb21pc2VzW2V2ZW50XS5raWxsKCk7XG4gICAgICAgICAgICBkZWxldGUgdGhpcy5fcHJvbWlzZXNbZXZlbnRdO1xuICAgICAgICB9XG5cbiAgICAgICAgZW1pdChldmVudCkge1xuICAgICAgICAgICAgZm9yICh2YXIgX2xlbiA9IGFyZ3VtZW50cy5sZW5ndGgsIGRhdGEgPSBBcnJheShfbGVuID4gMSA/IF9sZW4gLSAxIDogMCksIF9rZXkgPSAxOyBfa2V5IDwgX2xlbjsgX2tleSsrKSB7XG4gICAgICAgICAgICAgICAgZGF0YVtfa2V5IC0gMV0gPSBhcmd1bWVudHNbX2tleV07XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IGV4ZWN1dG9yID0gKG9rLCBrbykgPT4gb2suYXBwbHkodW5kZWZpbmVkLCBkYXRhKTtcblxuICAgICAgICAgICAgbGV0IFNMUCA9IHRoaXMuX3Byb21pc2VzLmdldChldmVudCksXG4gICAgICAgICAgICAgICAgaGFzQ29udGV4dCA9IGV2ZW50LmluY2x1ZGVzKE5TX1NFUEFSQVRPUiksXG4gICAgICAgICAgICAgICAgc3BsaXRFdmVudCA9IGV2ZW50LnNwbGl0KE5TX1NFUEFSQVRPUiksXG4gICAgICAgICAgICAgICAgcHJvbWlzZU1haW5FdmVudCA9IHRoaXMuX3Byb21pc2VzLmdldChzcGxpdEV2ZW50WzBdKTtcblxuICAgICAgICAgICAgaWYgKGhhc0NvbnRleHQpIHtcbiAgICAgICAgICAgICAgICBpZiAoU0xQKSB7XG4gICAgICAgICAgICAgICAgICAgIFNMUC5hd2FrZShleGVjdXRvcik7XG4gICAgICAgICAgICAgICAgICAgIGlmIChwcm9taXNlTWFpbkV2ZW50KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBwcm9taXNlTWFpbkV2ZW50LmF3YWtlKGV4ZWN1dG9yKTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHByb21pc2VNYWluRXZlbnQgPSBuZXcgU3VwZXJMYXp5UHJvbWlzZShFTVBUWV9GTik7XG4gICAgICAgICAgICAgICAgICAgICAgICBwcm9taXNlTWFpbkV2ZW50LnVwZGF0ZVByb21pc2UoU0xQLnByb21pc2UpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5fcHJvbWlzZXMuc2V0KHNwbGl0RXZlbnRbMF0sIHByb21pc2VNYWluRXZlbnQpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHByb21pc2VNYWluRXZlbnQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHByb21pc2VNYWluRXZlbnQuYXdha2UoZXhlY3V0b3IpO1xuICAgICAgICAgICAgICAgICAgICAgICAgU0xQID0gbmV3IFN1cGVyTGF6eVByb21pc2UoRU1QVFlfRk4pO1xuICAgICAgICAgICAgICAgICAgICAgICAgU0xQLnVwZGF0ZVByb21pc2UocHJvbWlzZU1haW5FdmVudC5wcm9taXNlKTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHByb21pc2VNYWluRXZlbnQgPSBuZXcgU3VwZXJMYXp5UHJvbWlzZShleGVjdXRvcik7XG4gICAgICAgICAgICAgICAgICAgICAgICBwcm9taXNlTWFpbkV2ZW50LmF3YWtlKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBTTFAgPSBuZXcgU3VwZXJMYXp5UHJvbWlzZShFTVBUWV9GTik7XG4gICAgICAgICAgICAgICAgICAgICAgICBTTFAudXBkYXRlUHJvbWlzZShwcm9taXNlTWFpbkV2ZW50LnByb21pc2UpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBpZiAoU0xQKSB7XG4gICAgICAgICAgICAgICAgICAgIFNMUC5hd2FrZShleGVjdXRvcik7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgU0xQID0gbmV3IFN1cGVyTGF6eVByb21pc2UoZXhlY3V0b3IpO1xuICAgICAgICAgICAgICAgICAgICBTTFAuYXdha2UoKTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fcHJvbWlzZXMuc2V0KGV2ZW50LCBTTFApO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGZvciAobGV0IGtleVZhbCBvZiB0aGlzLl9wcm9taXNlcykge1xuICAgICAgICAgICAgICAgICAgICBpZiAoa2V5VmFsWzBdLmluY2x1ZGVzKE5TX1NFUEFSQVRPUikgJiYga2V5VmFsWzBdLnNwbGl0KE5TX1NFUEFSQVRPUilbMF0gPT09IGV2ZW50KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBsZXQgc3ViRXZlbnQgPSBrZXlWYWxbMF0sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ViUHJvbWlzZSA9IHRoaXMuX3Byb21pc2VzLmdldChzdWJFdmVudCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBzdWJQcm9taXNlLnVwZGF0ZVByb21pc2UoU0xQLnByb21pc2UpO1xuICAgICAgICAgICAgICAgICAgICAgICAgc3ViUHJvbWlzZS5hd2FrZSgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgZXhwb3J0cy5FdmVudEVtaXR0ZXIgPSBFdmVudEVtaXR0ZXI7XG59KShfbW9kdWxlLCBfZXhwb3J0cyk7Il0sImZpbGUiOiJFdmVudEVtaXR0ZXIuanMiLCJzb3VyY2VSb290IjoiL3NvdXJjZS8ifQ== diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..d4cce30 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,42 @@ +const gulp = require('gulp'), + clean = require('gulp-clean'), + babel = require('gulp-babel'), + sourcemaps = require('gulp-sourcemaps'), + gutil = require('gulp-util'), + concat = require('gulp-concat'); + +gulp.task('build', () => { + gutil.log('Build for Node ...'); + gulp.src('src/**/*.js') + .pipe(babel({ + presets: ['es2015-node4'], + comments: false + }).on('error', err => gutil.log('Some shit appends for node dist ... ', err.message))) + .pipe(sourcemaps.init()) + .pipe(sourcemaps.write()) + .pipe(gulp.dest('dist_node')); + + gutil.log('Build for browser ...'); + gulp.src('src/**/*.js') + .pipe(babel({ + presets: ['es2015'], + comments: false + }).on('error', err => gutil.log('Some shit appends for browser dist ...', err.message))) + .pipe(sourcemaps.init()) + .pipe(concat('../bin/EventEmitter.browser.js')) + .pipe(sourcemaps.write()) + .pipe(gulp.dest('dist_browser')); +}); + +gulp.task('clean', () => { + gulp.src(['dist_node/**/*', 'dist_browser/**/*', 'temp'], {read: false}) + .pipe(clean()) +}); + +gulp.task('watch', ['cleanBuild'], () => { + gulp.watch(['bower.json', 'src/index.html'], ['bower']); + gulp.watch('src/**/*.js', ['build']); +}); + +gulp.task('default', ['watch']); +gulp.task('cleanBuild', ['clean', 'build']); diff --git a/package.json b/package.json new file mode 100644 index 0000000..ade53dd --- /dev/null +++ b/package.json @@ -0,0 +1,45 @@ +{ + "name": "eventemitter-promise", + "version": "0.0.1", + "description": "Event Emitter with lazy promise.", + "main": "bin/EventEmitter.node.js", + "scripts": { + "test": "mocha test/*.js" + }, + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/bios21/eventemitter-promise.git" + }, + "keywords": [ + "event", + "eventemitter", + "lazy", + "promise", + "a+", + "then", + "thenable" + ], + "devDependencies": { + "babel": "^6.5.2", + "babel-cli": "^6.10.1", + "babel-preset-es2015": "^6.9.0", + "babel-preset-es2015-node4": "^2.1.0", + "gulp": "^3.9.1", + "gulp-babel": "^6.1.2", + "gulp-clean": "^0.3.2", + "gulp-concat": "^2.6.0", + "gulp-sourcemaps": "^1.6.0", + "gulp-util": "^3.0.7", + "mocha": "^2.5.3", + "should": "^9.0.2" + }, + "author": "Lilian Saget-Lethias (http://github.com/bios21)", + "license": "ISC", + "bugs": { + "url": "https://github.com/bios21/eventemitter-promise/issues" + }, + "homepage": "https://github.com/bios21/eventemitter-promise#readme", + "dependencies": { + "superlazypromise": "0.0.1" + } +} diff --git a/src/EventEmitter.js b/src/EventEmitter.js new file mode 100644 index 0000000..3719e6a --- /dev/null +++ b/src/EventEmitter.js @@ -0,0 +1,188 @@ +'use strict'; + +var _module = null, + _exports = null; +if (module && module.exports) { + _exports = module.exports; + _module = { + SuperLazyPromise: require('superlazypromise').SuperLazyPromise + }; +} else { + _exports = window; + _module = _exports; +} + +((module, exports) => { + const SuperLazyPromise = module.SuperLazyPromise; + + const EMPTY_FN = function() {}, + NS_SEPARATOR = '.'; + + /** + * A manager for listening and emitting event on a object. + * + * + * Each listener are stored to be fired later on a `SuperLazyPromise` + * + * | emit\on | event | event.* | event.context | event.context! | + * |:--------------:|:-----:|:-------:|:-------------:|:--------------:| + * | event | ✓ | ✓ | ✓ | ✓ | + * | event.context | ✓ | | ✓ | ✓ | + * | event.context! | | | ✓ | ✓ | + * + * **!** : Firing an event on strict context (`event.context!`) is not yet implemented. + * + * @abstract + */ + class EventEmitter { + + /** + * Build the internal super lazy promise pool. + */ + constructor() { + + /** + * @type {Map} + * @private + */ + this._promises = new Map(); + } + + /** + * Listen the given event. + * Contrary to classic event manager like `addEventListener` for DOM, `EventEmitter` provide a `Promise`-like + * to manipulate instead of a callback. + * + * This `Promise` will be **SuperLazy** which means that it will be active only when we fire it. + * + * See `SuperLazyPromise` for more explanation. + * + * @param {String} event Type of event with context or not + * @return {LazyPromise} The promise to manipulate + * + * @see SuperLazyPromise + * @listening event + */ + on(event) { + let SLP = this._promises.get(event), + hasContext = event.includes(NS_SEPARATOR), + splitEvent = event.split(NS_SEPARATOR), + promiseMainEvent = this._promises.get(splitEvent[0]); + + if (hasContext) { + if (SLP) { + if (!promiseMainEvent) { + promiseMainEvent = new SuperLazyPromise(SLP.fn); + promiseMainEvent.updatePromise(SLP.promise); + this._promises.set(splitEvent[0], promiseMainEvent); + } + } else { + if (promiseMainEvent) { + SLP = new SuperLazyPromise(promiseMainEvent.fn); + SLP.updatePromise(promiseMainEvent.promise); + this._promises.set(event, SLP); + } else { + this._promises.set(splitEvent[0], new SuperLazyPromise(EMPTY_FN)); + this._promises.set(event, new SuperLazyPromise(EMPTY_FN)); + } + } + } else { + if (!SLP) { + this._promises.set(event, new SuperLazyPromise(EMPTY_FN)); + } + } + return this._promises.get(event); + } + + /** + * Stop listening on the given event. + * The promise stored by `on` or `emit` will become not usable. + * + * @param {String} event Type of event with context or not + * @listened event + */ + off(event) { + if (!event.includes(NS_SEPARATOR)) { + this._destroyPromise(event); + } else { + for (let keyVal of this._promises) { + if (keyVal[0].split(NS_SEPARATOR)[0] === event) { + this._destroyPromise(keyVal[0]); + } + } + } + } + + /** + * Kill & destroy a lazy promise to prevent future use. + * + * @param {String} event Type of event with context or not + * @private + */ + _destroyPromise(event) { + this._promises[event].kill(); + delete this._promises[event]; + } + + /** + * Emit a possibly listened event and call the function associated with some param. + * See the array in class description for event specific firing. + * + * @param {String} event Type of event with context or not + * @param {...*} data What is fired to the first listener + * + * @emit event + */ + emit(event, ...data) { + const executor = (ok, ko) => ok(...data); + + let SLP = this._promises.get(event), + hasContext = event.includes(NS_SEPARATOR), + splitEvent = event.split(NS_SEPARATOR), + promiseMainEvent = this._promises.get(splitEvent[0]); + + if (hasContext) { + if (SLP) { + SLP.awake(executor); + if (promiseMainEvent) { + promiseMainEvent.awake(executor); + } else { + promiseMainEvent = new SuperLazyPromise(EMPTY_FN); + promiseMainEvent.updatePromise(SLP.promise); + this._promises.set(splitEvent[0], promiseMainEvent); + } + } else { + if (promiseMainEvent) { + promiseMainEvent.awake(executor); + SLP = new SuperLazyPromise(EMPTY_FN); + SLP.updatePromise(promiseMainEvent.promise); + } else { + promiseMainEvent = new SuperLazyPromise(executor); + promiseMainEvent.awake(); + SLP = new SuperLazyPromise(EMPTY_FN); + SLP.updatePromise(promiseMainEvent.promise); + } + } + } else { + if (SLP) { + SLP.awake(executor); + } else { + SLP = new SuperLazyPromise(executor); + SLP.awake(); + this._promises.set(event, SLP); + } + + for (let keyVal of this._promises) { + if (keyVal[0].includes(NS_SEPARATOR) && keyVal[0].split(NS_SEPARATOR)[0] === event) { + let subEvent = keyVal[0], + subPromise = this._promises.get(subEvent); + subPromise.updatePromise(SLP.promise); + subPromise.awake(); + } + } + } + } + } + + exports.EventEmitter = EventEmitter; +})(_module, _exports); diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..8e49954 --- /dev/null +++ b/test/test.js @@ -0,0 +1,130 @@ +'use strict'; + +const EventEmitter = require('../dist_node/EventEmitter').EventEmitter, + mocha = require('mocha'), + should = require('should'); + +console.log(EventEmitter); + +describe('EventEmitter', () => { + describe('on/emit event => event', () => { + it('should emit and listen on event', done => { + class Test extends EventEmitter { + + } + + let test = new Test(); + test.on('event').then(anything => { + anything.should.be.eql('test'); + done(); + }); + + test.emit('event', 'test'); + }); + + it('should emit on event and listen on event.test', done => { + class Test extends EventEmitter { + + } + + let test = new Test(); + test.on('event.test').then(anything => { + anything.should.be.eql('test'); + done(); + }); + + test.emit('event', 'test'); + }); + + it('should emit on event.test and listen on event', done => { + class Test extends EventEmitter { + + } + + let test = new Test(); + test.on('event').then(anything => { + anything.should.be.eql('test'); + done(); + }); + + test.emit('event.test', 'test'); + }); + + + it('should emit on "tneve" and listen on event without receiving', done => { + class Test extends EventEmitter { + + } + + let test = new Test(); + test.on('event').then(anything => { + anything.should.not.be.eql('test'); + done(new Error('Should not be here.')); + }); + + test.emit('tneve', 'test'); + done(); + }); + }); + + describe('on/emit event.* => event.*', () => { + it('should emit and listen on event', done => { + class Test extends EventEmitter { + + } + + let test = new Test(); + test.on('event.test').then(anything => { + anything.should.be.eql('test'); + done(); + }); + + test.emit('event.test', 'test'); + }); + + it('should emit on event and listen on event.test', done => { + class Test extends EventEmitter { + + } + + let test = new Test(); + test.on('event.test').then(anything => { + anything.should.be.eql('test'); + done(); + }); + + test.emit('event', 'test'); + }); + + it('should emit on event.test and listen on event', done => { + class Test extends EventEmitter { + + } + + let test = new Test(); + test.on('event').then(anything => { + anything.should.be.eql('test'); + done(); + }); + + test.emit('event.test', 'test'); + }); + + + it('should emit on "tneve" and listen on event without receiving', done => { + class Test extends EventEmitter { + + } + + let test = new Test(); + test.on('event').then(anything => { + anything.should.not.be.eql('test'); + done(new Error('Should not be here.')); + }); + + test.emit('tneve', 'test'); + done(); + }); + }); + +});