├── .gitattributes ├── .stylelintrc.json ├── index.js ├── .gitignore ├── .npmignore ├── .editorconfig ├── inch.json ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── workflows │ └── ci.yml └── CONTRIBUTING.md ├── .eslintrc.js ├── test └── spec │ ├── index-spec.js │ └── event-emitter-enhancer-spec.js ├── docs ├── CHANGELOG.md └── api.md ├── .jsbeautifyrc ├── package.json ├── lib ├── event-emitter-enhancer.js ├── event-emitter-enhancer.d.ts └── enhanced-event-emitter.js ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard" 3 | } 4 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./lib/event-emitter-enhancer'); 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .temp 3 | node_modules 4 | bower_components 5 | npm-debug.log 6 | package-lock.json 7 | .nyc_output 8 | coverage 9 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | target 2 | .temp 3 | .nyc_output 4 | .config 5 | coverage 6 | bower_components 7 | .github 8 | test 9 | example 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | indent_style = space 10 | indent_size = 4 11 | 12 | [*.json] 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /inch.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": { 3 | "included": [ 4 | "*.js", 5 | "lib/**/*.js", 6 | "tasks/**/*.js" 7 | ], 8 | "excluded": [ 9 | "**/Gruntfile.js", 10 | "**/.eslintrc.js", 11 | "**/stylelint.config.js" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: sagiegurari 7 | 8 | --- 9 | 10 | ### Feature Description 11 | 12 | 13 | ### Describe The Solution You'd Like 14 | 15 | 16 | ### Code Sample 17 | 18 | ```js 19 | // paste code here 20 | ``` 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: sagiegurari 7 | 8 | --- 9 | 10 | ### Describe The Bug 11 | 12 | 13 | ### To Reproduce 14 | 15 | 16 | ### Error Stack 17 | 18 | ```console 19 | The error stack trace 20 | ``` 21 | 22 | ### Code Sample 23 | 24 | ```js 25 | // paste code here 26 | ``` 27 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'env': { 3 | 'node': true, 4 | 'commonjs': true, 5 | 'es2021': true, 6 | 'mocha': true 7 | }, 8 | 'extends': 'eslint:recommended', 9 | 'parserOptions': { 10 | 'ecmaVersion': 13 11 | }, 12 | 'rules': { 13 | 'indent': [ 14 | 'error', 15 | 4 16 | ], 17 | 'linebreak-style': [ 18 | 'error', 19 | 'unix' 20 | ], 21 | 'quotes': [ 22 | 'error', 23 | 'single' 24 | ], 25 | 'semi': [ 26 | 'error', 27 | 'always' 28 | ] 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /test/spec/index-spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const chai = require('chai'); 4 | const assert = chai.assert; 5 | const EventEmitterEnhancer = require('../../'); 6 | 7 | describe('Index Tests', function () { 8 | it('EventEmitter setup', function () { 9 | assert.isFunction(EventEmitterEnhancer.extend); 10 | assert.isFunction(EventEmitterEnhancer.modify); 11 | 12 | const EventEmitter = require('events').EventEmitter; 13 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 14 | const emitter = new EnhancedEventEmitter(); 15 | 16 | assert.isFunction(emitter.else); 17 | assert.isFunction(EventEmitterEnhancer.modify); 18 | assert.isFunction(EventEmitterEnhancer.extend); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | env: 4 | CLICOLOR_FORCE: 1 5 | jobs: 6 | ci: 7 | name: CI 8 | runs-on: ubuntu-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | node-version: ['18.x'] 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | - name: Install node.js 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - name: Install Dependencies 21 | run: npm install 22 | - name: Run CI 23 | run: npm test 24 | - name: Coveralls 25 | uses: coverallsapp/github-action@master 26 | with: 27 | github-token: ${{ secrets.GITHUB_TOKEN }} 28 | path-to-lcov: './coverage/lcov.info' 29 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | 4 | ## Issues 5 | 6 | Found a bug? Got a question? Want some enhancement?
7 | First place to go is the repository issues section, and I'll try to help as much as possible. 8 | 9 | ## Pull Requests 10 | 11 | Fixed a bug or just want to provided additional functionality?
12 | Simply fork this repository, implement your changes and create a pull request.
13 | Few guidelines regarding pull requests: 14 | 15 | * This repository is integrated with github actions for continuous integration.
16 | 17 | Your pull request build must pass (the build will run automatically).
18 | You can run the following command locally to ensure the build will pass: 19 | 20 | ````sh 21 | npm test 22 | ```` 23 | 24 | * This library is using multiple code inspection tools to validate certain level of standards.
The configuration is part of the repository and you can set your favorite IDE using that configuration.
You can run the following command locally to ensure the code inspection passes: 25 | 26 | ````sh 27 | npm run lint 28 | ```` 29 | 30 | * There are many automatic unit tests as part of the library which provide full coverage of the functionality.
Any fix/enhancement must come with a set of tests to ensure it's working well. 31 | -------------------------------------------------------------------------------- /docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | | Date | Version | Description | 2 | | ----------- | ------- | ----------- | 3 | | 2020-05-11 | v2.0.0 | Migrate to github actions and upgrade minimal node version | 4 | | 2019-12-06 | v1.1.0 | Support event parent parsing and emit #7 | 5 | | 2018-06-13 | v1.0.57 | Added typescript definition (#5 and #6) | 6 | | 2017-11-03 | v1.0.51 | Added 'addNoop' | 7 | | 2017-10-30 | v1.0.49 | Added 'ignoreError' | 8 | | 2017-10-30 | v1.0.48 | New extended 'removeAllListeners' function | 9 | | 2017-01-16 | v1.0.27 | New extended 'once' function | 10 | | 2017-01-07 | v1.0.25 | New 'proxyEvents' function | 11 | | 2017-01-06 | v1.0.24 | New extended 'on' function | 12 | | 2016-11-11 | v1.0.15 | 'emitAsync' callback is now optional | 13 | | 2015-09-23 | v0.0.44 | Added 'onAny' | 14 | | 2015-04-22 | v0.0.31 | Prevent from multiple enhance of same prototype/instance | 15 | | 2014-12-31 | v0.0.10 | EventEmitter is no longer automatically modified,
instead there are 2 ways to extend/modify prototype/modify instance
functions exposed by this library. | 16 | | 2014-12-30 | v0.0.9 | Added ability to enhance compatible EventEmitter types | 17 | | 2014-12-29 | v0.0.6 | Added 'filter' | 18 | | 2014-12-28 | v0.0.5 | Added 'onAsync' | 19 | | 2014-12-28 | v0.0.4 | Added 'emitAsync' | 20 | | 2014-12-28 | v0.0.2 | Initial release. | 21 | -------------------------------------------------------------------------------- /.jsbeautifyrc: -------------------------------------------------------------------------------- 1 | { 2 | "js": { 3 | "indent_size": 4, 4 | "indent_char": " ", 5 | "eol": "\n", 6 | "indent_level": 0, 7 | "indent_with_tabs": false, 8 | "preserve_newlines": true, 9 | "max_preserve_newlines": 2, 10 | "space_in_paren": false, 11 | "jslint_happy": true, 12 | "space_after_anon_function": true, 13 | "brace_style": "collapse", 14 | "break_chained_methods": false, 15 | "keep_array_indentation": true, 16 | "unescape_strings": false, 17 | "wrap_line_length": 0, 18 | "end_with_newline": true, 19 | "comma_first": false, 20 | "eval_code": false, 21 | "keep_function_indentation": false, 22 | "space_before_conditional": true, 23 | "good_stuff": true 24 | }, 25 | "css": { 26 | "indent_size": 2, 27 | "indent_char": " ", 28 | "indent_with_tabs": false, 29 | "eol": "\n", 30 | "end_with_newline": true, 31 | "selector_separator_newline": false, 32 | "newline_between_rules": true 33 | }, 34 | "html": { 35 | "indent_size": 4, 36 | "indent_char": " ", 37 | "indent_with_tabs": false, 38 | "eol": "\n", 39 | "end_with_newline": true, 40 | "preserve_newlines": true, 41 | "max_preserve_newlines": 2, 42 | "indent_inner_html": true, 43 | "brace_style": "collapse", 44 | "indent_scripts": "normal", 45 | "wrap_line_length": 0, 46 | "wrap_attributes": "auto", 47 | "wrap_attributes_indent_size": 4 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "event-emitter-enhancer", 3 | "version": "2.0.0", 4 | "description": "Enhances the Node.js EventEmitter with extra capabilities.", 5 | "author": { 6 | "name": "Sagie Gur-Ari", 7 | "email": "sagiegurari@gmail.com" 8 | }, 9 | "license": "Apache-2.0", 10 | "homepage": "http://github.com/sagiegurari/event-emitter-enhancer", 11 | "repository": { 12 | "type": "git", 13 | "url": "http://github.com/sagiegurari/event-emitter-enhancer.git" 14 | }, 15 | "bugs": { 16 | "url": "http://github.com/sagiegurari/event-emitter-enhancer/issues" 17 | }, 18 | "keywords": [ 19 | "events", 20 | "EventEmitter" 21 | ], 22 | "main": "index.js", 23 | "types": "lib/event-emitter-enhancer.d.ts", 24 | "directories": { 25 | "lib": "lib", 26 | "test": "test/spec" 27 | }, 28 | "scripts": { 29 | "clean": "rm -Rf ./.nyc_output ./coverage", 30 | "format": "js-beautify --config ./.jsbeautifyrc --file ./*.js ./lib/**/*.js ./test/**/*.js", 31 | "lint-js": "eslint ./*.js ./lib/**/*.js ./test/**/*.js", 32 | "lint-css": "stylelint --allow-empty-input ./docs/**/*.css", 33 | "lint": "npm run lint-js && npm run lint-css", 34 | "jstest": "mocha --exit ./test/spec/**/*.js", 35 | "coverage": "nyc --reporter=html --reporter=text --reporter=lcovonly --check-coverage=true mocha --exit ./test/spec/**/*.js", 36 | "docs": "jsdoc2md lib/**/*.js > ./docs/api.md", 37 | "test": "npm run clean && npm run format && npm run lint && npm run docs && npm run coverage", 38 | "postpublish": "git fetch && git pull" 39 | }, 40 | "dependencies": { 41 | "funcs-js": "latest", 42 | "node-later": "latest" 43 | }, 44 | "devDependencies": { 45 | "chai": "^4", 46 | "eslint": "^8", 47 | "eventemitter2": "^6", 48 | "js-beautify": "^1", 49 | "jsdoc-to-markdown": "^8", 50 | "mocha": "^10", 51 | "nyc": "^15", 52 | "stylelint": "^13", 53 | "stylelint-config-standard": "^22" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/event-emitter-enhancer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | //load dependencies 4 | const events = require('events'); 5 | const EventEmitter = events.EventEmitter; 6 | const EnhancedEventEmitter = require('./enhanced-event-emitter'); 7 | 8 | /** 9 | * This class enables to enhance event emitter prototypes and instances with extra capabilities. 10 | * 11 | * @author Sagie Gur-Ari 12 | * @class EventEmitterEnhancer 13 | * @public 14 | */ 15 | function EventEmitterEnhancer() { 16 | this.EnhancedEventEmitter = this.extend(EventEmitter); 17 | } 18 | 19 | /** 20 | * Throws the already enhanced error in case provided input 21 | * has already been enhanced. 22 | * 23 | * @function 24 | * @memberof! EventEmitterEnhancer 25 | * @private 26 | * @param {Object} type - The type to validate 27 | */ 28 | EventEmitterEnhancer.prototype.validateDoubleEnhancement = function (type) { 29 | if (type.enhancedEmitterType) { 30 | throw new Error('Provided input has already been enhanced.'); 31 | } 32 | }; 33 | 34 | /** 35 | * Modified/extends the provided object prototype with the extended emitter capabilities.
36 | * The provided object type must have an Node.js events.EventEmitter compatible interface. 37 | * 38 | * @function 39 | * @memberof! EventEmitterEnhancer 40 | * @private 41 | * @param {Object} EmitterType - The object type 42 | * @param {Number} modifyType - 0 to extend the prototype of the provided object, 1 to modify the prototype of the provided object, 2 to modify the provided instance 43 | * @returns {Object} The modified object type 44 | */ 45 | EventEmitterEnhancer.prototype.enhance = function (EmitterType, modifyType) { 46 | const self = this; 47 | 48 | let Emitter; 49 | let EnhancedEventEmitterType = null; 50 | switch (modifyType) { 51 | case 0: //extend prototype 52 | self.validateDoubleEnhancement(EmitterType.prototype); 53 | 54 | Emitter = function () { 55 | EmitterType.call(self); 56 | }; 57 | 58 | //extend the provided type 59 | Emitter.prototype = Object.create(EmitterType.prototype); 60 | Emitter.prototype.constructor = Emitter; 61 | 62 | EnhancedEventEmitterType = Emitter; 63 | Emitter = Emitter.prototype; 64 | 65 | break; 66 | case 1: //modify prototype 67 | self.validateDoubleEnhancement(EmitterType.prototype); 68 | 69 | Emitter = EmitterType; 70 | 71 | EnhancedEventEmitterType = Emitter; 72 | Emitter = Emitter.prototype; 73 | 74 | break; 75 | case 2: //modify instance 76 | self.validateDoubleEnhancement(EmitterType); 77 | 78 | Emitter = EmitterType; 79 | 80 | break; 81 | } 82 | 83 | //keep original needed functions before replacing it with enhanced version 84 | Emitter.baseEmit = Emitter.emit; 85 | Emitter.baseOn = Emitter.on; 86 | Emitter.baseOnce = Emitter.once; 87 | Emitter.baseRemoveAllListeners = Emitter.removeAllListeners; 88 | 89 | const functions = Object.keys(EnhancedEventEmitter.prototype); 90 | functions.forEach(function addProperty(property) { 91 | Emitter[property] = EnhancedEventEmitter.prototype[property]; 92 | }); 93 | 94 | return EnhancedEventEmitterType; 95 | }; 96 | 97 | /** 98 | * Extends the provided object prototype with the extended emitter capabilities.
99 | * The provided object type must have an Node.js events.EventEmitter compatible interface. 100 | * 101 | * @function 102 | * @memberof! EventEmitterEnhancer 103 | * @public 104 | * @param {Object} EmitterType - The object type 105 | * @returns {Object} The modified object type 106 | * @example 107 | * ```js 108 | * //extend events.EventEmitter class (or any class that has the same interface) 109 | * //now you can create instances of the new EnhancedEventEmitter type while events.EventEmitter is not modified/impacted in any way 110 | * const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); //extend the event emitter class (can be Node.js of some custom event emitter). original base class is not affected. 111 | * const emitter = new EnhancedEventEmitter(); //create a new instance using the new extended class type. 112 | * ``` 113 | */ 114 | EventEmitterEnhancer.prototype.extend = function (EmitterType) { 115 | return this.enhance(EmitterType, 0); 116 | }; 117 | 118 | /** 119 | * Modified the provided object prototype with the extended emitter capabilities.
120 | * The provided object type must have an Node.js events.EventEmitter compatible interface. 121 | * 122 | * @function 123 | * @memberof! EventEmitterEnhancer 124 | * @public 125 | * @param {Object} EmitterType - The object type 126 | * @example 127 | * ```js 128 | * //modify the proto of an events.EventEmitter class (or any class that has the same interface) 129 | * //now all existing and future instances of the original class are modified to include the new extended capabilities. 130 | * EventEmitterEnhancer.modify(EventEmitter); //modify the event emitter class prototype (can be Node.js of some custom event emitter). existing instances are impacted. 131 | * const emitter = new EventEmitter(); //create an instance of the original class and automatically get the new extended capabilities. 132 | * ``` 133 | */ 134 | EventEmitterEnhancer.prototype.modify = function (EmitterType) { 135 | this.enhance(EmitterType, 1); 136 | }; 137 | 138 | /** 139 | * Modified the specific object instance with the extended emitter capabilities.
140 | * The provided object type must have an Node.js events.EventEmitter compatible interface. 141 | * 142 | * @function 143 | * @memberof! EventEmitterEnhancer 144 | * @public 145 | * @param {Object} emitterInstance - The emitter instance 146 | * @example 147 | * ```js 148 | * //modify specific instance to include the extended capabilities (other existing/future instances of that class type are not modified/impacted in any way). 149 | * const emitter = new EventEmitter(); //create an instance of an event emitter (can be Node.js of some custom event emitter) 150 | * EventEmitterEnhancer.modifyInstance(emitter); //modify the specific instance and add the extended capabilities. the original prototype is not affected. 151 | * ``` 152 | */ 153 | EventEmitterEnhancer.prototype.modifyInstance = function (emitterInstance) { 154 | this.enhance(emitterInstance, 2); 155 | }; 156 | 157 | /** 158 | * The node.js event emitter prototype extended with the extra capabilities. 159 | * 160 | * @member {EventEmitter} 161 | * @alias EventEmitterEnhancer.EnhancedEventEmitter 162 | * @memberof! EventEmitterEnhancer 163 | * @public 164 | */ 165 | 166 | module.exports = new EventEmitterEnhancer(); 167 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /lib/event-emitter-enhancer.d.ts: -------------------------------------------------------------------------------- 1 | import EventEmitter from 'events' 2 | 3 | /** 4 | * A unique event name, defined by a string or a symbol. 5 | */ 6 | type Event = string | symbol 7 | 8 | /** 9 | * An optional callback attached to asynchronously emitted events, fired after 10 | * the event has been dispatched. 11 | */ 12 | interface AsyncEmitCallback extends Function { 13 | (event: Event, emitted: boolean): void, 14 | (event: Event, arg0: any, emitted: boolean): void, 15 | (event: Event, arg0: any, arg1: any, emitted: boolean): void, 16 | (event: Event, arg0: any, arg1: any, arg2: any, emitted: boolean): void, 17 | (event: Event, arg0: any, arg1: any, arg2: any, arg3: any, emitted: boolean): void, 18 | (event: Event, arg0: any, arg1: any, arg2: any, arg3: any, arg4: any, emitted: boolean): void, 19 | (event: Event, ...args: Array): void, 20 | } 21 | 22 | /** 23 | * The filter function responsible for pre-processing events. 24 | */ 25 | interface FilterCallback extends Function { 26 | (event: Event, ...args: any[]): boolean, 27 | } 28 | 29 | /** 30 | * A basic event handler that receives the event name as its first parameter 31 | * along with any arguments passed to the dispatcher. 32 | */ 33 | interface ListenerFunction extends Function { 34 | (event: Event, ...args: any[]): void, 35 | } 36 | 37 | /** 38 | * Options for the more complex version of the "on" method, which supports 39 | * multiple events, multiple listeners, asynchronous events, and a timeout. 40 | */ 41 | interface OnOptions { 42 | /** 43 | * Whether the event handler should be called asynchronously. 44 | */ 45 | async?: boolean, 46 | 47 | /** 48 | * The event or events to handle. 49 | */ 50 | event: Event | Event[], 51 | 52 | /** 53 | * The handler or handlers to call when events are triggered. 54 | */ 55 | listener: ListenerFunction | ListenerFunction[], 56 | 57 | /** 58 | * Optionally disable the listener after a certain number of milliseconds. 59 | */ 60 | timeout?: number, 61 | } 62 | 63 | interface RemoveFunction extends Function { 64 | (): void, 65 | } 66 | 67 | /** 68 | * Extends the Node.js EventEmitter with additional capabilities. 69 | */ 70 | export class EnhancedEventEmitter { 71 | /** 72 | * If true, listeners (including "else" listeners) will not be triggered 73 | * on emit. 74 | */ 75 | suspended: boolean 76 | 77 | /** 78 | * If defined, events will be splitted by this separator and emitted as partials.
79 | * For example, if the separator is ':' an event event1:event2:event3 will be emitted as 3 events: event1, event1:event2, event1:event2:event3. 80 | */ 81 | subscriptionSeparator: string 82 | 83 | /** 84 | * Adds a filter function that will be triggered before every emit of a 85 | * specific event. The filter should return "true" to permit dispatch, or 86 | * "false" to block dispatch. 87 | */ 88 | addEventFilter(event: Event, filter: FilterCallback): RemoveFunction 89 | 90 | /** 91 | * Adds a filter function that will be triggered before every emit (if no 92 | * event name is provided) or before every emit of a specific event (if an 93 | * event name is passed as the first parameter). The filter should return 94 | * "true" to permit dispatch, or "false" to block dispatch. 95 | */ 96 | addFilter(filter: FilterCallback): RemoveFunction 97 | 98 | /** 99 | * Adds a filter function that will be triggered before every emit (if no 100 | * event name is provided) or before every emit of a specific event (if an 101 | * event name is passed as the first parameter). The filter should return 102 | * "true" to permit dispatch, or "false" to block dispatch. 103 | */ 104 | addFilter(event: Event, filter: FilterCallback): RemoveFunction 105 | 106 | /** 107 | * Adds a filter function that will be triggered before every emit. The 108 | * filter should return "true" to permit dispatch, or "false" to block 109 | * dispatch. 110 | */ 111 | addGlobalFilter(filter: FilterCallback): RemoveFunction 112 | 113 | /** 114 | * The base Node.js "addListener" function, accepting an event name and 115 | * a listener function and returning the instance for chaining. 116 | */ 117 | addListener(event: Event, listener: ListenerFunction): this 118 | 119 | /** 120 | * Creates an empty listener for a given event. 121 | */ 122 | addNoop(event: Event): RemoveFunction 123 | 124 | /** 125 | * Adds a listener which will be triggered by all events that do not have 126 | * a matching listener. 127 | */ 128 | else(listener: ListenerFunction): void 129 | 130 | /** 131 | * If an event with the provided name is emitted, but no listeners are 132 | * listening for it, the EnhancedEventEmitter will emit an error. 133 | */ 134 | elseError(event: Event): void 135 | 136 | /** 137 | * Causes the emitter to trigger all listeners attached to the provided 138 | * event. Returns "true" if the event was handled, "false" if it was not. 139 | */ 140 | emit(event: Event, ...args: any[]): boolean 141 | 142 | /** 143 | * Emits the event asynchronously, ensuring flow is not blocked by 144 | * listeners. 145 | */ 146 | emitAsync(event: Event, arg0: any, callback?: AsyncEmitCallback): void 147 | 148 | /** 149 | * Emits the event asynchronously, ensuring flow is not blocked by 150 | * listeners. 151 | */ 152 | emitAsync(event: Event, arg0: any, arg1: any, callback?: AsyncEmitCallback): void 153 | 154 | /** 155 | * Emits the event asynchronously, ensuring flow is not blocked by 156 | * listeners. 157 | */ 158 | emitAsync(event: Event, arg0: any, arg1: any, arg2: any, callback?: AsyncEmitCallback): void 159 | 160 | /** 161 | * Emits the event asynchronously, ensuring flow is not blocked by 162 | * listeners. 163 | */ 164 | emitAsync(event: Event, arg0: any, arg1: any, arg2: any, arg3: any, callback?: AsyncEmitCallback): void 165 | 166 | /** 167 | * Emits the event asynchronously, ensuring flow is not blocked by 168 | * listeners. 169 | */ 170 | emitAsync(event: Event, arg0: any, arg1: any, arg2: any, arg3: any, arg4: any, callback?: AsyncEmitCallback): void 171 | 172 | /** 173 | * Emits the event asynchronously, ensuring flow is not blocked by 174 | * listeners. 175 | */ 176 | emitAsync(event: Event, ...args: Array): void 177 | 178 | /** 179 | * Returns all configured event names with listeners attached. 180 | */ 181 | eventNames(): Event[] 182 | 183 | /** 184 | * Adds a filter function that will be triggered before every emit (if no 185 | * event name is provided) or before every emit of a specific event (if an 186 | * event name is passed as the first parameter). The filter should return 187 | * "true" to permit dispatch, or "false" to block dispatch. 188 | */ 189 | filter(filter: FilterCallback): RemoveFunction 190 | 191 | /** 192 | * Adds a filter function that will be triggered before every emit (if no 193 | * event name is provided) or before every emit of a specific event (if an 194 | * event name is passed as the first parameter). The filter should return 195 | * "true" to permit dispatch, or "false" to block dispatch. 196 | */ 197 | filter(event: Event, filter: FilterCallback): RemoveFunction 198 | 199 | /** 200 | * Get the maximum configured number of listeners per event name this 201 | * instance will allow. 202 | */ 203 | getMaxListeners(): number 204 | 205 | /** 206 | * Creates a dummy empty error handler to prevent unhandled errors from 207 | * crashing the process. 208 | */ 209 | ignoreError(): void 210 | 211 | /** 212 | * Returns the total number of listeners attached to the given event name. 213 | */ 214 | listenerCount(event: Event): number 215 | 216 | /** 217 | * Returns all listeners for a given event name. 218 | */ 219 | listeners(event: Event): ListenerFunction[] 220 | 221 | /** 222 | * Disables a previously configured event listener with the given name 223 | * and listener function and returns the instance for chaining. 224 | */ 225 | off(event: Event, listener: ListenerFunction): this 226 | 227 | /** 228 | * The Node.js EventEmitter "on" function. Returns a function that removes 229 | * the listener when called. 230 | */ 231 | on(event: Event, listener: ListenerFunction): RemoveFunction 232 | 233 | /** 234 | * Extends "on" with more complex capabilities, including providing 235 | * multiple listeners and event names, timing out the listener, and 236 | * more. To remove the listeners, the returned function must be 237 | * called instead of "removeEventListener." 238 | */ 239 | on(options: OnOptions): RemoveFunction 240 | 241 | /** 242 | * Adds a listener for all provided event names. To remove the listener, 243 | * the returned function must be called instead of "removeEventListener." 244 | */ 245 | onAny(events: Event[], listener: ListenerFunction): RemoveFunction 246 | 247 | /** 248 | * Adds a listener which will be triggered asynchronously, ensuring that 249 | * the listener is invoked after all other listeners without blocking flow. 250 | * To remove the listener, the returned function must be called instead of 251 | * "removeEventListener." 252 | */ 253 | onAsync(event: Event, listener: ListenerFunction): RemoveFunction 254 | 255 | /** 256 | * The Node.js EventEmitter "once" function. Returns a function that 257 | * removes the listener when called. 258 | */ 259 | once(event: Event, listener: ListenerFunction): RemoveFunction 260 | 261 | /** 262 | * Inserts a new event listener at the beginning of the listener queue, 263 | * returning the instance for chaining. 264 | */ 265 | prependListener(event: Event, listener: ListenerFunction): this 266 | 267 | /** 268 | * Inserts a new event listener which will be called only once at the 269 | * beginning of the listener queue, returning the instance for chaining. 270 | */ 271 | prependOnceListener(event: Event, listener: ListenerFunction): this 272 | 273 | /** 274 | * Proxies events emitted by another emitter through this emitter, so that 275 | * it emits them itself. 276 | */ 277 | proxyEvents(emitter: EventEmitter | EnhancedEventEmitter, event: Event): RemoveFunction 278 | 279 | /** 280 | * Proxies events emitted by another emitter through this emitter, so that 281 | * it emits them itself. 282 | */ 283 | proxyEvents(emitter: EventEmitter | EnhancedEventEmitter, events: Event[]): RemoveFunction 284 | 285 | /** 286 | * Proxies events emitted by another emitter through this emitter, so that 287 | * it emits them itself. 288 | */ 289 | proxyEvents(emitters: Array, event: Event): RemoveFunction 290 | 291 | /** 292 | * Proxies events emitted by another emitter through this emitter, so that 293 | * it emits them itself. 294 | */ 295 | proxyEvents(emitters: Array, events: Event[]): RemoveFunction 296 | 297 | /** 298 | * The Node.js EventEmitter "removeAllListeners" function. 299 | */ 300 | removeAllListeners(event: Event): void 301 | 302 | /** 303 | * The Node.js EventEmitter "removeAllListeners" function, modified to 304 | * accept an array of event names. 305 | */ 306 | removeAllListeners(events: Event[]): this 307 | 308 | /** 309 | * Removes all "else" listeners. 310 | */ 311 | removeAllElseListeners(): void 312 | 313 | /** 314 | * Removes the "elseError" handler for the provided event. 315 | */ 316 | removeElseError(event: Event): void 317 | 318 | /** 319 | * Removes the "else" listener that calls the provided function. 320 | */ 321 | removeElseListener(listener: ListenerFunction): void 322 | 323 | /** 324 | * Disables a previously configured event listener with the given name 325 | * and listener function and returns the instance for chaining. 326 | */ 327 | removeListener(event: Event, listener: ListenerFunction): this 328 | 329 | /** 330 | * Set the maximum number of listeners per event name this instance 331 | * will allow. 332 | */ 333 | setMaxListeners(count: number): this 334 | 335 | /** 336 | * Suspends all listeners for the provided event name, including "else" 337 | * listeners. 338 | */ 339 | suspend(event: Event): void 340 | 341 | /** 342 | * Removes the "else" listener that calls the provided function. 343 | */ 344 | unelse(listener: ListenerFunction): void 345 | 346 | /** 347 | * Removes the "elseError" handler for the provided event. 348 | */ 349 | unelseError(event: Event): void 350 | 351 | /** 352 | * Resumes all listeners for the provided event name. 353 | */ 354 | unsuspend(event: Event): void 355 | } 356 | 357 | /** 358 | * An interface for extending EventEmitter prototypes with additional 359 | * capabilities. 360 | */ 361 | export class EventEmitterEnhancer { 362 | /** 363 | * A version of the EventEmitter already extended. 364 | */ 365 | static readonly EnhancedEventEmitter: typeof EnhancedEventEmitter 366 | 367 | /** 368 | * Extends the provided object prototype with the capabilities of the 369 | * EnhancedEventEmitter, returning a new EnhancedEventEmitter constructor. 370 | */ 371 | static extend(emitter: typeof EventEmitter): typeof EnhancedEventEmitter 372 | 373 | /** 374 | * Modifies the provided object prototype, replacing it with an extended 375 | * EnhancedEventEmitter with additional capabilities. 376 | */ 377 | static modify(emitter: typeof EventEmitter): void 378 | 379 | /** 380 | * Extends a single EventEmitter object with additional capabilities. 381 | */ 382 | static modifyInstance(emitter: EventEmitter): EnhancedEventEmitter 383 | } 384 | 385 | export default EventEmitterEnhancer 386 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # event-emitter-enhancer 2 | 3 | [![NPM Version](http://img.shields.io/npm/v/event-emitter-enhancer.svg?style=flat)](https://www.npmjs.org/package/event-emitter-enhancer) [![CI](https://github.com/sagiegurari/event-emitter-enhancer/workflows/CI/badge.svg?branch=master)](https://github.com/sagiegurari/event-emitter-enhancer/actions) [![Coverage Status](https://coveralls.io/repos/sagiegurari/event-emitter-enhancer/badge.svg)](https://coveralls.io/r/sagiegurari/event-emitter-enhancer) [![Known Vulnerabilities](https://snyk.io/test/github/sagiegurari/event-emitter-enhancer/badge.svg)](https://snyk.io/test/github/sagiegurari/event-emitter-enhancer) [![Inline docs](http://inch-ci.org/github/sagiegurari/event-emitter-enhancer.svg?branch=master)](http://inch-ci.org/github/sagiegurari/event-emitter-enhancer) [![License](https://img.shields.io/npm/l/event-emitter-enhancer.svg?style=flat)](https://github.com/sagiegurari/event-emitter-enhancer/blob/master/LICENSE) [![Total Downloads](https://img.shields.io/npm/dt/event-emitter-enhancer.svg?style=flat)](https://www.npmjs.org/package/event-emitter-enhancer) 4 | 5 | > Extends the Node.js events.EventEmitter to provide additional functionality. 6 | 7 | * [Overview](#overview) 8 | * [Usage](#usage) 9 | * [on(event, listener)](#usage-on1) 10 | * [on(options)](#usage-on2) 11 | * [once(event, listener)](#usage-once) 12 | * [removeAllListeners](#usage-removeAllListeners) 13 | * [else](#usage-else) 14 | * [suspend](#usage-suspend) 15 | * [elseError](#usage-else-error) 16 | * [emitAsync](#usage-emit-async) 17 | * [onAsync](#usage-on-async) 18 | * [onAny](#usage-on-any) 19 | * [filter](#usage-filter) 20 | * [proxyEvents](#usage-proxyEvents) 21 | * [addNoop](#usage-addNoop) 22 | * [ignoreError](#usage-ignoreError) 23 | * [Installation](#installation) 24 | * [API Documentation](docs/api.md) 25 | * [Contributing](.github/CONTRIBUTING.md) 26 | * [Release History](#history) 27 | * [License](#license) 28 | 29 | 30 | ## Overview 31 | This library extends the Node.js events.EventEmitter to provide additional functionality. 32 | 33 | 34 | ## Usage 35 | First you must require this library as follows: 36 | 37 | ```js 38 | var EventEmitterEnhancer = require('event-emitter-enhancer'); 39 | ``` 40 | 41 | Next you can either modify the proto of an EventEmiter type class, or extend it to get a new custom type or modify a specific emitter instance. 42 | 43 | ```js 44 | var EventEmitter = require('events').EventEmitter; 45 | 46 | //Get predefined extended version of the events.EventEmitter class (original EventEmitter is not impacted) 47 | var emitter = new EventEmitterEnhancer.EnhancedEventEmitter(); //create a new instance using the new extended class type. 48 | 49 | //extend events.EventEmitter class (or any class that has the same interface) 50 | //now you can create instances of the new EnhancedEventEmitter type while events.EventEmitter is not modified/impacted in any way 51 | var EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); //extend the event emitter class (can be Node.js of some custom event emitter). original base class is not affected. 52 | var emitter = new EnhancedEventEmitter(); //create a new instance using the new extended class type. 53 | 54 | //modify the proto of an events.EventEmitter class (or any class that has the same interface) 55 | //now all existing and future instances of the original class are modified to include the new extended capabilities. 56 | EventEmitterEnhancer.modify(EventEmitter); //modify the event emitter class prototype (can be Node.js of some custom event emitter). existing instances are impacted. 57 | var emitter = new EventEmitter(); //create an instance of the original class and automatically get the new extended capabilities. 58 | 59 | //modify specific instance to include the extended capabilities (other existing/future instances of that class type are not modified/impacted in any way). 60 | var emitter = new EventEmitter(); //create an instance of an event emitter (can be Node.js of some custom event emitter) 61 | EventEmitterEnhancer.modifyInstance(emitter); //modify the specific instance and add the extended capabilities. the original prototype is not affected. 62 | ``` 63 | 64 | 65 | 66 | ### 'emitter.on(event, listener) ⇒ function' 67 | See node.js events.EventEmitter.on.
68 | This function also returns a removeListener function to easily remove the provided listener. 69 | 70 | **Example** 71 | ```js 72 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 73 | const emitter = new EnhancedEventEmitter(); 74 | 75 | const remove = emitter.on('error', function (error) { 76 | console.error(error); 77 | }); 78 | 79 | //remove listener (no longer need to keep a reference to the listener function) 80 | remove(); 81 | ``` 82 | 83 | 84 | 85 | 86 | ### 'emitter.on(options) ⇒ function' 87 | Enables more complex on capabilities including providing multiple listeners/event names, timeout the listener and more.
88 | To remove the listener/s, the returned function must be called instead of doing emitter.removeListener(...) 89 | 90 | **Example** 91 | ```js 92 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 93 | const emitter = new EnhancedEventEmitter(); 94 | 95 | const removeListener = emitter.on({ 96 | event: ['error', 'connection-error', 'write-error', 'read-error'], //The event names (can be string for a single event) 97 | listener: [ //The listener callback functions (can be a function instead of an array for a single listener callback) 98 | function firstListener(arg1, arg2) { 99 | //do something 100 | }, 101 | function secondListener(arg1, arg2) { 102 | //do something 103 | } 104 | ], 105 | async: true, //The callback functions will be called after next tick 106 | timeout: 1500 //All listeners will be removed after the provided timeout (if not provided, listeners can only be removed manually via returned function) 107 | }); 108 | 109 | //emit any event 110 | emitter.emit('write-error', 1, 2, 3); 111 | 112 | //once done, remove all listeners from all events 113 | removeListener(); 114 | ``` 115 | 116 | 117 | 118 | 119 | ### 'emitter.once(event, listener) ⇒ function' 120 | See node.js events.EventEmitter.once.
121 | This function also returns a removeListener function to easily remove the provided listener. 122 | 123 | **Example** 124 | ```js 125 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 126 | const emitter = new EnhancedEventEmitter(); 127 | 128 | const remove = emitter.once('error', function (error) { 129 | console.error(error); 130 | }); 131 | 132 | //remove listener (no longer need to keep a reference to the listener function) 133 | remove(); 134 | ``` 135 | 136 | 137 | 138 | 139 | ### 'emitter.removeAllListeners([event])' 140 | See node.js events.EventEmitter.removeAllListeners.
141 | This function is modified to also accept an array of event names. 142 | 143 | **Example** 144 | ```js 145 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 146 | const emitter = new EnhancedEventEmitter(); 147 | 148 | //same as the basic removeAllListeners 149 | emitter.removeAllListeners('my-event'); 150 | 151 | //also supports array of event names 152 | emitter.removeAllListeners(['my-event', 'another-event']); 153 | ``` 154 | 155 | 156 | 157 | 158 | ### 'emitter.else(listener)' 159 | Adds an 'else' listener which will be triggered by all events that do not have a listener currently for them (apart of the special 'error' event). 160 | 161 | **Example** 162 | ```js 163 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 164 | const emitter = new EnhancedEventEmitter(); 165 | emitter.else(function onUnhandledEvent(event, arg1, arg2) { 166 | //logic here.... 167 | 168 | //to remove 'else' listeners, simply use the unelse function 169 | emitter.unelse(this); 170 | }); 171 | 172 | emitter.emit('test', 1, 2); 173 | ``` 174 | 175 | 176 | 177 | 178 | ### 'emitter.suspend(event)' 179 | Suspends all emit calls for the provided event name (including 'else' listeners).
180 | For suspended events, the emit function will simply do nothing ('else' listeners won't be invoked either). 181 | 182 | **Example** 183 | ```js 184 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 185 | const emitter = new EnhancedEventEmitter(); 186 | emitter.on('test', function () { 187 | //will never be called 188 | }); 189 | 190 | emitter.suspended = true; //suspend ALL events (to unsuspend use emitter.suspended = false;) 191 | //or 192 | emitter.suspend('test'); //suspend only 'test' event (to unsuspend use emitter.unsuspend('test');) 193 | 194 | emitter.emit('test'); 195 | ``` 196 | 197 | 198 | 199 | 200 | ### 'emitter.elseError(event)' 201 | In case an event with the provided name is emitted but no listener is attached to it, an error event will emitted by this emitter instance instead. 202 | 203 | **Example** 204 | ```js 205 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 206 | const emitter = new EnhancedEventEmitter(); 207 | emitter.on('error', function (error) { 208 | //logic here... 209 | 210 | //To remove elseError 211 | emitter.unelseError('test'); 212 | }); 213 | 214 | emitter.elseError('test'); 215 | 216 | emitter.emit('test'); 217 | ``` 218 | 219 | 220 | 221 | 222 | ### 'emitter.emitAsync(event, [...params], [callback])' 223 | Invokes the emit after a timeout to enable calling flow to continue and not block due to event listeners. 224 | 225 | **Example** 226 | ```js 227 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 228 | const emitter = new EnhancedEventEmitter(); 229 | emitter.on('test', function onTestEvent(num1, num2) { 230 | //event logic here 231 | }); 232 | 233 | emitter.emitAsync('test', 1, 2, function onEmitDone(event, num1, num2, emitted) { 234 | //emit callback logic 235 | }); 236 | ``` 237 | 238 | 239 | 240 | 241 | ### 'emitter.onAsync(event, listener) ⇒ function' 242 | Adds a listener that will be triggered after a timeout during an emit.
243 | This ensures that the provided listener is invoked after all other listeners and that it will not block the emit caller flow.
244 | To remove the listener, the returned function must be called instead of doing emitter.removeListener(...) 245 | 246 | **Example** 247 | ```js 248 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 249 | const emitter = new EnhancedEventEmitter(); 250 | emitter.on('test', function onEventSync() { 251 | //sync handle function logic 252 | }); 253 | const removeListener = emitter.onAsync('test', function onEventAsync() { 254 | //async handle function logic 255 | }); 256 | 257 | emitter.emit('test', 1, 2); 258 | 259 | //remove the async listener 260 | removeListener(); 261 | ``` 262 | 263 | 264 | 265 | 266 | ### 'emitter.onAny(events, listener) ⇒ function' 267 | Adds a listener to all provided event names.
268 | To remove the listener, the returned function must be called instead of doing emitter.removeListener(...) 269 | 270 | **Example** 271 | ```js 272 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 273 | const emitter = new EnhancedEventEmitter(); 274 | 275 | //add same listener to multiple events 276 | const remove = emitter.onAny(['test1', 'test2', 'test3'], function (arg1, arg2, arg3) { 277 | console.log(arg1, arg2, arg3); 278 | }); 279 | 280 | //remove listener from all events 281 | remove(); 282 | ``` 283 | 284 | 285 | 286 | 287 | ### 'emitter.filter([event], filter) ⇒ function' 288 | Adds a filter that will be triggered before every emit for the provided event type (if no event is provided, than the filter is invoked for all events).
289 | The filter enables to prevent events from reaching the listeners in case some criteria is met. 290 | 291 | **Example** 292 | ```js 293 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 294 | const emitter = new EnhancedEventEmitter(); 295 | 296 | //add filters for test event only 297 | const removeTestEventFilter = emitter.filter('test', function (event, arg1, arg2) { 298 | if (arg1 && (arg1 > 3)) { 299 | return true; //continue with emit 300 | } 301 | 302 | return false; //prevent emit 303 | }); 304 | emitter.filter('test', function (event, arg1, arg2) { 305 | if (arg2 && (arg2 < 20)) { 306 | return true; //continue with emit 307 | } 308 | 309 | return false; //prevent emit 310 | }); 311 | 312 | //add global filter for all events 313 | emitter.filter(function (event, arg1, arg2) { 314 | if (arg1 && (arg1 > 5)) { 315 | return true; //continue with emit 316 | } 317 | 318 | return false; //prevent emit 319 | }); 320 | const removeGlobalArg2Filter = emitter.filter(function (event, arg1, arg2) { 321 | if (arg2 && (arg2 < 18)) { 322 | return true; //continue with emit 323 | } 324 | 325 | return false; //prevent emit 326 | }); 327 | 328 | emitter.on('test', function onTestEvent(arg1, arg2) { 329 | //event logic here... 330 | }); 331 | 332 | emitter.emit('test', 10, 15); 333 | 334 | //remove some filters 335 | removeTestEventFilter(); 336 | removeGlobalArg2Filter(); 337 | ``` 338 | 339 | 340 | 341 | 342 | ### 'emitter.proxyEvents(emitters, events) ⇒ function' 343 | Will setup an event proxy so if any of the requested event/s are fired from the provided emitter/s, they will be triggered by this emitter. 344 | 345 | **Example** 346 | ```js 347 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 348 | const emitter = new EnhancedEventEmitter(); 349 | 350 | //proxy the 'data' and 'end' events from all sockets 351 | const stop = emitter.proxyEvents(sockets, ['data', 'end']); 352 | 353 | //listen to events via emitter 354 | emitter.on('data', onData); 355 | 356 | //stop events proxy 357 | stop(); 358 | ``` 359 | 360 | 361 | 362 | 363 | ### 'emitter.addNoop(event) ⇒ function' 364 | Adds empty event handler. 365 | 366 | **Example** 367 | ```js 368 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 369 | const emitter = new EnhancedEventEmitter(); 370 | 371 | //add noop even handler for the 'error' event 372 | const remove = emitter.addNoop('error'); 373 | 374 | //remove listener 375 | remove(); 376 | ``` 377 | 378 | 379 | 380 | 381 | ### 'emitter.ignoreError()' 382 | Adds empty error event handler to prevent node.js from crashing in case of an error which we do not want/need to handle.
383 | This function will only add a new empty handler in case no other handler is defined for the error event. 384 | 385 | **Example** 386 | ```js 387 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 388 | const emitter = new EnhancedEventEmitter(); 389 | 390 | //adds empty error handler 391 | emitter.ignoreError(); 392 | 393 | //emit error will not crash the node.js process 394 | emitter.emit('error', new Error('test')); 395 | ``` 396 | 397 | 398 | 399 | ## Installation 400 | In order to use this library, just run the following npm install command: 401 | 402 | ```sh 403 | npm install --save event-emitter-enhancer 404 | ``` 405 | 406 | ## API Documentation 407 | See full docs at: [API Docs](docs/api.md) 408 | 409 | ## Contributing 410 | See [contributing guide](.github/CONTRIBUTING.md) 411 | 412 | 413 | ## Release History 414 | 415 | | Date | Version | Description | 416 | | ----------- | ------- | ----------- | 417 | | 2020-05-11 | v2.0.0 | Migrate to github actions and upgrade minimal node version | 418 | | 2019-12-06 | v1.1.0 | Support event parent parsing and emit #7 | 419 | | 2018-06-13 | v1.0.57 | Added typescript definition (#5 and #6) | 420 | | 2017-11-03 | v1.0.51 | Added 'addNoop' | 421 | | 2017-10-30 | v1.0.49 | Added 'ignoreError' | 422 | | 2017-10-30 | v1.0.48 | New extended 'removeAllListeners' function | 423 | | 2017-01-16 | v1.0.27 | New extended 'once' function | 424 | | 2017-01-07 | v1.0.25 | New 'proxyEvents' function | 425 | | 2017-01-06 | v1.0.24 | New extended 'on' function | 426 | | 2016-11-11 | v1.0.15 | 'emitAsync' callback is now optional | 427 | | 2015-09-23 | v0.0.44 | Added 'onAny' | 428 | | 2015-04-22 | v0.0.31 | Prevent from multiple enhance of same prototype/instance | 429 | | 2014-12-31 | v0.0.10 | EventEmitter is no longer automatically modified,
instead there are 2 ways to extend/modify prototype/modify instance
functions exposed by this library. | 430 | | 2014-12-30 | v0.0.9 | Added ability to enhance compatible EventEmitter types | 431 | | 2014-12-29 | v0.0.6 | Added 'filter' | 432 | | 2014-12-28 | v0.0.5 | Added 'onAsync' | 433 | | 2014-12-28 | v0.0.4 | Added 'emitAsync' | 434 | | 2014-12-28 | v0.0.2 | Initial release. | 435 | 436 | 437 | ## License 438 | Developed by Sagie Gur-Ari and licensed under the Apache 2 open source license. 439 | -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | ## Classes 2 | 3 |
4 |
EnhancedEventEmitter
5 |
6 |
EventEmitterEnhancer
7 |
8 |
9 | 10 | ## Objects 11 | 12 |
13 |
EventEmitterEnhancer : object
14 |

Extends the Node.js events.EventEmitter with extra capabilities.

15 |
16 |
17 | 18 | ## Typedefs 19 | 20 |
21 |
FilterCallbackBoolean
22 |

'filter' callback.

23 |
24 |
ElseCallback : function
25 |

'else' callback.

26 |
27 |
AsyncEmitCallback : function
28 |

'async-emit' callback.

29 |
30 |
31 | 32 | 33 | 34 | ## EnhancedEventEmitter 35 | **Kind**: global class 36 | **Access**: public 37 | **Author**: Sagie Gur-Ari 38 | 39 | * [EnhancedEventEmitter](#EnhancedEventEmitter) 40 | * [new EnhancedEventEmitter()](#new_EnhancedEventEmitter_new) 41 | * [.suspended](#EnhancedEventEmitter.suspended) : Boolean 42 | * [.subscriptionSeparator](#EnhancedEventEmitter.subscriptionSeparator) : String 43 | * [#on(event, listener)](#EnhancedEventEmitter+on) ⇒ function 44 | * [#on(options)](#EnhancedEventEmitter+on) ⇒ function 45 | * [#once(event, listener)](#EnhancedEventEmitter+once) ⇒ function 46 | * [#removeAllListeners([event])](#EnhancedEventEmitter+removeAllListeners) 47 | * [#suspend(event)](#EnhancedEventEmitter+suspend) 48 | * [#unsuspend(event)](#EnhancedEventEmitter+unsuspend) 49 | * [#else(listener)](#EnhancedEventEmitter+else) 50 | * [#removeElseListener(listener)](#EnhancedEventEmitter+removeElseListener) 51 | * [#unelse(listener)](#EnhancedEventEmitter+unelse) 52 | * [#removeAllElseListeners()](#EnhancedEventEmitter+removeAllElseListeners) 53 | * [#elseError(event)](#EnhancedEventEmitter+elseError) 54 | * [#removeElseError(event)](#EnhancedEventEmitter+removeElseError) 55 | * [#unelseError(event)](#EnhancedEventEmitter+unelseError) 56 | * [#emit(event, [params])](#EnhancedEventEmitter+emit) ⇒ Boolean 57 | * [#emitAsync(event, [params], [callback])](#EnhancedEventEmitter+emitAsync) 58 | * [#onAsync(event, listener)](#EnhancedEventEmitter+onAsync) ⇒ function 59 | * [#onAny(events, listener)](#EnhancedEventEmitter+onAny) ⇒ function 60 | * [#addFilter([event], filter)](#EnhancedEventEmitter+addFilter) ⇒ function 61 | * [#addEventFilter(event, filter)](#EnhancedEventEmitter+addEventFilter) ⇒ function 62 | * [#addGlobalFilter(filter)](#EnhancedEventEmitter+addGlobalFilter) ⇒ function 63 | * [#filter([event], filter)](#EnhancedEventEmitter+filter) ⇒ function 64 | * [#proxyEvents(emitters, events)](#EnhancedEventEmitter+proxyEvents) ⇒ function 65 | * [#addNoop(event)](#EnhancedEventEmitter+addNoop) ⇒ function 66 | * [#ignoreError()](#EnhancedEventEmitter+ignoreError) 67 | 68 | 69 | 70 | ### new EnhancedEventEmitter() 71 | This class holds all the extended capabilities added to any emitter. 72 | 73 | 74 | 75 | ### EnhancedEventEmitter.suspended : Boolean 76 | If true, all events will not trigger any listener (or 'else' listener).
77 | The emit function will simply do nothing. 78 | 79 | **Access**: public 80 | 81 | 82 | ### EnhancedEventEmitter.subscriptionSeparator : String 83 | If defined, events will be splitted by this separator and emitted as partials.
84 | For example, if the separator is ':' an event event1:event2:event3 will be emitted as 3 events: event1, event1:event2, event1:event2:event3. 85 | 86 | **Access**: public 87 | 88 | 89 | ### EnhancedEventEmitter#on(event, listener) ⇒ function 90 | See node.js events.EventEmitter.on.
91 | This function also returns a removeListener function to easily remove the provided listener. 92 | 93 | **Returns**: function - The remove listener function 94 | **Access**: public 95 | 96 | | Param | Type | Description | 97 | | --- | --- | --- | 98 | | event | String | The name of the event | 99 | | listener | function | The callback function | 100 | 101 | **Example** 102 | ```js 103 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 104 | const emitter = new EnhancedEventEmitter(); 105 | 106 | const remove = emitter.on('error', function (error) { 107 | console.error(error); 108 | }); 109 | 110 | //remove listener (no longer need to keep a reference to the listener function) 111 | remove(); 112 | ``` 113 | 114 | 115 | ### EnhancedEventEmitter#on(options) ⇒ function 116 | Enables more complex on capabilities including providing multiple listeners/event names, timeout the listener and more.
117 | To remove the listener/s, the returned function must be called instead of doing emitter.removeListener(...) 118 | 119 | **Returns**: function - The remove listener function 120 | **Access**: public 121 | 122 | | Param | Type | Default | Description | 123 | | --- | --- | --- | --- | 124 | | options | Object | | All options needed to setup the listeners | 125 | | options.event | Array.<String> \| String | | The event name or an array of event names | 126 | | options.listener | Array.<function()> \| function | | The callback function or an array of callback functions | 127 | | [options.async] | Boolean | false | If true, the callback functions will be called after next tick | 128 | | [options.timeout] | Number | | If provided, the returned remove listener function will be called after the provided timeout value in millies (unless called manually before the timeout was triggered) | 129 | 130 | **Example** 131 | ```js 132 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 133 | const emitter = new EnhancedEventEmitter(); 134 | 135 | const removeListener = emitter.on({ 136 | event: ['error', 'connection-error', 'write-error', 'read-error'], //The event names (can be string for a single event) 137 | listener: [ //The listener callback functions (can be a function instead of an array for a single listener callback) 138 | function firstListener(arg1, arg2) { 139 | //do something 140 | }, 141 | function secondListener(arg1, arg2) { 142 | //do something 143 | } 144 | ], 145 | async: true, //The callback functions will be called after next tick 146 | timeout: 1500 //All listeners will be removed after the provided timeout (if not provided, listeners can only be removed manually via returned function) 147 | }); 148 | 149 | //emit any event 150 | emitter.emit('write-error', 1, 2, 3); 151 | 152 | //once done, remove all listeners from all events 153 | removeListener(); 154 | ``` 155 | 156 | 157 | ### EnhancedEventEmitter#once(event, listener) ⇒ function 158 | See node.js events.EventEmitter.once.
159 | This function also returns a removeListener function to easily remove the provided listener. 160 | 161 | **Returns**: function - The remove listener function 162 | **Access**: public 163 | 164 | | Param | Type | Description | 165 | | --- | --- | --- | 166 | | event | String | The name of the event | 167 | | listener | function | The callback function | 168 | 169 | **Example** 170 | ```js 171 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 172 | const emitter = new EnhancedEventEmitter(); 173 | 174 | const remove = emitter.once('error', function (error) { 175 | console.error(error); 176 | }); 177 | 178 | //remove listener (no longer need to keep a reference to the listener function) 179 | remove(); 180 | ``` 181 | 182 | 183 | ### EnhancedEventEmitter#removeAllListeners([event]) 184 | See node.js events.EventEmitter.removeAllListeners.
185 | This function is modified to also accept an array of event names. 186 | 187 | **Access**: public 188 | 189 | | Param | Type | Description | 190 | | --- | --- | --- | 191 | | [event] | String \| Array | The name/s of the event | 192 | 193 | **Example** 194 | ```js 195 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 196 | const emitter = new EnhancedEventEmitter(); 197 | 198 | //same as the basic removeAllListeners 199 | emitter.removeAllListeners('my-event'); 200 | 201 | //also supports array of event names 202 | emitter.removeAllListeners(['my-event', 'another-event']); 203 | ``` 204 | 205 | 206 | ### EnhancedEventEmitter#suspend(event) 207 | Suspends all emit calls for the provided event name (including 'else' listeners).
208 | For suspended events, the emit function will simply do nothing ('else' listeners won't be invoked either). 209 | 210 | **Access**: public 211 | 212 | | Param | Type | Description | 213 | | --- | --- | --- | 214 | | event | String | The event to suspend | 215 | 216 | **Example** 217 | ```js 218 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 219 | const emitter = new EnhancedEventEmitter(); 220 | emitter.on('test', function () { 221 | //will never be called 222 | }); 223 | 224 | emitter.suspended = true; //suspend ALL events (to unsuspend use emitter.suspended = false;) 225 | //or 226 | emitter.suspend('test'); //suspend only 'test' event (to unsuspend use emitter.unsuspend('test');) 227 | 228 | emitter.emit('test'); 229 | ``` 230 | 231 | 232 | ### EnhancedEventEmitter#unsuspend(event) 233 | Unsuspends the emit calls for the provided event name. 234 | 235 | **Access**: public 236 | 237 | | Param | Type | Description | 238 | | --- | --- | --- | 239 | | event | String | The event to unsuspend | 240 | 241 | 242 | 243 | ### EnhancedEventEmitter#else(listener) 244 | Adds an 'else' listener which will be triggered by all events that do not have a listener currently for them (apart of the special 'error' event). 245 | 246 | **Access**: public 247 | 248 | | Param | Type | Description | 249 | | --- | --- | --- | 250 | | listener | [ElseCallback](#ElseCallback) | The listener that will catch all 'else' events | 251 | 252 | **Example** 253 | ```js 254 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 255 | const emitter = new EnhancedEventEmitter(); 256 | emitter.else(function onUnhandledEvent(event, arg1, arg2) { 257 | //logic here.... 258 | 259 | //to remove 'else' listeners, simply use the unelse function 260 | emitter.unelse(this); 261 | }); 262 | 263 | emitter.emit('test', 1, 2); 264 | ``` 265 | 266 | 267 | ### EnhancedEventEmitter#removeElseListener(listener) 268 | Removes the provided 'else' listener.
269 | Same as 'unelse' function. 270 | 271 | **Access**: public 272 | 273 | | Param | Type | Description | 274 | | --- | --- | --- | 275 | | listener | [ElseCallback](#ElseCallback) | The listener to remove | 276 | 277 | 278 | 279 | ### EnhancedEventEmitter#unelse(listener) 280 | See 'removeElseListener' documentation. 281 | 282 | **Access**: public 283 | 284 | | Param | Type | Description | 285 | | --- | --- | --- | 286 | | listener | [ElseCallback](#ElseCallback) | The listener to remove | 287 | 288 | 289 | 290 | ### EnhancedEventEmitter#removeAllElseListeners() 291 | Removes all 'else' listeners. 292 | 293 | **Access**: public 294 | 295 | 296 | ### EnhancedEventEmitter#elseError(event) 297 | In case an event with the provided name is emitted but no listener is attached to it, an error event will emitted by this emitter instance instead. 298 | 299 | **Access**: public 300 | 301 | | Param | Type | Description | 302 | | --- | --- | --- | 303 | | event | String | The event name | 304 | 305 | **Example** 306 | ```js 307 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 308 | const emitter = new EnhancedEventEmitter(); 309 | emitter.on('error', function (error) { 310 | //logic here... 311 | 312 | //To remove elseError 313 | emitter.unelseError('test'); 314 | }); 315 | 316 | emitter.elseError('test'); 317 | 318 | emitter.emit('test'); 319 | ``` 320 | 321 | 322 | ### EnhancedEventEmitter#removeElseError(event) 323 | Removes the else-error handler for the provided event.
324 | Same as 'unelseError' function. 325 | 326 | **Access**: public 327 | 328 | | Param | Type | Description | 329 | | --- | --- | --- | 330 | | event | String | The event name | 331 | 332 | 333 | 334 | ### EnhancedEventEmitter#unelseError(event) 335 | See 'removeElseError' documentation. 336 | 337 | **Access**: public 338 | 339 | | Param | Type | Description | 340 | | --- | --- | --- | 341 | | event | String | The event name | 342 | 343 | 344 | 345 | ### EnhancedEventEmitter#emit(event, [params]) ⇒ Boolean 346 | See Node.js events.EventEmitter documentation. 347 | 348 | **Returns**: Boolean - True if a listener or an 'else' listener handled the event 349 | **Access**: public 350 | 351 | | Param | Type | Description | 352 | | --- | --- | --- | 353 | | event | String | The event name | 354 | | [params] | \* | The event parameters | 355 | 356 | 357 | 358 | ### EnhancedEventEmitter#emitAsync(event, [params], [callback]) 359 | Invokes the emit after a timeout to enable calling flow to continue and not block due to event listeners. 360 | 361 | **Access**: public 362 | 363 | | Param | Type | Description | 364 | | --- | --- | --- | 365 | | event | String | The event name | 366 | | [params] | \* | The event parameters (if last param is a function, it is considered as the callback of the emitAsync) | 367 | | [callback] | [AsyncEmitCallback](#AsyncEmitCallback) | The async callback | 368 | 369 | **Example** 370 | ```js 371 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 372 | const emitter = new EnhancedEventEmitter(); 373 | emitter.on('test', function onTestEvent(num1, num2) { 374 | //event logic here 375 | }); 376 | 377 | emitter.emitAsync('test', 1, 2, function onEmitDone(event, num1, num2, emitted) { 378 | //emit callback logic 379 | }); 380 | ``` 381 | 382 | 383 | ### EnhancedEventEmitter#onAsync(event, listener) ⇒ function 384 | Adds a listener that will be triggered after a timeout during an emit.
385 | This ensures that the provided listener is invoked after all other listeners and that it will not block the emit caller flow.
386 | To remove the listener, the returned function must be called instead of doing emitter.removeListener(...) 387 | 388 | **Returns**: function - The remove listener function 389 | **Access**: public 390 | 391 | | Param | Type | Description | 392 | | --- | --- | --- | 393 | | event | String | The event name | 394 | | listener | function | The listener function | 395 | 396 | **Example** 397 | ```js 398 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 399 | const emitter = new EnhancedEventEmitter(); 400 | emitter.on('test', function onEventSync() { 401 | //sync handle function logic 402 | }); 403 | const removeListener = emitter.onAsync('test', function onEventAsync() { 404 | //async handle function logic 405 | }); 406 | 407 | emitter.emit('test', 1, 2); 408 | 409 | //remove the async listener 410 | removeListener(); 411 | ``` 412 | 413 | 414 | ### EnhancedEventEmitter#onAny(events, listener) ⇒ function 415 | Adds a listener to all provided event names.
416 | To remove the listener, the returned function must be called instead of doing emitter.removeListener(...) 417 | 418 | **Returns**: function - The remove listener function 419 | **Access**: public 420 | 421 | | Param | Type | Description | 422 | | --- | --- | --- | 423 | | events | String \| Array | The event name/s | 424 | | listener | function | The listener function | 425 | 426 | **Example** 427 | ```js 428 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 429 | const emitter = new EnhancedEventEmitter(); 430 | 431 | //add same listener to multiple events 432 | const remove = emitter.onAny(['test1', 'test2', 'test3'], function (arg1, arg2, arg3) { 433 | console.log(arg1, arg2, arg3); 434 | }); 435 | 436 | //remove listener from all events 437 | remove(); 438 | ``` 439 | 440 | 441 | ### EnhancedEventEmitter#addFilter([event], filter) ⇒ function 442 | Adds a filter that will be triggered before every emit for the provided event type (if no event is provided, than the filter is invoked for all events).
443 | The filter enables to prevent events from reaching the listeners in case some criteria is met. 444 | 445 | **Returns**: function - The remove filter function 446 | **Access**: public 447 | 448 | | Param | Type | Description | 449 | | --- | --- | --- | 450 | | [event] | String | The event name. If not provided, the filter is relevant for all events. | 451 | | filter | [FilterCallback](#FilterCallback) | The filter function | 452 | 453 | **Example** 454 | ```js 455 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 456 | const emitter = new EnhancedEventEmitter(); 457 | 458 | //add filters for test event only 459 | const removeTestEventFilter = emitter.filter('test', function (event, arg1, arg2) { 460 | if (arg1 && (arg1 > 3)) { 461 | return true; //continue with emit 462 | } 463 | 464 | return false; //prevent emit 465 | }); 466 | emitter.filter('test', function (event, arg1, arg2) { 467 | if (arg2 && (arg2 < 20)) { 468 | return true; //continue with emit 469 | } 470 | 471 | return false; //prevent emit 472 | }); 473 | 474 | //add global filter for all events 475 | emitter.filter(function (event, arg1, arg2) { 476 | if (arg1 && (arg1 > 5)) { 477 | return true; //continue with emit 478 | } 479 | 480 | return false; //prevent emit 481 | }); 482 | const removeGlobalArg2Filter = emitter.filter(function (event, arg1, arg2) { 483 | if (arg2 && (arg2 < 18)) { 484 | return true; //continue with emit 485 | } 486 | 487 | return false; //prevent emit 488 | }); 489 | 490 | emitter.on('test', function onTestEvent(arg1, arg2) { 491 | //event logic here... 492 | }); 493 | 494 | emitter.emit('test', 10, 15); 495 | 496 | //remove some filters 497 | removeTestEventFilter(); 498 | removeGlobalArg2Filter(); 499 | ``` 500 | 501 | 502 | ### EnhancedEventEmitter#addEventFilter(event, filter) ⇒ function 503 | Adds an event filter (See addFilter) 504 | 505 | **Returns**: function - The remove filter function 506 | **Access**: public 507 | 508 | | Param | Type | Description | 509 | | --- | --- | --- | 510 | | event | String | The event name. | 511 | | filter | [FilterCallback](#FilterCallback) | The filter function | 512 | 513 | 514 | 515 | ### EnhancedEventEmitter#addGlobalFilter(filter) ⇒ function 516 | Adds a global filter (See addFilter) 517 | 518 | **Returns**: function - The remove filter function 519 | **Access**: public 520 | 521 | | Param | Type | Description | 522 | | --- | --- | --- | 523 | | filter | [FilterCallback](#FilterCallback) | The filter function | 524 | 525 | 526 | 527 | ### EnhancedEventEmitter#filter([event], filter) ⇒ function 528 | See 'addFilter' documentation. 529 | 530 | **Returns**: function - The remove filter function 531 | **Access**: public 532 | 533 | | Param | Type | Description | 534 | | --- | --- | --- | 535 | | [event] | String | The event name. If not provided, the filter is relevant for all events. | 536 | | filter | [FilterCallback](#FilterCallback) | The filter function | 537 | 538 | 539 | 540 | ### EnhancedEventEmitter#proxyEvents(emitters, events) ⇒ function 541 | Will setup an event proxy so if any of the requested event/s are fired from the provided emitter/s, they will be triggered by this emitter. 542 | 543 | **Returns**: function - Once invoked, will stop proxying of events 544 | **Access**: public 545 | 546 | | Param | Type | Description | 547 | | --- | --- | --- | 548 | | emitters | Array.<Object> \| Object | An event emitter or array of event emitters to proxy the events from | 549 | | events | Array.<String> \| String | A single event name or an array of event names to proxy from the provided emitter/s | 550 | 551 | **Example** 552 | ```js 553 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 554 | const emitter = new EnhancedEventEmitter(); 555 | 556 | //proxy the 'data' and 'end' events from all sockets 557 | const stop = emitter.proxyEvents(sockets, ['data', 'end']); 558 | 559 | //listen to events via emitter 560 | emitter.on('data', onData); 561 | 562 | //stop events proxy 563 | stop(); 564 | ``` 565 | 566 | 567 | ### EnhancedEventEmitter#addNoop(event) ⇒ function 568 | Adds empty event handler. 569 | 570 | **Returns**: function - The remove listener function 571 | **Access**: public 572 | 573 | | Param | Type | Description | 574 | | --- | --- | --- | 575 | | event | String | The name of the event | 576 | 577 | **Example** 578 | ```js 579 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 580 | const emitter = new EnhancedEventEmitter(); 581 | 582 | //add noop even handler for the 'error' event 583 | const remove = emitter.addNoop('error'); 584 | 585 | //remove listener 586 | remove(); 587 | ``` 588 | 589 | 590 | ### EnhancedEventEmitter#ignoreError() 591 | Adds empty error event handler to prevent node.js from crashing in case of an error which we do not want/need to handle.
592 | This function will only add a new empty handler in case no other handler is defined for the error event. 593 | 594 | **Access**: public 595 | **Example** 596 | ```js 597 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 598 | const emitter = new EnhancedEventEmitter(); 599 | 600 | //adds empty error handler 601 | emitter.ignoreError(); 602 | 603 | //emit error will not crash the node.js process 604 | emitter.emit('error', new Error('test')); 605 | ``` 606 | 607 | 608 | ## EventEmitterEnhancer 609 | **Kind**: global class 610 | **Access**: public 611 | **Author**: Sagie Gur-Ari 612 | 613 | * [EventEmitterEnhancer](#EventEmitterEnhancer) 614 | * [new EventEmitterEnhancer()](#new_EventEmitterEnhancer_new) 615 | * [.EnhancedEventEmitter](#EventEmitterEnhancer.EnhancedEventEmitter) : EventEmitter 616 | * [#extend(EmitterType)](#EventEmitterEnhancer+extend) ⇒ Object 617 | * [#modify(EmitterType)](#EventEmitterEnhancer+modify) 618 | * [#modifyInstance(emitterInstance)](#EventEmitterEnhancer+modifyInstance) 619 | 620 | 621 | 622 | ### new EventEmitterEnhancer() 623 | This class enables to enhance event emitter prototypes and instances with extra capabilities. 624 | 625 | 626 | 627 | ### EventEmitterEnhancer.EnhancedEventEmitter : EventEmitter 628 | The node.js event emitter prototype extended with the extra capabilities. 629 | 630 | **Access**: public 631 | 632 | 633 | ### EventEmitterEnhancer#extend(EmitterType) ⇒ Object 634 | Extends the provided object prototype with the extended emitter capabilities.
635 | The provided object type must have an Node.js events.EventEmitter compatible interface. 636 | 637 | **Returns**: Object - The modified object type 638 | **Access**: public 639 | 640 | | Param | Type | Description | 641 | | --- | --- | --- | 642 | | EmitterType | Object | The object type | 643 | 644 | **Example** 645 | ```js 646 | //extend events.EventEmitter class (or any class that has the same interface) 647 | //now you can create instances of the new EnhancedEventEmitter type while events.EventEmitter is not modified/impacted in any way 648 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); //extend the event emitter class (can be Node.js of some custom event emitter). original base class is not affected. 649 | const emitter = new EnhancedEventEmitter(); //create a new instance using the new extended class type. 650 | ``` 651 | 652 | 653 | ### EventEmitterEnhancer#modify(EmitterType) 654 | Modified the provided object prototype with the extended emitter capabilities.
655 | The provided object type must have an Node.js events.EventEmitter compatible interface. 656 | 657 | **Access**: public 658 | 659 | | Param | Type | Description | 660 | | --- | --- | --- | 661 | | EmitterType | Object | The object type | 662 | 663 | **Example** 664 | ```js 665 | //modify the proto of an events.EventEmitter class (or any class that has the same interface) 666 | //now all existing and future instances of the original class are modified to include the new extended capabilities. 667 | EventEmitterEnhancer.modify(EventEmitter); //modify the event emitter class prototype (can be Node.js of some custom event emitter). existing instances are impacted. 668 | const emitter = new EventEmitter(); //create an instance of the original class and automatically get the new extended capabilities. 669 | ``` 670 | 671 | 672 | ### EventEmitterEnhancer#modifyInstance(emitterInstance) 673 | Modified the specific object instance with the extended emitter capabilities.
674 | The provided object type must have an Node.js events.EventEmitter compatible interface. 675 | 676 | **Access**: public 677 | 678 | | Param | Type | Description | 679 | | --- | --- | --- | 680 | | emitterInstance | Object | The emitter instance | 681 | 682 | **Example** 683 | ```js 684 | //modify specific instance to include the extended capabilities (other existing/future instances of that class type are not modified/impacted in any way). 685 | const emitter = new EventEmitter(); //create an instance of an event emitter (can be Node.js of some custom event emitter) 686 | EventEmitterEnhancer.modifyInstance(emitter); //modify the specific instance and add the extended capabilities. the original prototype is not affected. 687 | ``` 688 | 689 | 690 | ## EventEmitterEnhancer : object 691 | Extends the Node.js events.EventEmitter with extra capabilities. 692 | 693 | **Kind**: global namespace 694 | **Author**: Sagie Gur-Ari 695 | 696 | * [EventEmitterEnhancer](#EventEmitterEnhancer) : object 697 | * [new EventEmitterEnhancer()](#new_EventEmitterEnhancer_new) 698 | * [.EnhancedEventEmitter](#EventEmitterEnhancer.EnhancedEventEmitter) : EventEmitter 699 | * [#extend(EmitterType)](#EventEmitterEnhancer+extend) ⇒ Object 700 | * [#modify(EmitterType)](#EventEmitterEnhancer+modify) 701 | * [#modifyInstance(emitterInstance)](#EventEmitterEnhancer+modifyInstance) 702 | 703 | 704 | 705 | ### new EventEmitterEnhancer() 706 | This class enables to enhance event emitter prototypes and instances with extra capabilities. 707 | 708 | 709 | 710 | ### EventEmitterEnhancer.EnhancedEventEmitter : EventEmitter 711 | The node.js event emitter prototype extended with the extra capabilities. 712 | 713 | **Access**: public 714 | 715 | 716 | ### EventEmitterEnhancer#extend(EmitterType) ⇒ Object 717 | Extends the provided object prototype with the extended emitter capabilities.
718 | The provided object type must have an Node.js events.EventEmitter compatible interface. 719 | 720 | **Returns**: Object - The modified object type 721 | **Access**: public 722 | 723 | | Param | Type | Description | 724 | | --- | --- | --- | 725 | | EmitterType | Object | The object type | 726 | 727 | **Example** 728 | ```js 729 | //extend events.EventEmitter class (or any class that has the same interface) 730 | //now you can create instances of the new EnhancedEventEmitter type while events.EventEmitter is not modified/impacted in any way 731 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); //extend the event emitter class (can be Node.js of some custom event emitter). original base class is not affected. 732 | const emitter = new EnhancedEventEmitter(); //create a new instance using the new extended class type. 733 | ``` 734 | 735 | 736 | ### EventEmitterEnhancer#modify(EmitterType) 737 | Modified the provided object prototype with the extended emitter capabilities.
738 | The provided object type must have an Node.js events.EventEmitter compatible interface. 739 | 740 | **Access**: public 741 | 742 | | Param | Type | Description | 743 | | --- | --- | --- | 744 | | EmitterType | Object | The object type | 745 | 746 | **Example** 747 | ```js 748 | //modify the proto of an events.EventEmitter class (or any class that has the same interface) 749 | //now all existing and future instances of the original class are modified to include the new extended capabilities. 750 | EventEmitterEnhancer.modify(EventEmitter); //modify the event emitter class prototype (can be Node.js of some custom event emitter). existing instances are impacted. 751 | const emitter = new EventEmitter(); //create an instance of the original class and automatically get the new extended capabilities. 752 | ``` 753 | 754 | 755 | ### EventEmitterEnhancer#modifyInstance(emitterInstance) 756 | Modified the specific object instance with the extended emitter capabilities.
757 | The provided object type must have an Node.js events.EventEmitter compatible interface. 758 | 759 | **Access**: public 760 | 761 | | Param | Type | Description | 762 | | --- | --- | --- | 763 | | emitterInstance | Object | The emitter instance | 764 | 765 | **Example** 766 | ```js 767 | //modify specific instance to include the extended capabilities (other existing/future instances of that class type are not modified/impacted in any way). 768 | const emitter = new EventEmitter(); //create an instance of an event emitter (can be Node.js of some custom event emitter) 769 | EventEmitterEnhancer.modifyInstance(emitter); //modify the specific instance and add the extended capabilities. the original prototype is not affected. 770 | ``` 771 | 772 | 773 | ## FilterCallback ⇒ Boolean 774 | 'filter' callback. 775 | 776 | **Kind**: global typedef 777 | **Returns**: Boolean - True to continue with the emit, false to prevent emit 778 | 779 | | Param | Type | Description | 780 | | --- | --- | --- | 781 | | type | String | The event type | 782 | | [params] | \* | The event parameters | 783 | 784 | 785 | 786 | ## ElseCallback : function 787 | 'else' callback. 788 | 789 | **Kind**: global typedef 790 | 791 | | Param | Type | Description | 792 | | --- | --- | --- | 793 | | type | String | The event type | 794 | | [params] | \* | The event parameters | 795 | 796 | 797 | 798 | ## AsyncEmitCallback : function 799 | 'async-emit' callback. 800 | 801 | **Kind**: global typedef 802 | 803 | | Param | Type | Description | 804 | | --- | --- | --- | 805 | | type | String | The event type | 806 | | [params] | \* | The event parameters | 807 | | emitted | Boolean | True if emitted, else false | 808 | 809 | -------------------------------------------------------------------------------- /lib/enhanced-event-emitter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const funcs = require('funcs-js'); 4 | const later = require('node-later'); 5 | const defer = later(); 6 | 7 | /** 8 | * Extends the Node.js events.EventEmitter with extra capabilities. 9 | * 10 | * @author Sagie Gur-Ari 11 | * @namespace EventEmitterEnhancer 12 | */ 13 | 14 | /** 15 | * 'filter' callback. 16 | * 17 | * @callback FilterCallback 18 | * @param {String} type - The event type 19 | * @param {*} [params] - The event parameters 20 | * @returns {Boolean} True to continue with the emit, false to prevent emit 21 | */ 22 | 23 | /** 24 | * 'else' callback. 25 | * 26 | * @callback ElseCallback 27 | * @param {String} type - The event type 28 | * @param {*} [params] - The event parameters 29 | */ 30 | 31 | /** 32 | * 'async-emit' callback. 33 | * 34 | * @callback AsyncEmitCallback 35 | * @param {String} type - The event type 36 | * @param {*} [params] - The event parameters 37 | * @param {Boolean} emitted - True if emitted, else false 38 | */ 39 | 40 | /*jslint debug: true */ 41 | /*istanbul ignore next*/ 42 | /** 43 | * This class holds all the extended capabilities added to any emitter. 44 | * 45 | * @author Sagie Gur-Ari 46 | * @class EnhancedEventEmitter 47 | * @public 48 | */ 49 | function EnhancedEventEmitter() { 50 | //should not be called 51 | } 52 | /*jslint debug: false */ 53 | 54 | /** 55 | * Marker attribute to prevent multiple wrapping of emitter. 56 | * 57 | * @member {Boolean} 58 | * @alias EnhancedEventEmitter.enhancedEmitterType 59 | * @memberof! EnhancedEventEmitter 60 | * @private 61 | */ 62 | EnhancedEventEmitter.prototype.enhancedEmitterType = true; 63 | 64 | /** 65 | * If true, all events will not trigger any listener (or 'else' listener).
66 | * The emit function will simply do nothing. 67 | * 68 | * @member {Boolean} 69 | * @alias EnhancedEventEmitter.suspended 70 | * @memberof! EnhancedEventEmitter 71 | * @public 72 | */ 73 | EnhancedEventEmitter.prototype.suspended = false; 74 | 75 | /** 76 | * If defined, events will be splitted by this separator and emitted as partials.
77 | * For example, if the separator is ':' an event event1:event2:event3 will be emitted as 3 events: event1, event1:event2, event1:event2:event3. 78 | * 79 | * @member {String} 80 | * @alias EnhancedEventEmitter.subscriptionSeparator 81 | * @memberof! EnhancedEventEmitter 82 | * @public 83 | */ 84 | EnhancedEventEmitter.prototype.subscriptionSeparator = null; 85 | 86 | /*eslint-disable valid-jsdoc*/ 87 | //jscs:disable jsDoc 88 | /** 89 | * See node.js events.EventEmitter.on.
90 | * This function also returns a removeListener function to easily remove the provided listener. 91 | * 92 | * @function 93 | * @memberof! EnhancedEventEmitter 94 | * @public 95 | * @param {String} event - The name of the event 96 | * @param {function} listener - The callback function 97 | * @returns {function} The remove listener function 98 | * @example 99 | * ```js 100 | * const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 101 | * const emitter = new EnhancedEventEmitter(); 102 | * 103 | * const remove = emitter.on('error', function (error) { 104 | * console.error(error); 105 | * }); 106 | * 107 | * //remove listener (no longer need to keep a reference to the listener function) 108 | * remove(); 109 | * ``` 110 | * 111 | * @also 112 | * 113 | * Enables more complex on capabilities including providing multiple listeners/event names, timeout the listener and more.
114 | * To remove the listener/s, the returned function must be called instead of doing emitter.removeListener(...) 115 | * 116 | * @function 117 | * @memberof! EnhancedEventEmitter 118 | * @public 119 | * @param {Object} options - All options needed to setup the listeners 120 | * @param {String[]|String} options.event - The event name or an array of event names 121 | * @param {function[]|function} options.listener - The callback function or an array of callback functions 122 | * @param {Boolean} [options.async=false] - If true, the callback functions will be called after next tick 123 | * @param {Number} [options.timeout] - If provided, the returned remove listener function will be called after the provided timeout value in millies (unless called manually before the timeout was triggered) 124 | * @returns {function} The remove listener function 125 | * @example 126 | * ```js 127 | * const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 128 | * const emitter = new EnhancedEventEmitter(); 129 | * 130 | * const removeListener = emitter.on({ 131 | * event: ['error', 'connection-error', 'write-error', 'read-error'], //The event names (can be string for a single event) 132 | * listener: [ //The listener callback functions (can be a function instead of an array for a single listener callback) 133 | * function firstListener(arg1, arg2) { 134 | * //do something 135 | * }, 136 | * function secondListener(arg1, arg2) { 137 | * //do something 138 | * } 139 | * ], 140 | * async: true, //The callback functions will be called after next tick 141 | * timeout: 1500 //All listeners will be removed after the provided timeout (if not provided, listeners can only be removed manually via returned function) 142 | * }); 143 | * 144 | * //emit any event 145 | * emitter.emit('write-error', 1, 2, 3); 146 | * 147 | * //once done, remove all listeners from all events 148 | * removeListener(); 149 | * ``` 150 | */ 151 | EnhancedEventEmitter.prototype.on = function (eventOrOptions, listener) { 152 | const self = this; 153 | let removeListener; 154 | 155 | if (eventOrOptions && (!listener) && (typeof eventOrOptions === 'object') && eventOrOptions.listener && eventOrOptions.event) { 156 | let listeners = eventOrOptions.listener; 157 | if (typeof listeners === 'function') { 158 | listeners = [ 159 | listeners 160 | ]; 161 | } 162 | 163 | const events = eventOrOptions.event; 164 | 165 | const asyncOn = eventOrOptions.async; 166 | 167 | const timeout = eventOrOptions.timeout || 0; 168 | let timeoutID; 169 | 170 | const getListener = function (eventListener) { 171 | let onEvent = eventListener; 172 | 173 | if (asyncOn) { 174 | onEvent = self.asAsyncListener(eventListener); 175 | } 176 | 177 | return onEvent; 178 | }; 179 | 180 | let removeFunction; 181 | const removeFunctions = []; 182 | for (let index = 0; index < listeners.length; index++) { 183 | listener = listeners[index]; 184 | 185 | listener = getListener(listener); 186 | 187 | removeFunction = self.onAny(events, listener); 188 | 189 | removeFunctions.push(removeFunction); 190 | } 191 | 192 | removeListener = function () { 193 | for (let funcIndex = 0; funcIndex < removeFunctions.length; funcIndex++) { 194 | removeFunctions[funcIndex](); 195 | } 196 | 197 | if (timeoutID) { 198 | clearTimeout(timeoutID); 199 | timeoutID = null; 200 | } 201 | }; 202 | 203 | if (timeout && (timeout > 0)) { 204 | timeoutID = setTimeout(removeListener, timeout); 205 | } 206 | } else { 207 | this.baseOn(eventOrOptions, listener); 208 | 209 | removeListener = function () { 210 | self.removeListener(eventOrOptions, listener); 211 | }; 212 | } 213 | 214 | removeListener = funcs.once(removeListener); 215 | 216 | return removeListener; 217 | }; 218 | //jscs:enable jsDoc 219 | /*eslint-enable valid-jsdoc*/ 220 | 221 | /** 222 | * See node.js events.EventEmitter.once.
223 | * This function also returns a removeListener function to easily remove the provided listener. 224 | * 225 | * @function 226 | * @memberof! EnhancedEventEmitter 227 | * @public 228 | * @param {String} event - The name of the event 229 | * @param {function} listener - The callback function 230 | * @returns {function} The remove listener function 231 | * @example 232 | * ```js 233 | * const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 234 | * const emitter = new EnhancedEventEmitter(); 235 | * 236 | * const remove = emitter.once('error', function (error) { 237 | * console.error(error); 238 | * }); 239 | * 240 | * //remove listener (no longer need to keep a reference to the listener function) 241 | * remove(); 242 | * ``` 243 | */ 244 | EnhancedEventEmitter.prototype.once = function (event, listener) { 245 | const self = this; 246 | 247 | this.baseOnce(event, listener); 248 | 249 | return funcs.once(function removeListener() { 250 | self.removeListener(event, listener); 251 | }); 252 | }; 253 | 254 | /** 255 | * See node.js events.EventEmitter.removeAllListeners.
256 | * This function is modified to also accept an array of event names. 257 | * 258 | * @function 259 | * @memberof! EnhancedEventEmitter 260 | * @public 261 | * @param {String|Array} [event] - The name/s of the event 262 | * @example 263 | * ```js 264 | * const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 265 | * const emitter = new EnhancedEventEmitter(); 266 | * 267 | * //same as the basic removeAllListeners 268 | * emitter.removeAllListeners('my-event'); 269 | * 270 | * //also supports array of event names 271 | * emitter.removeAllListeners(['my-event', 'another-event']); 272 | * ``` 273 | */ 274 | EnhancedEventEmitter.prototype.removeAllListeners = function (event) { 275 | if (event) { 276 | if (Array.isArray(event)) { 277 | for (let index = 0; index < event.length; index++) { 278 | this.removeAllListeners(event[index]); 279 | } 280 | } else { 281 | this.baseRemoveAllListeners(event); 282 | } 283 | } 284 | }; 285 | 286 | /** 287 | * Suspends all emit calls for the provided event name (including 'else' listeners).
288 | * For suspended events, the emit function will simply do nothing ('else' listeners won't be invoked either). 289 | * 290 | * @function 291 | * @memberof! EnhancedEventEmitter 292 | * @public 293 | * @param {String} event - The event to suspend 294 | * @example 295 | * ```js 296 | * const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 297 | * const emitter = new EnhancedEventEmitter(); 298 | * emitter.on('test', function () { 299 | * //will never be called 300 | * }); 301 | * 302 | * emitter.suspended = true; //suspend ALL events (to unsuspend use emitter.suspended = false;) 303 | * //or 304 | * emitter.suspend('test'); //suspend only 'test' event (to unsuspend use emitter.unsuspend('test');) 305 | * 306 | * emitter.emit('test'); 307 | * ``` 308 | */ 309 | EnhancedEventEmitter.prototype.suspend = function (event) { 310 | this.suspendedEvents = this.markEvent(event, this.suspendedEvents); 311 | }; 312 | 313 | /** 314 | * Unsuspends the emit calls for the provided event name. 315 | * 316 | * @function 317 | * @memberof! EnhancedEventEmitter 318 | * @public 319 | * @param {String} event - The event to unsuspend 320 | */ 321 | EnhancedEventEmitter.prototype.unsuspend = function (event) { 322 | if (event && this.suspendedEvents) { 323 | delete this.suspendedEvents[event]; 324 | } 325 | }; 326 | 327 | /** 328 | * Adds an 'else' listener which will be triggered by all events that do not have a listener currently for them (apart of the special 'error' event). 329 | * 330 | * @function 331 | * @memberof! EnhancedEventEmitter 332 | * @public 333 | * @param {ElseCallback} listener - The listener that will catch all 'else' events 334 | * @example 335 | * ```js 336 | * const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 337 | * const emitter = new EnhancedEventEmitter(); 338 | * emitter.else(function onUnhandledEvent(event, arg1, arg2) { 339 | * //logic here.... 340 | * 341 | * //to remove 'else' listeners, simply use the unelse function 342 | * emitter.unelse(this); 343 | * }); 344 | * 345 | * emitter.emit('test', 1, 2); 346 | * ``` 347 | */ 348 | EnhancedEventEmitter.prototype.else = function (listener) { 349 | this.elseListeners = this.elseListeners || []; 350 | this.elseListeners.push(listener); 351 | }; 352 | 353 | /** 354 | * Removes the provided 'else' listener.
355 | * Same as 'unelse' function. 356 | * 357 | * @function 358 | * @memberof! EnhancedEventEmitter 359 | * @public 360 | * @param {ElseCallback} listener - The listener to remove 361 | */ 362 | EnhancedEventEmitter.prototype.removeElseListener = function (listener) { 363 | if (listener && this.elseListeners && this.elseListeners.length) { 364 | let index; 365 | do { 366 | index = this.elseListeners.indexOf(listener); 367 | if (index !== -1) { 368 | this.elseListeners.splice(index, 1); 369 | } 370 | } while (index !== -1); 371 | } 372 | }; 373 | 374 | /** 375 | * See 'removeElseListener' documentation. 376 | * 377 | * @function 378 | * @memberof! EnhancedEventEmitter 379 | * @public 380 | * @param {ElseCallback} listener - The listener to remove 381 | */ 382 | EnhancedEventEmitter.prototype.unelse = EnhancedEventEmitter.prototype.removeElseListener; //shorter function name 383 | 384 | /** 385 | * Removes all 'else' listeners. 386 | * 387 | * @function 388 | * @memberof! EnhancedEventEmitter 389 | * @public 390 | */ 391 | EnhancedEventEmitter.prototype.removeAllElseListeners = function () { 392 | if (this.elseListeners && this.elseListeners.length) { 393 | this.elseListeners.splice(0, this.elseListeners.length); 394 | } 395 | }; 396 | 397 | /** 398 | * In case an event with the provided name is emitted but no listener is attached to it, an error event will emitted by this emitter instance instead. 399 | * 400 | * @function 401 | * @memberof! EnhancedEventEmitter 402 | * @public 403 | * @param {String} event - The event name 404 | * @example 405 | * ```js 406 | * const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 407 | * const emitter = new EnhancedEventEmitter(); 408 | * emitter.on('error', function (error) { 409 | * //logic here... 410 | * 411 | * //To remove elseError 412 | * emitter.unelseError('test'); 413 | * }); 414 | * 415 | * emitter.elseError('test'); 416 | * 417 | * emitter.emit('test'); 418 | * ``` 419 | */ 420 | EnhancedEventEmitter.prototype.elseError = function (event) { 421 | this.elseErrorEvents = this.markEvent(event, this.elseErrorEvents); 422 | }; 423 | 424 | /** 425 | * Removes the else-error handler for the provided event.
426 | * Same as 'unelseError' function. 427 | * 428 | * @function 429 | * @memberof! EnhancedEventEmitter 430 | * @public 431 | * @param {String} event - The event name 432 | */ 433 | EnhancedEventEmitter.prototype.removeElseError = function (event) { 434 | if (event && this.elseErrorEvents) { 435 | delete this.elseErrorEvents[event]; 436 | } 437 | }; 438 | 439 | /** 440 | * See 'removeElseError' documentation. 441 | * 442 | * @function 443 | * @memberof! EnhancedEventEmitter 444 | * @public 445 | * @param {String} event - The event name 446 | */ 447 | EnhancedEventEmitter.prototype.unelseError = EnhancedEventEmitter.prototype.removeElseError; //shorter function name 448 | 449 | /** 450 | * See Node.js events.EventEmitter documentation. 451 | * 452 | * @function 453 | * @memberof! EnhancedEventEmitter 454 | * @public 455 | * @param {String} event - The event name 456 | * @param {*} [params] - The event parameters 457 | * @returns {Boolean} True if a listener or an 'else' listener handled the event 458 | */ 459 | EnhancedEventEmitter.prototype.emit = function () { 460 | if (this.subscriptionSeparator) { 461 | return this.doEmitByPath.apply(this, arguments); 462 | } 463 | 464 | return this.doEmit.apply(this, arguments); 465 | }; 466 | 467 | /** 468 | * Runs the emit flow for all event paths splitted by the subscriptionSeparator value. 469 | * 470 | * @function 471 | * @memberof! EnhancedEventEmitter 472 | * @private 473 | * @param {String} event - The event name 474 | * @param {*} [params] - The event parameters 475 | * @returns {Boolean} True if a listener or an 'else' listener handled the event 476 | */ 477 | EnhancedEventEmitter.prototype.doEmitByPath = function (event) { 478 | if (!this.subscriptionSeparator) { 479 | return false; 480 | } 481 | 482 | const events = event.split(this.subscriptionSeparator); 483 | let handled = false; 484 | let eventName; 485 | for (let index = 0; index < events.length; index++) { 486 | const argumentsArray = Array.prototype.slice.call(arguments, 0); 487 | if (eventName) { 488 | eventName = eventName + this.subscriptionSeparator + events[index]; 489 | } else { 490 | eventName = events[index]; 491 | } 492 | 493 | argumentsArray[0] = eventName; 494 | 495 | handled = this.doEmit.apply(this, argumentsArray) || handled; 496 | } 497 | 498 | return handled; 499 | }; 500 | 501 | /** 502 | * Runs the emit flow only for the given event. 503 | * 504 | * @function 505 | * @memberof! EnhancedEventEmitter 506 | * @private 507 | * @param {String} event - The event name 508 | * @param {*} [params] - The event parameters 509 | * @returns {Boolean} True if a listener or an 'else' listener handled the event 510 | */ 511 | EnhancedEventEmitter.prototype.doEmit = function (event) { 512 | let emitted = false; 513 | if ((!this.suspended) && ((!this.suspendedEvents) || (!this.suspendedEvents[event]))) { 514 | const emitArguments = arguments; 515 | 516 | if (this.runFilterChain(emitArguments)) { 517 | emitted = this.baseEmit.apply(this, emitArguments); 518 | if (!emitted) { 519 | emitted = this.handleNoEmit(event, emitArguments); 520 | } 521 | } 522 | } 523 | 524 | return emitted; 525 | }; 526 | 527 | /** 528 | * Handles events which had no listeners. 529 | * 530 | * @function 531 | * @memberof! EnhancedEventEmitter 532 | * @private 533 | * @param {String} event - The event name 534 | * @param {Array} eventArguments - All the arguments to send the else callbacks 535 | * @returns {Boolean} True if a listener or an 'else' listener handled the event 536 | */ 537 | EnhancedEventEmitter.prototype.handleNoEmit = function (event, eventArguments) { 538 | let emitted = false; 539 | const elseErrorDefined = (this.elseErrorEvents && this.elseErrorEvents[event]); 540 | if ((this.elseListeners && this.elseListeners.length) || elseErrorDefined) { 541 | if (elseErrorDefined) { 542 | this.emit('error', new Error('No listener attached for event: ' + event)); 543 | } else { 544 | this.invokeElseListener(eventArguments); 545 | emitted = true; 546 | } 547 | } 548 | 549 | return emitted; 550 | }; 551 | 552 | /** 553 | * Invokes all of the 'else' listeners. 554 | * 555 | * @function 556 | * @memberof! EnhancedEventEmitter 557 | * @private 558 | * @param {Array} eventArguments - All the arguments to send the else callbacks 559 | */ 560 | EnhancedEventEmitter.prototype.invokeElseListener = function (eventArguments) { 561 | for (let index = 0; index < this.elseListeners.length; index++) { 562 | this.elseListeners[index].apply(this.elseListeners[index], eventArguments); 563 | } 564 | }; 565 | 566 | /** 567 | * Invokes the emit after a timeout to enable calling flow to continue and not block due to event listeners. 568 | * 569 | * @function 570 | * @memberof! EnhancedEventEmitter 571 | * @public 572 | * @param {String} event - The event name 573 | * @param {*} [params] - The event parameters (if last param is a function, it is considered as the callback of the emitAsync) 574 | * @param {AsyncEmitCallback} [callback] - The async callback 575 | * @example 576 | * ```js 577 | * const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 578 | * const emitter = new EnhancedEventEmitter(); 579 | * emitter.on('test', function onTestEvent(num1, num2) { 580 | * //event logic here 581 | * }); 582 | * 583 | * emitter.emitAsync('test', 1, 2, function onEmitDone(event, num1, num2, emitted) { 584 | * //emit callback logic 585 | * }); 586 | * ``` 587 | */ 588 | EnhancedEventEmitter.prototype.emitAsync = function (event) { 589 | if ((!event) || (typeof event !== 'string')) { 590 | throw new Error('Event not provided.'); 591 | } 592 | 593 | const self = this; 594 | 595 | let callback; 596 | const argumentsArray = Array.prototype.splice.call(arguments, 0); 597 | if ((argumentsArray.length > 1) && (typeof argumentsArray[argumentsArray.length - 1] === 'function')) { 598 | callback = argumentsArray.pop(); 599 | } 600 | 601 | defer(function invokeEmit() { 602 | const emitted = self.emit.apply(self, argumentsArray); 603 | 604 | if (callback) { 605 | argumentsArray.push(emitted); 606 | callback.apply(callback, argumentsArray); 607 | } 608 | }); 609 | }; 610 | 611 | /** 612 | * Adds a listener that will be triggered after a timeout during an emit.
613 | * This ensures that the provided listener is invoked after all other listeners and that it will not block the emit caller flow.
614 | * To remove the listener, the returned function must be called instead of doing emitter.removeListener(...) 615 | * 616 | * @function 617 | * @memberof! EnhancedEventEmitter 618 | * @public 619 | * @param {String} event - The event name 620 | * @param {function} listener - The listener function 621 | * @returns {function} The remove listener function 622 | * @example 623 | * ```js 624 | * const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 625 | * const emitter = new EnhancedEventEmitter(); 626 | * emitter.on('test', function onEventSync() { 627 | * //sync handle function logic 628 | * }); 629 | * const removeListener = emitter.onAsync('test', function onEventAsync() { 630 | * //async handle function logic 631 | * }); 632 | * 633 | * emitter.emit('test', 1, 2); 634 | * 635 | * //remove the async listener 636 | * removeListener(); 637 | * ``` 638 | */ 639 | EnhancedEventEmitter.prototype.onAsync = function (event, listener) { 640 | let removeAsyncListener = null; 641 | if (event && listener && (typeof listener === 'function')) { 642 | const self = this; 643 | 644 | const onEvent = self.asAsyncListener(listener); 645 | 646 | self.on(event, onEvent); 647 | 648 | removeAsyncListener = funcs.once(function removeListener() { 649 | self.removeListener(event, onEvent); 650 | }); 651 | } else { 652 | throw new Error('Missing mandatory parameters'); 653 | } 654 | 655 | return removeAsyncListener; 656 | }; 657 | 658 | /** 659 | * Adds a listener to all provided event names.
660 | * To remove the listener, the returned function must be called instead of doing emitter.removeListener(...) 661 | * 662 | * @function 663 | * @memberof! EnhancedEventEmitter 664 | * @public 665 | * @param {String|Array} events - The event name/s 666 | * @param {function} listener - The listener function 667 | * @returns {function} The remove listener function 668 | * @example 669 | * ```js 670 | * const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 671 | * const emitter = new EnhancedEventEmitter(); 672 | * 673 | * //add same listener to multiple events 674 | * const remove = emitter.onAny(['test1', 'test2', 'test3'], function (arg1, arg2, arg3) { 675 | * console.log(arg1, arg2, arg3); 676 | * }); 677 | * 678 | * //remove listener from all events 679 | * remove(); 680 | * ``` 681 | */ 682 | EnhancedEventEmitter.prototype.onAny = function (events, listener) { 683 | const self = this; 684 | 685 | let removeListener = null; 686 | if (events && listener && (typeof listener === 'function')) { 687 | if (Array.isArray(events)) { 688 | for (let index = 0; index < events.length; index++) { 689 | self.on(events[index], listener); 690 | } 691 | 692 | removeListener = function () { 693 | for (let index = 0; index < events.length; index++) { 694 | self.removeListener(events[index], listener); 695 | } 696 | }; 697 | } else { 698 | self.on(events, listener); 699 | 700 | removeListener = function () { 701 | self.removeListener(events, listener); 702 | }; 703 | } 704 | } else { 705 | throw new Error('Missing mandatory parameters'); 706 | } 707 | 708 | removeListener = funcs.once(removeListener); 709 | 710 | return removeListener; 711 | }; 712 | 713 | /** 714 | * Adds a filter that will be triggered before every emit for the provided event type (if no event is provided, than the filter is invoked for all events).
715 | * The filter enables to prevent events from reaching the listeners in case some criteria is met. 716 | * 717 | * @function 718 | * @memberof! EnhancedEventEmitter 719 | * @public 720 | * @param {String} [event] - The event name. If not provided, the filter is relevant for all events. 721 | * @param {FilterCallback} filter - The filter function 722 | * @returns {function} The remove filter function 723 | * @example 724 | * ```js 725 | * const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 726 | * const emitter = new EnhancedEventEmitter(); 727 | * 728 | * //add filters for test event only 729 | * const removeTestEventFilter = emitter.filter('test', function (event, arg1, arg2) { 730 | * if (arg1 && (arg1 > 3)) { 731 | * return true; //continue with emit 732 | * } 733 | * 734 | * return false; //prevent emit 735 | * }); 736 | * emitter.filter('test', function (event, arg1, arg2) { 737 | * if (arg2 && (arg2 < 20)) { 738 | * return true; //continue with emit 739 | * } 740 | * 741 | * return false; //prevent emit 742 | * }); 743 | * 744 | * //add global filter for all events 745 | * emitter.filter(function (event, arg1, arg2) { 746 | * if (arg1 && (arg1 > 5)) { 747 | * return true; //continue with emit 748 | * } 749 | * 750 | * return false; //prevent emit 751 | * }); 752 | * const removeGlobalArg2Filter = emitter.filter(function (event, arg1, arg2) { 753 | * if (arg2 && (arg2 < 18)) { 754 | * return true; //continue with emit 755 | * } 756 | * 757 | * return false; //prevent emit 758 | * }); 759 | * 760 | * emitter.on('test', function onTestEvent(arg1, arg2) { 761 | * //event logic here... 762 | * }); 763 | * 764 | * emitter.emit('test', 10, 15); 765 | * 766 | * //remove some filters 767 | * removeTestEventFilter(); 768 | * removeGlobalArg2Filter(); 769 | * ``` 770 | */ 771 | EnhancedEventEmitter.prototype.addFilter = function (event, filter) { 772 | const self = this; 773 | let removeFunction = null; 774 | 775 | if ((!filter) && event && (typeof event === 'function')) { 776 | filter = event; 777 | event = null; 778 | } 779 | 780 | if (!filter) { 781 | throw new Error('Filter not provided.'); 782 | } 783 | 784 | if (typeof filter !== 'function') { 785 | throw new Error('Filter is not a function.'); 786 | } 787 | 788 | self.filters = self.filters || {}; 789 | if (event) { 790 | removeFunction = self.addEventFilter(event, filter); 791 | } else { 792 | removeFunction = self.addGlobalFilter(filter); 793 | } 794 | 795 | return removeFunction; 796 | }; 797 | 798 | /** 799 | * Adds an event filter (See addFilter) 800 | * 801 | * @function 802 | * @memberof! EnhancedEventEmitter 803 | * @public 804 | * @param {String} event - The event name. 805 | * @param {FilterCallback} filter - The filter function 806 | * @returns {function} The remove filter function 807 | */ 808 | EnhancedEventEmitter.prototype.addEventFilter = function (event, filter) { 809 | const self = this; 810 | 811 | self.filters.event = self.filters.event || {}; 812 | self.filters.event[event] = self.filters.event[event] || []; 813 | self.filters.event[event].push(filter); 814 | 815 | return funcs.once(function removeEventFilter() { 816 | if (self.filters && self.filters.event && self.filters.event[event] && self.filters.event[event].length) { 817 | const index = self.filters.event[event].indexOf(filter); 818 | 819 | if (index !== -1) { 820 | self.filters.event[event].splice(index, 1); 821 | } 822 | } 823 | }); 824 | }; 825 | 826 | /** 827 | * Adds a global filter (See addFilter) 828 | * 829 | * @function 830 | * @memberof! EnhancedEventEmitter 831 | * @public 832 | * @param {FilterCallback} filter - The filter function 833 | * @returns {function} The remove filter function 834 | */ 835 | EnhancedEventEmitter.prototype.addGlobalFilter = function (filter) { 836 | const self = this; 837 | 838 | self.filters.global = self.filters.global || []; 839 | self.filters.global.push(filter); 840 | 841 | return funcs.once(function removeGlobalFilter() { 842 | if (self.filters && self.filters.global && self.filters.global.length) { 843 | const index = self.filters.global.indexOf(filter); 844 | 845 | if (index !== -1) { 846 | self.filters.global.splice(index, 1); 847 | } 848 | } 849 | }); 850 | }; 851 | 852 | /** 853 | * See 'addFilter' documentation. 854 | * 855 | * @function 856 | * @memberof! EnhancedEventEmitter 857 | * @public 858 | * @param {String} [event] - The event name. If not provided, the filter is relevant for all events. 859 | * @param {FilterCallback} filter - The filter function 860 | * @returns {function} The remove filter function 861 | */ 862 | EnhancedEventEmitter.prototype.filter = EnhancedEventEmitter.prototype.addFilter; //shorter function name 863 | 864 | /** 865 | * Will setup an event proxy so if any of the requested event/s are fired from the provided emitter/s, they will be triggered by this emitter. 866 | * 867 | * @function 868 | * @memberof! EnhancedEventEmitter 869 | * @public 870 | * @param {Object[]|Object} emitters - An event emitter or array of event emitters to proxy the events from 871 | * @param {String[]|String} events - A single event name or an array of event names to proxy from the provided emitter/s 872 | * @returns {function} Once invoked, will stop proxying of events 873 | * @example 874 | * ```js 875 | * const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 876 | * const emitter = new EnhancedEventEmitter(); 877 | * 878 | * //proxy the 'data' and 'end' events from all sockets 879 | * const stop = emitter.proxyEvents(sockets, ['data', 'end']); 880 | * 881 | * //listen to events via emitter 882 | * emitter.on('data', onData); 883 | * 884 | * //stop events proxy 885 | * stop(); 886 | * ``` 887 | */ 888 | EnhancedEventEmitter.prototype.proxyEvents = function (emitters, events) { 889 | if (!emitters) { 890 | throw new Error('Event Emitter/s not provided.'); 891 | } 892 | 893 | if (!events) { 894 | throw new Error('Event/s not provided.'); 895 | } 896 | 897 | const self = this; 898 | 899 | if (!Array.isArray(emitters)) { 900 | emitters = [ 901 | emitters 902 | ]; 903 | } 904 | 905 | if (typeof events === 'string') { 906 | events = [ 907 | events 908 | ]; 909 | } 910 | 911 | let releaseFuncs = []; 912 | events.forEach(function createProxy(event) { 913 | emitters.forEach(function onClientEvent(emitter) { 914 | const onEvent = function onEvent() { 915 | //get event arguments 916 | const argumentsArray = Array.prototype.slice.call(arguments, 0); 917 | 918 | //add event name to list 919 | argumentsArray.unshift(event); 920 | 921 | //emit event 922 | self.emit.apply(self, argumentsArray); 923 | }; 924 | 925 | emitter.on(event, onEvent); 926 | 927 | releaseFuncs.push(function stop() { 928 | emitter.removeListener(event, onEvent); 929 | }); 930 | }); 931 | }); 932 | 933 | return function stopProxy() { 934 | if (releaseFuncs && releaseFuncs.length) { 935 | releaseFuncs.forEach(function invokeStop(release) { 936 | release(); 937 | }); 938 | 939 | releaseFuncs = null; 940 | } 941 | }; 942 | }; 943 | 944 | /** 945 | * Adds empty event handler. 946 | * 947 | * @function 948 | * @memberof! EnhancedEventEmitter 949 | * @public 950 | * @param {String} event - The name of the event 951 | * @returns {function} The remove listener function 952 | * @example 953 | * ```js 954 | * const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 955 | * const emitter = new EnhancedEventEmitter(); 956 | * 957 | * //add noop even handler for the 'error' event 958 | * const remove = emitter.addNoop('error'); 959 | * 960 | * //remove listener 961 | * remove(); 962 | * ``` 963 | */ 964 | EnhancedEventEmitter.prototype.addNoop = function (event) { 965 | let remove; 966 | 967 | if (event && (typeof event === 'string')) { 968 | remove = this.on(event, funcs.noop); 969 | } 970 | 971 | return remove; 972 | }; 973 | 974 | /** 975 | * Adds empty error event handler to prevent node.js from crashing in case of an error which we do not want/need to handle.
976 | * This function will only add a new empty handler in case no other handler is defined for the error event. 977 | * 978 | * @function 979 | * @memberof! EnhancedEventEmitter 980 | * @public 981 | * @example 982 | * ```js 983 | * const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 984 | * const emitter = new EnhancedEventEmitter(); 985 | * 986 | * //adds empty error handler 987 | * emitter.ignoreError(); 988 | * 989 | * //emit error will not crash the node.js process 990 | * emitter.emit('error', new Error('test')); 991 | * ``` 992 | */ 993 | EnhancedEventEmitter.prototype.ignoreError = function () { 994 | if (!this.listenerCount('error')) { 995 | this.addNoop('error'); 996 | } 997 | }; 998 | 999 | /** 1000 | * Converts the provided listener function to be async. 1001 | * 1002 | * @function 1003 | * @memberof! EnhancedEventEmitter 1004 | * @private 1005 | * @param {function} listener - The listener function 1006 | * @returns {function} The async listener 1007 | * @example 1008 | * ```js 1009 | * const asyncListner = emitter.asAsyncListener(listner); 1010 | * emitter.on('my_event', asyncListner); 1011 | * ``` 1012 | */ 1013 | EnhancedEventEmitter.prototype.asAsyncListener = function (listener) { 1014 | let onEvent; 1015 | if (listener && (typeof listener === 'function')) { 1016 | onEvent = function () { 1017 | const argumentsArray = arguments; 1018 | 1019 | defer(function invokeListener() { 1020 | listener.apply(listener, argumentsArray); 1021 | }); 1022 | }; 1023 | } else { 1024 | throw new Error('Missing mandatory parameters'); 1025 | } 1026 | 1027 | return onEvent; 1028 | }; 1029 | 1030 | /** 1031 | * Returns true if to allow to emit the event based on the currently setup filters. 1032 | * 1033 | * @function 1034 | * @memberof! EnhancedEventEmitter 1035 | * @private 1036 | * @param {Array} emitArguments - All emit function arguments array 1037 | * @returns {Boolean} True to continue with the emit, false to prevent it 1038 | */ 1039 | EnhancedEventEmitter.prototype.runFilterChain = function (emitArguments) { 1040 | if (this.filters) { 1041 | //first check global filters 1042 | let filter; 1043 | if (this.filters.global && this.filters.global.length) { 1044 | for (let index = 0; index < this.filters.global.length; index++) { 1045 | filter = this.filters.global[index]; 1046 | if (!filter.apply(filter, emitArguments)) { 1047 | return false; 1048 | } 1049 | } 1050 | } 1051 | 1052 | //get event 1053 | const event = emitArguments[0]; 1054 | 1055 | //check event specific filters 1056 | if (this.filters.event && this.filters.event[event] && this.filters.event[event].length) { 1057 | for (let index = 0; index < this.filters.event[event].length; index++) { 1058 | filter = this.filters.event[event][index]; 1059 | if (!filter.apply(filter, emitArguments)) { 1060 | return false; 1061 | } 1062 | } 1063 | } 1064 | } 1065 | 1066 | return true; 1067 | }; 1068 | 1069 | /** 1070 | * Marks the given event in the events map. 1071 | * 1072 | * @function 1073 | * @memberof! EnhancedEventEmitter 1074 | * @private 1075 | * @param {String} event - The event name to mark 1076 | * @param {Object} [events] - The events map 1077 | * @returns {Object} The updated events map 1078 | */ 1079 | EnhancedEventEmitter.prototype.markEvent = function (event, events) { 1080 | if (event) { 1081 | events = events || {}; 1082 | events[event] = true; 1083 | } 1084 | 1085 | return events; 1086 | }; 1087 | 1088 | module.exports = EnhancedEventEmitter; 1089 | -------------------------------------------------------------------------------- /test/spec/event-emitter-enhancer-spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const chai = require('chai'); 4 | const assert = chai.assert; 5 | const EventEmitter = require('events').EventEmitter; 6 | const EventEmitterEnhancer = require('../../lib/event-emitter-enhancer'); 7 | const funcs = require('funcs-js'); 8 | 9 | function createEventEmitter() { 10 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 11 | 12 | return new EnhancedEventEmitter(); 13 | } 14 | 15 | function emptyFunction() { 16 | return undefined; 17 | } 18 | 19 | function removeValidation(removed, done) { 20 | if (removed) { 21 | done(); 22 | } else { 23 | assert.fail(); 24 | } 25 | } 26 | 27 | describe('event-emitter-enhancer', function () { 28 | describe('extend', function () { 29 | it('predefined extended events.EventEmitter', function () { 30 | const EnhancedEventEmitter = EventEmitterEnhancer.EnhancedEventEmitter; 31 | let emitter = new EnhancedEventEmitter(); 32 | 33 | assert.isFunction(emitter.baseOn); 34 | assert.isFunction(emitter.baseOnce); 35 | assert.isFunction(emitter.baseRemoveAllListeners); 36 | assert.isFunction(emitter.baseEmit); 37 | 38 | assert.isFunction(emitter.onAsync); 39 | assert.isFunction(emitter.elseError); 40 | assert.isFunction(emitter.else); 41 | assert.isFunction(emitter.filter); 42 | 43 | emitter = new EventEmitter(); 44 | 45 | assert.isUndefined(emitter.onAsync); 46 | assert.isUndefined(emitter.elseError); 47 | assert.isUndefined(emitter.else); 48 | assert.isUndefined(emitter.filter); 49 | }); 50 | 51 | it('extend events.EventEmitter', function () { 52 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 53 | let emitter = new EnhancedEventEmitter(); 54 | 55 | assert.isFunction(emitter.baseOn); 56 | assert.isFunction(emitter.baseOnce); 57 | assert.isFunction(emitter.baseRemoveAllListeners); 58 | assert.isFunction(emitter.baseEmit); 59 | 60 | assert.isFunction(emitter.onAsync); 61 | assert.isFunction(emitter.elseError); 62 | assert.isFunction(emitter.else); 63 | assert.isFunction(emitter.filter); 64 | 65 | emitter = new EventEmitter(); 66 | 67 | assert.isUndefined(emitter.onAsync); 68 | assert.isUndefined(emitter.elseError); 69 | assert.isUndefined(emitter.else); 70 | assert.isUndefined(emitter.filter); 71 | }); 72 | 73 | it('extend events.EventEmitter multi prevent', function () { 74 | assert.isUndefined(EventEmitter.prototype.enhancedEmitterType); 75 | const EnhancedEventEmitter = EventEmitterEnhancer.extend(EventEmitter); 76 | 77 | assert.isUndefined(EventEmitter.prototype.enhancedEmitterType); 78 | assert.isTrue(EnhancedEventEmitter.prototype.enhancedEmitterType); 79 | const emitter = new EnhancedEventEmitter(); 80 | assert.isTrue(emitter.enhancedEmitterType); 81 | 82 | let errorFound = false; 83 | try { 84 | EventEmitterEnhancer.extend(EnhancedEventEmitter); 85 | } catch (error) { 86 | assert.isDefined(error); 87 | errorFound = true; 88 | } 89 | 90 | assert.isTrue(errorFound); 91 | }); 92 | 93 | it('modify custom events.EventEmitter', function () { 94 | const CustomEventEmitter = function () { 95 | EventEmitter.call(this); 96 | }; 97 | CustomEventEmitter.prototype = Object.create(EventEmitter.prototype); 98 | 99 | EventEmitterEnhancer.modify(CustomEventEmitter); 100 | let emitter = new CustomEventEmitter(); 101 | 102 | assert.isFunction(emitter.baseOn); 103 | assert.isFunction(emitter.baseOnce); 104 | assert.isFunction(emitter.baseRemoveAllListeners); 105 | assert.isFunction(emitter.baseEmit); 106 | 107 | assert.isFunction(emitter.onAsync); 108 | assert.isFunction(emitter.elseError); 109 | assert.isFunction(emitter.else); 110 | assert.isFunction(emitter.filter); 111 | 112 | emitter = new EventEmitter(); 113 | 114 | assert.isUndefined(emitter.onAsync); 115 | assert.isUndefined(emitter.elseError); 116 | assert.isUndefined(emitter.else); 117 | assert.isUndefined(emitter.filter); 118 | }); 119 | 120 | it('modify custom events.EventEmitter multi prevent', function () { 121 | assert.isUndefined(EventEmitter.prototype.enhancedEmitterType); 122 | const CustomMultiEventEmitter = function () { 123 | EventEmitter.call(this); 124 | }; 125 | CustomMultiEventEmitter.prototype = Object.create(EventEmitter.prototype); 126 | 127 | EventEmitterEnhancer.modify(CustomMultiEventEmitter); 128 | 129 | assert.isUndefined(EventEmitter.prototype.enhancedEmitterType); 130 | assert.isTrue(CustomMultiEventEmitter.prototype.enhancedEmitterType); 131 | const emitter = new CustomMultiEventEmitter(); 132 | assert.isTrue(emitter.enhancedEmitterType); 133 | 134 | let errorFound = false; 135 | try { 136 | EventEmitterEnhancer.modify(CustomMultiEventEmitter); 137 | } catch (error) { 138 | assert.isDefined(error); 139 | errorFound = true; 140 | } 141 | 142 | assert.isTrue(errorFound); 143 | }); 144 | 145 | it('modifyInstance custom events.EventEmitter', function () { 146 | let emitter = new EventEmitter(); 147 | EventEmitterEnhancer.modifyInstance(emitter); 148 | 149 | assert.isFunction(emitter.baseOn); 150 | assert.isFunction(emitter.baseOnce); 151 | assert.isFunction(emitter.baseRemoveAllListeners); 152 | assert.isFunction(emitter.baseEmit); 153 | 154 | assert.isFunction(emitter.onAsync); 155 | assert.isFunction(emitter.elseError); 156 | assert.isFunction(emitter.else); 157 | assert.isFunction(emitter.filter); 158 | 159 | emitter = new EventEmitter(); 160 | 161 | assert.isUndefined(emitter.onAsync); 162 | assert.isUndefined(emitter.elseError); 163 | assert.isUndefined(emitter.else); 164 | assert.isUndefined(emitter.filter); 165 | }); 166 | 167 | it('modifyInstance custom events.EventEmitter multi prevent', function () { 168 | const emitter = new EventEmitter(); 169 | EventEmitterEnhancer.modifyInstance(emitter); 170 | 171 | assert.isFunction(emitter.baseOn); 172 | assert.isFunction(emitter.baseOnce); 173 | assert.isFunction(emitter.baseRemoveAllListeners); 174 | assert.isFunction(emitter.baseEmit); 175 | 176 | assert.isUndefined(EventEmitter.prototype.enhancedEmitterType); 177 | assert.isTrue(emitter.enhancedEmitterType); 178 | 179 | let errorFound = false; 180 | try { 181 | EventEmitterEnhancer.modifyInstance(emitter); 182 | } catch (error) { 183 | assert.isDefined(error); 184 | errorFound = true; 185 | } 186 | 187 | assert.isTrue(errorFound); 188 | }); 189 | }); 190 | 191 | describe('once', function () { 192 | it('nodejs with remove, called', function () { 193 | const emitter = createEventEmitter(); 194 | 195 | let invoked = false; 196 | const remove = emitter.once('test', function (arg1, arg2, arg3) { 197 | if (invoked) { 198 | assert.fail(); 199 | } 200 | 201 | assert.strictEqual(arg1, 1); 202 | assert.strictEqual(arg2, 2); 203 | assert.strictEqual(arg3, 3); 204 | 205 | invoked = true; 206 | }); 207 | 208 | emitter.emit('test', 1, 2, 3); 209 | 210 | remove(); 211 | 212 | emitter.emit('test', 'bad'); 213 | 214 | remove(); 215 | 216 | emitter.emit('test', 'bad'); 217 | }); 218 | 219 | it('nodejs with remove, removed', function () { 220 | const emitter = createEventEmitter(); 221 | 222 | const remove = emitter.once('test', function () { 223 | assert.fail(); 224 | }); 225 | 226 | remove(); 227 | 228 | emitter.emit('test', 'bad'); 229 | }); 230 | }); 231 | 232 | describe('removeAllListeners', function () { 233 | it('undefined', function () { 234 | const emitter = createEventEmitter(); 235 | 236 | emitter.removeAllListeners(); 237 | }); 238 | 239 | it('single', function () { 240 | const emitter = createEventEmitter(); 241 | 242 | emitter.on('error', funcs.noop); 243 | emitter.on('error', funcs.noop); 244 | assert.strictEqual(emitter.listenerCount('error'), 2); 245 | 246 | emitter.removeAllListeners('error'); 247 | 248 | assert.strictEqual(emitter.listenerCount('error'), 0); 249 | }); 250 | 251 | it('array', function () { 252 | const emitter = createEventEmitter(); 253 | 254 | emitter.on('error', funcs.noop); 255 | emitter.on('error', funcs.noop); 256 | assert.strictEqual(emitter.listenerCount('error'), 2); 257 | 258 | emitter.on('info', funcs.noop); 259 | assert.strictEqual(emitter.listenerCount('info'), 1); 260 | 261 | emitter.removeAllListeners(['error', 'info']); 262 | 263 | assert.strictEqual(emitter.listenerCount('error'), 0); 264 | assert.strictEqual(emitter.listenerCount('info'), 0); 265 | }); 266 | }); 267 | 268 | describe('on', function () { 269 | it('nodejs with remove', function () { 270 | const emitter = createEventEmitter(); 271 | 272 | let invoked = false; 273 | const remove = emitter.on('test', function (arg1, arg2, arg3) { 274 | if (invoked) { 275 | assert.fail(); 276 | } 277 | 278 | assert.strictEqual(arg1, 1); 279 | assert.strictEqual(arg2, 2); 280 | assert.strictEqual(arg3, 3); 281 | 282 | invoked = true; 283 | }); 284 | 285 | emitter.emit('test', 1, 2, 3); 286 | 287 | remove(); 288 | 289 | emitter.emit('test', 'bad'); 290 | 291 | remove(); 292 | 293 | emitter.emit('test', 'bad'); 294 | }); 295 | 296 | it('multiple listeners, single event', function () { 297 | const emitter = createEventEmitter(); 298 | 299 | let invoked1 = false; 300 | let invoked2 = false; 301 | 302 | emitter.on({ 303 | event: 'test-event', 304 | listener: [ 305 | function (arg1, arg2) { 306 | assert.strictEqual(arg1, 'test'); 307 | assert.strictEqual(arg2, 2); 308 | 309 | invoked1 = true; 310 | }, 311 | function (arg1, arg2) { 312 | assert.strictEqual(arg1, 'test'); 313 | assert.strictEqual(arg2, 2); 314 | 315 | invoked2 = true; 316 | } 317 | ] 318 | }); 319 | 320 | emitter.emit('test-event1', 'abc', 123); 321 | assert.isFalse(invoked1); 322 | assert.isFalse(invoked2); 323 | 324 | emitter.emit('test-event', 'test', 2); 325 | assert.isTrue(invoked1); 326 | assert.isTrue(invoked2); 327 | }); 328 | 329 | it('single listener, multiple events', function () { 330 | const emitter = createEventEmitter(); 331 | 332 | let invoked1 = false; 333 | let invoked2 = false; 334 | 335 | emitter.on({ 336 | event: [ 337 | 'test-event1', 338 | 'test-event2' 339 | ], 340 | listener(arg1, arg2) { 341 | if (invoked1 && invoked2) { 342 | assert.fail(); 343 | } else if (invoked1) { 344 | assert.strictEqual(arg1, 'test'); 345 | assert.strictEqual(arg2, 2); 346 | 347 | invoked2 = true; 348 | } else { 349 | assert.strictEqual(arg1, 'abc'); 350 | assert.strictEqual(arg2, 123); 351 | 352 | invoked1 = true; 353 | } 354 | } 355 | }); 356 | 357 | emitter.emit('test-event3', 'bad'); 358 | assert.isFalse(invoked1); 359 | assert.isFalse(invoked2); 360 | 361 | emitter.emit('test-event1', 'abc', 123); 362 | assert.isTrue(invoked1); 363 | assert.isFalse(invoked2); 364 | 365 | emitter.emit('test-event2', 'test', 2); 366 | assert.isTrue(invoked1); 367 | assert.isTrue(invoked2); 368 | }); 369 | 370 | it('multiple listeners, multiple event', function () { 371 | const emitter = createEventEmitter(); 372 | 373 | let invoked1a = false; 374 | let invoked1b = false; 375 | let invoked2a = false; 376 | let invoked2b = false; 377 | 378 | const remove = emitter.on({ 379 | event: [ 380 | 'test-event1', 381 | 'test-event2' 382 | ], 383 | listener: [ 384 | function (arg1, arg2) { 385 | if (invoked1a && invoked1b) { 386 | assert.fail(); 387 | } else if (invoked1a) { 388 | assert.strictEqual(arg1, 'test'); 389 | assert.strictEqual(arg2, 2); 390 | 391 | invoked1b = true; 392 | } else { 393 | assert.strictEqual(arg1, 'abc'); 394 | assert.strictEqual(arg2, 123); 395 | 396 | invoked1a = true; 397 | } 398 | }, 399 | function (arg1, arg2) { 400 | if (invoked2a && invoked2b) { 401 | assert.fail(); 402 | } else if (invoked2a) { 403 | assert.strictEqual(arg1, 'test'); 404 | assert.strictEqual(arg2, 2); 405 | 406 | invoked2b = true; 407 | } else { 408 | assert.strictEqual(arg1, 'abc'); 409 | assert.strictEqual(arg2, 123); 410 | 411 | invoked2a = true; 412 | } 413 | } 414 | ] 415 | }); 416 | 417 | emitter.emit('test-event3', 'bad'); 418 | assert.isFalse(invoked1a); 419 | assert.isFalse(invoked1b); 420 | assert.isFalse(invoked2a); 421 | assert.isFalse(invoked2b); 422 | 423 | emitter.emit('test-event1', 'abc', 123); 424 | assert.isTrue(invoked1a); 425 | assert.isFalse(invoked1b); 426 | assert.isTrue(invoked2a); 427 | assert.isFalse(invoked2b); 428 | 429 | emitter.emit('test-event2', 'test', 2); 430 | assert.isTrue(invoked1a); 431 | assert.isTrue(invoked1b); 432 | assert.isTrue(invoked2a); 433 | assert.isTrue(invoked2b); 434 | 435 | remove(); 436 | 437 | emitter.emit('test-event1', 'abc', 123); 438 | emitter.emit('test-event2', 'test', 2); 439 | 440 | remove(); 441 | 442 | emitter.emit('test-event1', 'abc', 123); 443 | emitter.emit('test-event2', 'test', 2); 444 | }); 445 | 446 | it('async and multiple listeners, multiple event', function (done) { 447 | const emitter = createEventEmitter(); 448 | 449 | let invoked1a = false; 450 | let invoked1b = false; 451 | let invoked2a = false; 452 | let invoked2b = false; 453 | emitter.on({ 454 | event: [ 455 | 'test-event1', 456 | 'test-event2' 457 | ], 458 | listener: [ 459 | function (arg1, arg2) { 460 | if (invoked1a && invoked1b) { 461 | assert.fail(); 462 | } else if (invoked1a) { 463 | assert.strictEqual(arg1, 'test'); 464 | assert.strictEqual(arg2, 2); 465 | 466 | invoked1b = true; 467 | } else { 468 | assert.strictEqual(arg1, 'abc'); 469 | assert.strictEqual(arg2, 123); 470 | 471 | invoked1a = true; 472 | } 473 | }, 474 | function (arg1, arg2) { 475 | if (invoked2a && invoked2b) { 476 | assert.fail(); 477 | } else if (invoked2a) { 478 | assert.strictEqual(arg1, 'test'); 479 | assert.strictEqual(arg2, 2); 480 | 481 | invoked2b = true; 482 | } else { 483 | assert.strictEqual(arg1, 'abc'); 484 | assert.strictEqual(arg2, 123); 485 | 486 | invoked2a = true; 487 | } 488 | } 489 | ], 490 | async: true 491 | }); 492 | 493 | emitter.emit('test-event3', 'bad'); 494 | 495 | setTimeout(function () { 496 | assert.isFalse(invoked1a); 497 | assert.isFalse(invoked1b); 498 | assert.isFalse(invoked2a); 499 | assert.isFalse(invoked2b); 500 | 501 | emitter.emit('test-event1', 'abc', 123); 502 | setTimeout(function () { 503 | assert.isTrue(invoked1a); 504 | assert.isFalse(invoked1b); 505 | assert.isTrue(invoked2a); 506 | assert.isFalse(invoked2b); 507 | 508 | emitter.emit('test-event2', 'test', 2); 509 | setTimeout(function () { 510 | assert.isTrue(invoked1a); 511 | assert.isTrue(invoked1b); 512 | assert.isTrue(invoked2a); 513 | assert.isTrue(invoked2b); 514 | 515 | done(); 516 | }, 5); 517 | }, 5); 518 | }, 5); 519 | }); 520 | 521 | it('timeout and multiple listeners, multiple event', function (done) { 522 | const emitter = createEventEmitter(); 523 | 524 | let invoked1a = false; 525 | let invoked1b = false; 526 | let invoked2a = false; 527 | let invoked2b = false; 528 | 529 | emitter.on({ 530 | event: [ 531 | 'test-event1', 532 | 'test-event2' 533 | ], 534 | listener: [ 535 | function (arg1, arg2) { 536 | if (invoked1a && invoked1b) { 537 | assert.fail(); 538 | } else if (invoked1a) { 539 | assert.strictEqual(arg1, 'test'); 540 | assert.strictEqual(arg2, 2); 541 | 542 | invoked1b = true; 543 | } else { 544 | assert.strictEqual(arg1, 'abc'); 545 | assert.strictEqual(arg2, 123); 546 | 547 | invoked1a = true; 548 | } 549 | }, 550 | function (arg1, arg2) { 551 | if (invoked2a && invoked2b) { 552 | assert.fail(); 553 | } else if (invoked2a) { 554 | assert.strictEqual(arg1, 'test'); 555 | assert.strictEqual(arg2, 2); 556 | 557 | invoked2b = true; 558 | } else { 559 | assert.strictEqual(arg1, 'abc'); 560 | assert.strictEqual(arg2, 123); 561 | 562 | invoked2a = true; 563 | } 564 | } 565 | ], 566 | timeout: 500 567 | }); 568 | 569 | setTimeout(function () { 570 | emitter.emit('test-event3', 'bad'); 571 | assert.isFalse(invoked1a); 572 | assert.isFalse(invoked1b); 573 | assert.isFalse(invoked2a); 574 | assert.isFalse(invoked2b); 575 | 576 | emitter.emit('test-event1', 'abc', 123); 577 | assert.isTrue(invoked1a); 578 | assert.isFalse(invoked1b); 579 | assert.isTrue(invoked2a); 580 | assert.isFalse(invoked2b); 581 | 582 | emitter.emit('test-event2', 'test', 2); 583 | assert.isTrue(invoked1a); 584 | assert.isTrue(invoked1b); 585 | assert.isTrue(invoked2a); 586 | assert.isTrue(invoked2b); 587 | 588 | setTimeout(function () { 589 | emitter.emit('test-event3', 'bad'); 590 | emitter.emit('test-event1', 'abc', 123); 591 | emitter.emit('test-event2', 'test', 2); 592 | 593 | done(); 594 | }, 600); 595 | }, 100); 596 | }); 597 | 598 | it('timeout and async and multiple listeners, multiple event', function (done) { 599 | const emitter = createEventEmitter(); 600 | 601 | let invoked1a = false; 602 | let invoked1b = false; 603 | let invoked2a = false; 604 | let invoked2b = false; 605 | emitter.on({ 606 | event: [ 607 | 'test-event1', 608 | 'test-event2' 609 | ], 610 | listener: [ 611 | function (arg1, arg2) { 612 | if (invoked1a && invoked1b) { 613 | assert.fail(); 614 | } else if (invoked1a) { 615 | assert.strictEqual(arg1, 'test'); 616 | assert.strictEqual(arg2, 2); 617 | 618 | invoked1b = true; 619 | } else { 620 | assert.strictEqual(arg1, 'abc'); 621 | assert.strictEqual(arg2, 123); 622 | 623 | invoked1a = true; 624 | } 625 | }, 626 | function (arg1, arg2) { 627 | if (invoked2a && invoked2b) { 628 | assert.fail(); 629 | } else if (invoked2a) { 630 | assert.strictEqual(arg1, 'test'); 631 | assert.strictEqual(arg2, 2); 632 | 633 | invoked2b = true; 634 | } else { 635 | assert.strictEqual(arg1, 'abc'); 636 | assert.strictEqual(arg2, 123); 637 | 638 | invoked2a = true; 639 | } 640 | } 641 | ], 642 | async: true, 643 | timeout: 300 644 | }); 645 | 646 | emitter.emit('test-event3', 'bad'); 647 | 648 | setTimeout(function () { 649 | assert.isFalse(invoked1a); 650 | assert.isFalse(invoked1b); 651 | assert.isFalse(invoked2a); 652 | assert.isFalse(invoked2b); 653 | 654 | emitter.emit('test-event1', 'abc', 123); 655 | setTimeout(function () { 656 | assert.isTrue(invoked1a); 657 | assert.isFalse(invoked1b); 658 | assert.isTrue(invoked2a); 659 | assert.isFalse(invoked2b); 660 | 661 | emitter.emit('test-event2', 'test', 2); 662 | setTimeout(function () { 663 | assert.isTrue(invoked1a); 664 | assert.isTrue(invoked1b); 665 | assert.isTrue(invoked2a); 666 | assert.isTrue(invoked2b); 667 | 668 | setTimeout(function () { 669 | emitter.emit('test-event1', 'bad'); 670 | 671 | setTimeout(done, 100); 672 | }, 400); 673 | }, 5); 674 | }, 5); 675 | }, 5); 676 | }); 677 | }); 678 | 679 | describe('suspend', function () { 680 | it('suspend all', function (done) { 681 | const emitter1 = createEventEmitter(); 682 | emitter1.on('test', function () { 683 | done(); 684 | }); 685 | const emitter2 = createEventEmitter(); 686 | emitter2.suspended = true; 687 | emitter2.on('test', function () { 688 | assert.fail(); 689 | }); 690 | 691 | assert.isFalse(emitter1.suspended); 692 | 693 | emitter2.emit('test'); 694 | emitter1.emit('test'); 695 | }); 696 | 697 | it('suspend specific', function (done) { 698 | const emitter1 = createEventEmitter(); 699 | emitter1.on('test', function () { 700 | done(); 701 | }); 702 | const emitter2 = createEventEmitter(); 703 | emitter2.suspend('test'); 704 | emitter2.on('test', function () { 705 | assert.fail(); 706 | }); 707 | 708 | assert.isFalse(emitter1.suspended); 709 | assert.isFalse(emitter2.suspended); 710 | 711 | emitter2.emit('test'); 712 | emitter1.emit('test'); 713 | }); 714 | 715 | it('unsuspend specific', function (done) { 716 | const emitter1 = createEventEmitter(); 717 | const emitter2 = createEventEmitter(); 718 | 719 | let unsuspendCalled = false; 720 | emitter1.on('test', function () { 721 | emitter2.unsuspend('test'); 722 | unsuspendCalled = true; 723 | 724 | emitter2.emit('test'); 725 | }); 726 | emitter2.suspend('test'); 727 | emitter2.on('test', function () { 728 | if (unsuspendCalled) { 729 | done(); 730 | } else { 731 | assert.fail(); 732 | } 733 | }); 734 | 735 | assert.isFalse(emitter1.suspended); 736 | assert.isFalse(emitter2.suspended); 737 | 738 | emitter2.emit('test'); 739 | emitter1.emit('test'); 740 | }); 741 | 742 | it('unsuspend no input', function (done) { 743 | const emitter1 = createEventEmitter(); 744 | const emitter2 = createEventEmitter(); 745 | 746 | let unsuspendCalled = false; 747 | emitter1.on('test', function () { 748 | emitter2.unsuspend('test'); 749 | unsuspendCalled = true; 750 | 751 | emitter2.emit('test'); 752 | }); 753 | emitter2.suspend('test'); 754 | emitter2.on('test', function () { 755 | if (unsuspendCalled) { 756 | done(); 757 | } else { 758 | assert.fail(); 759 | } 760 | }); 761 | 762 | assert.isFalse(emitter1.suspended); 763 | assert.isFalse(emitter2.suspended); 764 | 765 | emitter2.unsuspend(); 766 | 767 | emitter2.emit('test'); 768 | emitter1.emit('test'); 769 | }); 770 | }); 771 | 772 | describe('else', function () { 773 | it('no else', function (done) { 774 | const emitter = createEventEmitter(); 775 | emitter.on('test', function () { 776 | done(); 777 | }); 778 | 779 | emitter.emit('test'); 780 | }); 781 | 782 | it('single else', function (done) { 783 | const emitter = createEventEmitter(); 784 | emitter.else(function (type, arg1, arg2) { 785 | assert.equal(type, 'test'); 786 | assert.equal(arg1, 1); 787 | assert.equal(arg2, 2); 788 | assert.equal(arguments.length, 3); 789 | 790 | done(); 791 | }); 792 | 793 | emitter.emit('test', 1, 2); 794 | }); 795 | 796 | it('multi else', function (done) { 797 | const emitter = createEventEmitter(); 798 | let elseCalled = 0; 799 | emitter.else(function () { 800 | elseCalled++; 801 | }); 802 | emitter.else(function (type, arg1, arg2) { 803 | elseCalled++; 804 | 805 | assert.equal(type, 'test'); 806 | assert.equal(arg1, 1); 807 | assert.equal(arg2, 2); 808 | assert.equal(arguments.length, 3); 809 | 810 | assert.equal(elseCalled, 2); 811 | 812 | done(); 813 | }); 814 | 815 | emitter.emit('test', 1, 2); 816 | }); 817 | 818 | it('no else call', function (done) { 819 | const emitter = createEventEmitter(); 820 | emitter.else(function () { 821 | assert.fail(); 822 | }); 823 | emitter.on('test', function () { 824 | done(); 825 | }); 826 | 827 | emitter.emit('test'); 828 | }); 829 | }); 830 | 831 | describe('remove else', function () { 832 | it('no else', function (done) { 833 | const emitter = createEventEmitter(); 834 | emitter.on('test', function () { 835 | done(); 836 | }); 837 | emitter.removeAllElseListeners(); 838 | 839 | emitter.emit('test'); 840 | }); 841 | 842 | it('no input', function () { 843 | const emitter = createEventEmitter(); 844 | emitter.removeElseListener(); 845 | }); 846 | 847 | it('remove single else', function (done) { 848 | const emitter = createEventEmitter(); 849 | let removed = false; 850 | emitter.else(function (type, arg1, arg2) { 851 | if (removed) { 852 | assert.fail(); 853 | } else { 854 | assert.equal(type, 'test'); 855 | assert.equal(arg1, 1); 856 | assert.equal(arg2, 2); 857 | assert.equal(arguments.length, 3); 858 | 859 | emitter.removeElseListener(this); 860 | removed = true; 861 | 862 | emitter.emit('test', 1, 2); 863 | 864 | done(); 865 | } 866 | }); 867 | 868 | emitter.emit('test', 1, 2); 869 | }); 870 | 871 | it('multi else remove', function (done) { 872 | const emitter = createEventEmitter(); 873 | let elseCalled = 0; 874 | let removed = false; 875 | emitter.else(function () { 876 | elseCalled++; 877 | 878 | if (elseCalled === 3) { 879 | assert.isTrue(removed); 880 | done(); 881 | } 882 | }); 883 | emitter.else(function (type, arg1, arg2) { 884 | if (removed) { 885 | assert.fail(); 886 | } else { 887 | elseCalled++; 888 | 889 | assert.equal(type, 'test'); 890 | assert.equal(arg1, 1); 891 | assert.equal(arg2, 2); 892 | assert.equal(arguments.length, 3); 893 | 894 | assert.equal(elseCalled, 2); 895 | 896 | emitter.removeElseListener(this); 897 | removed = true; 898 | 899 | emitter.emit('test'); 900 | } 901 | }); 902 | 903 | emitter.emit('test', 1, 2); 904 | }); 905 | 906 | it('remove all else call', function (done) { 907 | const emitter = createEventEmitter(); 908 | let elseCalled = 0; 909 | let removed = false; 910 | emitter.else(function () { 911 | if (removed) { 912 | assert.fail(); 913 | } else { 914 | elseCalled++; 915 | } 916 | }); 917 | emitter.else(function () { 918 | if (removed) { 919 | assert.fail(); 920 | } else { 921 | elseCalled++; 922 | 923 | assert.equal(elseCalled, 2); 924 | 925 | emitter.removeAllElseListeners(); 926 | removed = true; 927 | 928 | emitter.emit('test'); 929 | 930 | done(); 931 | } 932 | }); 933 | 934 | emitter.emit('test'); 935 | }); 936 | 937 | it('remove with unelse', function (done) { 938 | const emitter = createEventEmitter(); 939 | let removed = false; 940 | emitter.else(function (type, arg1, arg2) { 941 | if (removed) { 942 | assert.fail(); 943 | } else { 944 | assert.equal(type, 'test'); 945 | assert.equal(arg1, 1); 946 | assert.equal(arg2, 2); 947 | assert.equal(arguments.length, 3); 948 | 949 | emitter.unelse(this); 950 | removed = true; 951 | 952 | emitter.emit('test', 1, 2); 953 | 954 | done(); 955 | } 956 | }); 957 | 958 | emitter.emit('test', 1, 2); 959 | }); 960 | }); 961 | 962 | describe('elseError', function () { 963 | it('remove no input', function () { 964 | const emitter = createEventEmitter(); 965 | emitter.removeElseError(); 966 | }); 967 | 968 | it('single event else error', function (done) { 969 | const emitter = createEventEmitter(); 970 | emitter.else(function () { 971 | assert.fail(); 972 | }); 973 | emitter.on('error', function (error) { 974 | assert.isDefined(error); 975 | 976 | done(); 977 | }); 978 | emitter.elseError('test'); 979 | 980 | emitter.emit('test'); 981 | }); 982 | 983 | it('remove event else error', function (done) { 984 | const emitter = createEventEmitter(); 985 | let removed = false; 986 | emitter.else(function () { 987 | removeValidation(removed, done); 988 | }); 989 | emitter.on('error', function (error) { 990 | assert.isDefined(error); 991 | 992 | emitter.removeElseError('test'); 993 | removed = true; 994 | 995 | emitter.emit('test'); 996 | }); 997 | emitter.elseError('test'); 998 | 999 | emitter.emit('test'); 1000 | }); 1001 | 1002 | it('remove event unelse error', function (done) { 1003 | const emitter = createEventEmitter(); 1004 | let removed = false; 1005 | emitter.else(function () { 1006 | removeValidation(removed, done); 1007 | }); 1008 | emitter.on('error', function (error) { 1009 | assert.isDefined(error); 1010 | 1011 | emitter.unelseError('test'); 1012 | removed = true; 1013 | 1014 | emitter.emit('test'); 1015 | }); 1016 | emitter.elseError('test'); 1017 | 1018 | emitter.emit('test'); 1019 | }); 1020 | }); 1021 | 1022 | describe('emitAsync', function () { 1023 | it('async emit', function (done) { 1024 | let eventDone = false; 1025 | const emitter = createEventEmitter(); 1026 | emitter.on('testAsync', function (arg1, arg2) { 1027 | eventDone = true; 1028 | 1029 | assert.equal(arg1, 'test arg 1'); 1030 | assert.equal(arg2, 2); 1031 | }); 1032 | 1033 | emitter.emitAsync('testAsync', 'test arg 1', 2, function onEmitDone(event, arg1, arg2, emitted) { 1034 | assert.equal(event, 'testAsync'); 1035 | assert.equal(arg1, 'test arg 1'); 1036 | assert.equal(arg2, 2); 1037 | assert.isTrue(emitted); 1038 | 1039 | done(); 1040 | }); 1041 | 1042 | if (eventDone) { 1043 | assert.fail(); 1044 | } 1045 | }); 1046 | 1047 | it('async emit noemit', function (done) { 1048 | const emitter = createEventEmitter(); 1049 | emitter.emitAsync('testNoEmit', 1, 2, function onEmitDone(event, arg1, arg2, emitted) { 1050 | assert.equal(event, 'testNoEmit'); 1051 | assert.equal(arg1, 1); 1052 | assert.equal(arg2, 2); 1053 | assert.isFalse(emitted); 1054 | 1055 | done(); 1056 | }); 1057 | }); 1058 | 1059 | it('async emit missing event with args', function () { 1060 | const emitter = createEventEmitter(); 1061 | 1062 | let errorFound = false; 1063 | try { 1064 | emitter.emitAsync(1, 2, function () { 1065 | return undefined; 1066 | }); 1067 | } catch (error) { 1068 | assert.isDefined(error); 1069 | errorFound = true; 1070 | } 1071 | 1072 | assert.isTrue(errorFound); 1073 | }); 1074 | 1075 | it('async emit no args', function () { 1076 | const emitter = createEventEmitter(); 1077 | 1078 | let errorFound = false; 1079 | try { 1080 | emitter.emitAsync(); 1081 | } catch (error) { 1082 | assert.isDefined(error); 1083 | errorFound = true; 1084 | } 1085 | 1086 | assert.isTrue(errorFound); 1087 | }); 1088 | 1089 | it('async emit missing callback with args', function (done) { 1090 | const emitter = createEventEmitter(); 1091 | 1092 | emitter.on('test', function (value1, value2) { 1093 | assert.strictEqual(value1, 1); 1094 | assert.strictEqual(value2, 2); 1095 | 1096 | done(); 1097 | }); 1098 | 1099 | emitter.emitAsync('test', 1, 2); 1100 | }); 1101 | 1102 | it('async emit missing callback without args', function (done) { 1103 | const emitter = createEventEmitter(); 1104 | 1105 | emitter.on('test', function () { 1106 | assert.strictEqual(0, arguments.length); 1107 | 1108 | done(); 1109 | }); 1110 | 1111 | emitter.emitAsync('test'); 1112 | }); 1113 | }); 1114 | 1115 | describe('onAsync', function () { 1116 | it('async on', function (done) { 1117 | let eventDone = false; 1118 | const eventDoneValidation = function () { 1119 | if (eventDone) { 1120 | assert.fail(); 1121 | } 1122 | }; 1123 | const emitter = createEventEmitter(); 1124 | emitter.on('test', eventDoneValidation); 1125 | emitter.onAsync('test', function (arg1, arg2) { 1126 | eventDone = true; 1127 | 1128 | assert.equal(arg1, 1); 1129 | assert.equal(arg2, 2); 1130 | 1131 | done(); 1132 | }); 1133 | emitter.on('test', eventDoneValidation); 1134 | 1135 | assert.equal(3, emitter.listeners('test').length); 1136 | 1137 | emitter.emit('test', 1, 2); 1138 | 1139 | if (eventDone) { 1140 | assert.fail(); 1141 | } 1142 | }); 1143 | 1144 | it('async on missing callback', function () { 1145 | const emitter = createEventEmitter(); 1146 | 1147 | let errorFound = false; 1148 | try { 1149 | emitter.onAsync('test'); 1150 | } catch (error) { 1151 | assert.isDefined(error); 1152 | errorFound = true; 1153 | } 1154 | 1155 | assert.isTrue(errorFound); 1156 | }); 1157 | 1158 | it('async on not a callback', function () { 1159 | const emitter = createEventEmitter(); 1160 | 1161 | let errorFound = false; 1162 | try { 1163 | const notFunction = 'something'; 1164 | emitter.onAsync('test', notFunction); 1165 | } catch (error) { 1166 | assert.isDefined(error); 1167 | errorFound = true; 1168 | } 1169 | 1170 | assert.isTrue(errorFound); 1171 | }); 1172 | 1173 | it('async on remove callback', function () { 1174 | const emitter = createEventEmitter(); 1175 | const remove = emitter.onAsync('test', emptyFunction); 1176 | emitter.on('test', emptyFunction); 1177 | 1178 | assert.equal(2, emitter.listeners('test').length); 1179 | 1180 | remove(); 1181 | 1182 | assert.equal(1, emitter.listeners('test').length); 1183 | 1184 | remove(); 1185 | 1186 | assert.equal(1, emitter.listeners('test').length); 1187 | }); 1188 | }); 1189 | 1190 | describe('onAny', function () { 1191 | it('onAny null events', function () { 1192 | const emitter = createEventEmitter(); 1193 | 1194 | let errorFound = false; 1195 | try { 1196 | emitter.onAny(null, function () { 1197 | assert.fail(); 1198 | }); 1199 | } catch (error) { 1200 | assert.isDefined(error); 1201 | errorFound = true; 1202 | } 1203 | 1204 | assert.isTrue(errorFound); 1205 | }); 1206 | 1207 | it('onAny undefined events', function () { 1208 | const emitter = createEventEmitter(); 1209 | 1210 | let errorFound = false; 1211 | try { 1212 | emitter.onAny(undefined, function () { 1213 | assert.fail(); 1214 | }); 1215 | } catch (error) { 1216 | assert.isDefined(error); 1217 | errorFound = true; 1218 | } 1219 | 1220 | assert.isTrue(errorFound); 1221 | }); 1222 | 1223 | it('onAny missing callback', function () { 1224 | const emitter = createEventEmitter(); 1225 | 1226 | let errorFound = false; 1227 | try { 1228 | emitter.onAny(['test']); 1229 | } catch (error) { 1230 | assert.isDefined(error); 1231 | errorFound = true; 1232 | } 1233 | 1234 | assert.isTrue(errorFound); 1235 | }); 1236 | 1237 | it('onAny single', function () { 1238 | const emitter = createEventEmitter(); 1239 | 1240 | let triggerCount = 0; 1241 | const remove = emitter.onAny('test-single', function (arg1, arg2, arg3) { 1242 | assert.equal(arg1, 1); 1243 | assert.equal(arg2, 'a'); 1244 | assert.deepEqual(arg3, { 1245 | something: [1, 2, 3] 1246 | }); 1247 | 1248 | triggerCount++; 1249 | }); 1250 | 1251 | emitter.emit('fake1', 'wrong'); 1252 | assert.equal(triggerCount, 0); 1253 | 1254 | emitter.emit('test-single', 1, 'a', { 1255 | something: [1, 2, 3] 1256 | }); 1257 | assert.equal(triggerCount, 1); 1258 | 1259 | remove(); 1260 | 1261 | emitter.emit('test-single', 1, 'a', { 1262 | something: [1, 2, 3] 1263 | }); 1264 | assert.equal(triggerCount, 1); 1265 | 1266 | remove(); 1267 | 1268 | emitter.emit('test-single', 1, 'a', { 1269 | something: [1, 2, 3] 1270 | }); 1271 | assert.equal(triggerCount, 1); 1272 | }); 1273 | 1274 | it('onAny single array event', function () { 1275 | const emitter = createEventEmitter(); 1276 | 1277 | let triggerCount = 0; 1278 | const remove = emitter.onAny(['test-array'], function (arg1, arg2, arg3) { 1279 | assert.equal(arg1, 1); 1280 | assert.equal(arg2, 'a'); 1281 | assert.deepEqual(arg3, { 1282 | something: [1, 2, 3] 1283 | }); 1284 | 1285 | triggerCount++; 1286 | }); 1287 | 1288 | emitter.emit('fake1', 'wrong'); 1289 | assert.equal(triggerCount, 0); 1290 | 1291 | emitter.emit('test-array', 1, 'a', { 1292 | something: [1, 2, 3] 1293 | }); 1294 | assert.equal(triggerCount, 1); 1295 | 1296 | emitter.emit('fake1', 'wrong'); 1297 | assert.equal(triggerCount, 1); 1298 | 1299 | remove(); 1300 | 1301 | emitter.emit('test-array', 1, 'a', { 1302 | something: [1, 2, 3] 1303 | }); 1304 | assert.equal(triggerCount, 1); 1305 | }); 1306 | 1307 | it('onAny multiple array event', function () { 1308 | const emitter = createEventEmitter(); 1309 | 1310 | let triggerCount = 0; 1311 | const events = ['test-array-1', 'test-array-2', 'test-array-3']; 1312 | const remove = emitter.onAny(events, function (arg1, arg2, arg3) { 1313 | assert.equal(arg1, 1); 1314 | assert.equal(arg2, 'a'); 1315 | assert.deepEqual(arg3, { 1316 | something: [1, 2, 3] 1317 | }); 1318 | 1319 | triggerCount++; 1320 | }); 1321 | 1322 | emitter.emit('fake1', 'wrong'); 1323 | assert.equal(triggerCount, 0); 1324 | 1325 | events.forEach(function (eventName) { 1326 | emitter.emit(eventName, 1, 'a', { 1327 | something: [1, 2, 3] 1328 | }); 1329 | }); 1330 | assert.equal(triggerCount, 3); 1331 | 1332 | emitter.emit('fake1', 'wrong'); 1333 | assert.equal(triggerCount, 3); 1334 | 1335 | remove(); 1336 | 1337 | events.forEach(function (eventName) { 1338 | emitter.emit(eventName, 1, 'a', { 1339 | something: [1, 2, 3] 1340 | }); 1341 | }); 1342 | assert.equal(triggerCount, 3); 1343 | 1344 | remove(); 1345 | 1346 | events.forEach(function (eventName) { 1347 | emitter.emit(eventName, 1, 'a', { 1348 | something: [1, 2, 3] 1349 | }); 1350 | }); 1351 | assert.equal(triggerCount, 3); 1352 | }); 1353 | }); 1354 | 1355 | describe('filter', function () { 1356 | it('no filter + event', function noFilterOrEventTest() { 1357 | const emitter = createEventEmitter(); 1358 | 1359 | let errorFound = false; 1360 | try { 1361 | emitter.filter(); 1362 | } catch (error) { 1363 | assert.isDefined(error); 1364 | errorFound = true; 1365 | } 1366 | 1367 | assert.isTrue(errorFound); 1368 | }); 1369 | 1370 | it('no filter', function noFilterFuncTest() { 1371 | const emitter = createEventEmitter(); 1372 | 1373 | let errorFound = false; 1374 | try { 1375 | emitter.filter('test'); 1376 | } catch (error) { 1377 | assert.isDefined(error); 1378 | errorFound = true; 1379 | } 1380 | 1381 | assert.isTrue(errorFound); 1382 | }); 1383 | 1384 | it('too many args', function tooManyArgsTest() { 1385 | const emitter = createEventEmitter(); 1386 | 1387 | let errorFound = false; 1388 | try { 1389 | emitter.filter('test', 'test', emptyFunction); 1390 | } catch (error) { 1391 | assert.isDefined(error); 1392 | errorFound = true; 1393 | } 1394 | 1395 | assert.isTrue(errorFound); 1396 | }); 1397 | 1398 | it('single global filter', function (done) { 1399 | const emitter = createEventEmitter(); 1400 | let filterAdded = false; 1401 | emitter.on('test', function () { 1402 | if (filterAdded) { 1403 | assert.fail(); 1404 | } else { 1405 | emitter.filter(function (event, arg1, arg2) { 1406 | assert.equal(event, 'test'); 1407 | assert.equal(arg1, 101); 1408 | assert.equal(arg2, 201); 1409 | 1410 | return false; 1411 | }); 1412 | filterAdded = true; 1413 | 1414 | emitter.emit('test', 101, 201); 1415 | 1416 | done(); 1417 | } 1418 | }); 1419 | emitter.emit('test', 1, 2); 1420 | }); 1421 | 1422 | it('single specific filter', function (done) { 1423 | const emitter = createEventEmitter(); 1424 | let filterAdded = false; 1425 | emitter.on('test', function () { 1426 | if (filterAdded) { 1427 | assert.fail(); 1428 | } else { 1429 | emitter.filter('test', function (event, arg1, arg2) { 1430 | assert.equal(event, 'test'); 1431 | assert.equal(arg1, 210); 1432 | assert.equal(arg2, 220); 1433 | 1434 | return false; 1435 | }); 1436 | filterAdded = true; 1437 | 1438 | emitter.emit('test', 210, 220); 1439 | 1440 | done(); 1441 | } 1442 | }); 1443 | emitter.emit('test', 1, 2); 1444 | }); 1445 | 1446 | it('single specific different event filter', function (done) { 1447 | const emitter = createEventEmitter(); 1448 | let filterAdded = false; 1449 | emitter.on('test', function () { 1450 | if (filterAdded) { 1451 | done(); 1452 | } else { 1453 | emitter.filter('test2', function () { 1454 | assert.fail(); 1455 | }); 1456 | filterAdded = true; 1457 | 1458 | emitter.emit('test', 10, 20); 1459 | } 1460 | }); 1461 | emitter.emit('test', 1, 2); 1462 | }); 1463 | 1464 | it('multiple global filter', function (done) { 1465 | const emitter = createEventEmitter(); 1466 | let filterAdded = false; 1467 | emitter.on('test', function () { 1468 | if (filterAdded) { 1469 | assert.fail(); 1470 | } else { 1471 | let filtersCalled = 0; 1472 | const onEvent = function (event, arg1, arg2) { 1473 | filtersCalled++; 1474 | assert.equal(event, 'test'); 1475 | assert.equal(arg1, 'abc'); 1476 | assert.equal(arg2, 'DEF'); 1477 | 1478 | return true; 1479 | }; 1480 | for (let i = 0; i < 15; i++) { 1481 | emitter.filter(onEvent); 1482 | } 1483 | emitter.filter(function () { 1484 | filtersCalled++; 1485 | assert.equal(filtersCalled, 16); 1486 | 1487 | return false; 1488 | }); 1489 | emitter.filter(function () { 1490 | assert.fail(); 1491 | }); 1492 | filterAdded = true; 1493 | 1494 | emitter.emit('test', 'abc', 'DEF'); 1495 | 1496 | done(); 1497 | } 1498 | }); 1499 | emitter.emit('test', 1, 2); 1500 | }); 1501 | 1502 | it('multiple specific filter', function (done) { 1503 | const emitter = createEventEmitter(); 1504 | let filterAdded = false; 1505 | emitter.on('test', function () { 1506 | if (filterAdded) { 1507 | assert.fail(); 1508 | } else { 1509 | let filtersCalled = 0; 1510 | const onEvent = function (event, arg1, arg2, arg3) { 1511 | filtersCalled++; 1512 | assert.equal(event, 'test'); 1513 | assert.equal(arg1, 'a'); 1514 | assert.equal(arg2, 1); 1515 | assert.isTrue(arg3); 1516 | 1517 | return true; 1518 | }; 1519 | for (let i = 0; i < 25; i++) { 1520 | emitter.filter('test', onEvent); 1521 | } 1522 | emitter.filter('test', function () { 1523 | filtersCalled++; 1524 | 1525 | assert.equal(filtersCalled, 26); 1526 | 1527 | return false; 1528 | }); 1529 | emitter.filter('test', function () { 1530 | assert.fail(); 1531 | }); 1532 | filterAdded = true; 1533 | 1534 | emitter.emit('test', 'a', 1, true); 1535 | 1536 | done(); 1537 | } 1538 | }); 1539 | emitter.emit('test', 1, 2); 1540 | }); 1541 | 1542 | it('remove global filter', function (done) { 1543 | const emitter = createEventEmitter(); 1544 | let filterAdded = false; 1545 | emitter.on('test', function () { 1546 | if (filterAdded) { 1547 | assert.fail(); 1548 | } else { 1549 | let filtersCalled = 0; 1550 | /*jslint unparam: true */ 1551 | const onEvent = function (event, arg1) { 1552 | filtersCalled++; 1553 | assert.isFalse(arg1); 1554 | 1555 | return true; 1556 | }; 1557 | /*jslint unparam: false */ 1558 | 1559 | for (let i = 0; i < 5; i++) { 1560 | emitter.filter(onEvent); 1561 | } 1562 | const remove1 = emitter.filter(function () { 1563 | filtersCalled++; 1564 | assert.equal(filtersCalled, 6); 1565 | 1566 | return false; 1567 | }); 1568 | const remove2 = emitter.filter(function () { 1569 | assert.fail(); 1570 | }); 1571 | filterAdded = true; 1572 | 1573 | emitter.emit('test', false); 1574 | 1575 | remove1(); 1576 | remove2(); 1577 | 1578 | const remove3 = emitter.filter(function () { 1579 | filtersCalled++; 1580 | assert.equal(filtersCalled, 12); 1581 | 1582 | return true; 1583 | }); 1584 | 1585 | emitter.emit('test2', false); 1586 | 1587 | remove1(); 1588 | remove2(); 1589 | remove3(); 1590 | 1591 | emitter.filter(function () { 1592 | filtersCalled++; 1593 | assert.equal(filtersCalled, 18); 1594 | 1595 | return true; 1596 | }); 1597 | 1598 | emitter.emit('test2', false); 1599 | 1600 | done(); 1601 | } 1602 | }); 1603 | emitter.emit('test', 1, 2); 1604 | }); 1605 | 1606 | it('remove specific filter', function (done) { 1607 | const emitter = createEventEmitter(); 1608 | let filterAdded = false; 1609 | emitter.on('test', function () { 1610 | if (filterAdded) { 1611 | assert.fail(); 1612 | } else { 1613 | let filtersCalled = 0; 1614 | const onEvent = function (event, arg1, arg2) { 1615 | filtersCalled++; 1616 | assert.equal(event, 'test'); 1617 | assert.equal(arg1, 10); 1618 | assert.equal(arg2, 20); 1619 | 1620 | return true; 1621 | }; 1622 | for (let i = 0; i < 3; i++) { 1623 | emitter.filter('test', onEvent); 1624 | } 1625 | const remove1 = emitter.filter('test', function () { 1626 | filtersCalled++; 1627 | 1628 | assert.equal(filtersCalled, 4); 1629 | 1630 | return false; 1631 | }); 1632 | const remove2 = emitter.filter('test', function () { 1633 | assert.fail(); 1634 | }); 1635 | filterAdded = true; 1636 | 1637 | emitter.emit('test', 10, 20); 1638 | 1639 | remove1(); 1640 | remove2(); 1641 | 1642 | emitter.filter('test2', function (event, arg1, arg2) { 1643 | filtersCalled++; 1644 | assert.equal(event, 'test2'); 1645 | assert.equal(arg1, 101); 1646 | assert.equal(arg2, 210); 1647 | 1648 | assert.equal(filtersCalled, 5); 1649 | 1650 | return true; 1651 | }); 1652 | 1653 | emitter.emit('test2', 101, 210); 1654 | 1655 | done(); 1656 | } 1657 | }); 1658 | emitter.emit('test', 1, 2); 1659 | }); 1660 | 1661 | it('remove specific multiple times', function () { 1662 | const emitter = createEventEmitter(); 1663 | 1664 | const remove = emitter.filter('test', function () { 1665 | assert.fail(); 1666 | }); 1667 | 1668 | remove(); 1669 | remove(); 1670 | 1671 | emitter.emit('test', 10, 20); 1672 | }); 1673 | 1674 | it('remove specific missing event', function () { 1675 | const emitter = createEventEmitter(); 1676 | 1677 | const remove = emitter.filter('test', function () { 1678 | assert.fail(); 1679 | }); 1680 | 1681 | emitter.filters.event = {}; 1682 | 1683 | remove(); 1684 | 1685 | emitter.emit('test', 10, 20); 1686 | }); 1687 | 1688 | it('remove specific missing listener', function () { 1689 | const emitter = createEventEmitter(); 1690 | 1691 | const remove = emitter.filter('test', function () { 1692 | assert.fail(); 1693 | }); 1694 | 1695 | emitter.filters.event.test = [ 1696 | function fake() { 1697 | return true; 1698 | } 1699 | ]; 1700 | 1701 | remove(); 1702 | 1703 | emitter.emit('test', 10, 20); 1704 | }); 1705 | 1706 | it('remove global multiple times', function () { 1707 | const emitter = createEventEmitter(); 1708 | 1709 | const remove = emitter.filter(function () { 1710 | assert.fail(); 1711 | }); 1712 | 1713 | remove(); 1714 | remove(); 1715 | 1716 | emitter.emit('test', 10, 20); 1717 | }); 1718 | 1719 | it('remove global empty', function () { 1720 | const emitter = createEventEmitter(); 1721 | 1722 | const remove = emitter.filter(function () { 1723 | assert.fail(); 1724 | }); 1725 | 1726 | emitter.filters.global = []; 1727 | 1728 | remove(); 1729 | 1730 | emitter.emit('test', 10, 20); 1731 | }); 1732 | 1733 | it('remove global missing listener', function () { 1734 | const emitter = createEventEmitter(); 1735 | 1736 | const remove = emitter.filter(function () { 1737 | assert.fail(); 1738 | }); 1739 | 1740 | emitter.filters.global = [ 1741 | function fake() { 1742 | return true; 1743 | } 1744 | ]; 1745 | 1746 | remove(); 1747 | 1748 | emitter.emit('test', 10, 20); 1749 | }); 1750 | }); 1751 | 1752 | describe('asAsyncListener', function () { 1753 | it('undefined', function () { 1754 | const emitter = createEventEmitter(); 1755 | 1756 | let errorFound = false; 1757 | 1758 | try { 1759 | emitter.asAsyncListener(); 1760 | } catch (error) { 1761 | assert.isDefined(error); 1762 | errorFound = true; 1763 | } 1764 | 1765 | assert.isTrue(errorFound); 1766 | }); 1767 | 1768 | it('null', function () { 1769 | const emitter = createEventEmitter(); 1770 | 1771 | let errorFound = false; 1772 | 1773 | try { 1774 | emitter.asAsyncListener(null); 1775 | } catch (error) { 1776 | assert.isDefined(error); 1777 | errorFound = true; 1778 | } 1779 | 1780 | assert.isTrue(errorFound); 1781 | }); 1782 | 1783 | it('not a function', function () { 1784 | const emitter = createEventEmitter(); 1785 | 1786 | let errorFound = false; 1787 | 1788 | try { 1789 | emitter.asAsyncListener('test'); 1790 | } catch (error) { 1791 | assert.isDefined(error); 1792 | errorFound = true; 1793 | } 1794 | 1795 | assert.isTrue(errorFound); 1796 | }); 1797 | 1798 | it('valid', function (done) { 1799 | const emitter = createEventEmitter(); 1800 | 1801 | const asyncDone = emitter.asAsyncListener(done); 1802 | 1803 | assert.isFunction(asyncDone); 1804 | assert.isFalse(asyncDone === done); 1805 | 1806 | asyncDone(); 1807 | }); 1808 | }); 1809 | 1810 | describe('proxyEvents', function () { 1811 | it('missing emitters', function () { 1812 | const emitter = createEventEmitter(); 1813 | 1814 | let errorFound = false; 1815 | 1816 | try { 1817 | emitter.proxyEvents(undefined, 'test'); 1818 | } catch (error) { 1819 | assert.isDefined(error); 1820 | errorFound = true; 1821 | } 1822 | 1823 | assert.isTrue(errorFound); 1824 | }); 1825 | 1826 | it('missing events', function () { 1827 | const emitter = createEventEmitter(); 1828 | 1829 | let errorFound = false; 1830 | 1831 | try { 1832 | emitter.proxyEvents(new EventEmitter()); 1833 | } catch (error) { 1834 | assert.isDefined(error); 1835 | errorFound = true; 1836 | } 1837 | 1838 | assert.isTrue(errorFound); 1839 | }); 1840 | 1841 | it('single emitter, single event', function () { 1842 | const emitter = createEventEmitter(); 1843 | 1844 | const source = new EventEmitter(); 1845 | 1846 | let counter = 0; 1847 | emitter.on('test', function (arg1, arg2) { 1848 | counter++; 1849 | 1850 | assert.strictEqual(arg1, counter); 1851 | assert.strictEqual(arg2, 'b'); 1852 | }); 1853 | 1854 | emitter.on('bad', function () { 1855 | assert.fail(); 1856 | }); 1857 | 1858 | emitter.proxyEvents(source, 'test'); 1859 | 1860 | source.emit('bad'); 1861 | 1862 | const loops = 5; 1863 | for (let index = 0; index < loops; index++) { 1864 | source.emit('test', index + 1, 'b'); 1865 | } 1866 | 1867 | assert.strictEqual(counter, 5); 1868 | }); 1869 | 1870 | it('multiple emitters, single event', function () { 1871 | const emitter = createEventEmitter(); 1872 | 1873 | const source1 = new EventEmitter(); 1874 | const source2 = new EventEmitter(); 1875 | 1876 | let counter = 0; 1877 | emitter.on('test', function (arg1) { 1878 | if (counter % 2) { 1879 | assert.strictEqual(arg1, 'source2'); 1880 | } else { 1881 | assert.strictEqual(arg1, 'source1'); 1882 | } 1883 | 1884 | counter++; 1885 | }); 1886 | 1887 | emitter.onAny([ 1888 | 'bad1', 1889 | 'bad2' 1890 | ], function () { 1891 | assert.fail(); 1892 | }); 1893 | 1894 | emitter.proxyEvents([ 1895 | source1, 1896 | source2 1897 | ], 'test'); 1898 | 1899 | source1.emit('bad1'); 1900 | source2.emit('bad2'); 1901 | 1902 | const loops = 5; 1903 | for (let index = 0; index < loops; index++) { 1904 | source1.emit('test', 'source1'); 1905 | source2.emit('test', 'source2'); 1906 | } 1907 | 1908 | assert.strictEqual(counter, 10); 1909 | }); 1910 | 1911 | it('single emitter, multiple events', function () { 1912 | const emitter = createEventEmitter(); 1913 | 1914 | const source = new EventEmitter(); 1915 | 1916 | let counter = 0; 1917 | emitter.onAny([ 1918 | 'test1', 1919 | 'test2' 1920 | ], function (arg1) { 1921 | if (counter % 2) { 1922 | assert.strictEqual(arg1, 'second'); 1923 | } else { 1924 | assert.strictEqual(arg1, 'first'); 1925 | } 1926 | 1927 | counter++; 1928 | }); 1929 | 1930 | emitter.on('bad', function () { 1931 | assert.fail(); 1932 | }); 1933 | 1934 | emitter.proxyEvents(source, [ 1935 | 'test1', 1936 | 'test2' 1937 | ]); 1938 | 1939 | source.emit('bad'); 1940 | 1941 | const loops = 5; 1942 | for (let index = 0; index < loops; index++) { 1943 | source.emit('test1', 'first'); 1944 | source.emit('test2', 'second'); 1945 | } 1946 | 1947 | assert.strictEqual(counter, 10); 1948 | }); 1949 | 1950 | it('multiple emitter, multiple events', function () { 1951 | const emitter = createEventEmitter(); 1952 | 1953 | const source1 = new EventEmitter(); 1954 | const source2 = new EventEmitter(); 1955 | 1956 | let counter = 0; 1957 | let test1Counter = 0; 1958 | let test2Counter = 0; 1959 | emitter.on('test1', function () { 1960 | test1Counter++; 1961 | }); 1962 | emitter.on('test2', function () { 1963 | test2Counter++; 1964 | }); 1965 | emitter.onAny([ 1966 | 'test1', 1967 | 'test2' 1968 | ], function (arg1) { 1969 | switch (counter % 4) { 1970 | case 0: 1971 | assert.strictEqual(arg1, '1a'); 1972 | break; 1973 | case 1: 1974 | assert.strictEqual(arg1, '1b'); 1975 | break; 1976 | case 2: 1977 | assert.strictEqual(arg1, '2a'); 1978 | break; 1979 | case 3: 1980 | assert.strictEqual(arg1, '2b'); 1981 | break; 1982 | } 1983 | 1984 | counter++; 1985 | }); 1986 | 1987 | emitter.onAny([ 1988 | 'bad1', 1989 | 'bad2' 1990 | ], function () { 1991 | assert.fail(); 1992 | }); 1993 | 1994 | const stop = emitter.proxyEvents([ 1995 | source1, 1996 | source2 1997 | ], [ 1998 | 'test1', 1999 | 'test2' 2000 | ]); 2001 | 2002 | source1.emit('bad1'); 2003 | source2.emit('bad2'); 2004 | 2005 | const loops = 5; 2006 | for (let index = 0; index < loops; index++) { 2007 | source1.emit('test1', '1a'); 2008 | source1.emit('test2', '1b'); 2009 | source2.emit('test1', '2a'); 2010 | source2.emit('test2', '2b'); 2011 | } 2012 | 2013 | assert.strictEqual(test1Counter, 10); 2014 | assert.strictEqual(test2Counter, 10); 2015 | assert.strictEqual(counter, 20); 2016 | 2017 | emitter.onAny([ 2018 | 'test1', 2019 | 'test2' 2020 | ], function () { 2021 | assert.fail(); 2022 | }); 2023 | 2024 | stop(); 2025 | 2026 | source1.emit('test1', 'bad'); 2027 | source1.emit('test2', 'bad'); 2028 | source2.emit('test1', 'bad'); 2029 | source2.emit('test2', 'bad'); 2030 | 2031 | stop(); //check if invoked again, nothing should change 2032 | 2033 | source1.emit('test1', 'bad'); 2034 | source1.emit('test2', 'bad'); 2035 | source2.emit('test1', 'bad'); 2036 | source2.emit('test2', 'bad'); 2037 | }); 2038 | }); 2039 | 2040 | describe('addNoop', function () { 2041 | it('undefined', function () { 2042 | const emitter = createEventEmitter(); 2043 | 2044 | const remove = emitter.addNoop(); 2045 | assert.isUndefined(remove); 2046 | }); 2047 | 2048 | it('null', function () { 2049 | const emitter = createEventEmitter(); 2050 | 2051 | const remove = emitter.addNoop(null); 2052 | assert.isUndefined(remove); 2053 | }); 2054 | 2055 | it('object', function () { 2056 | const emitter = createEventEmitter(); 2057 | 2058 | const remove = emitter.addNoop({}); 2059 | assert.isUndefined(remove); 2060 | }); 2061 | 2062 | it('valid', function () { 2063 | const emitter = createEventEmitter(); 2064 | 2065 | assert.strictEqual(emitter.listenerCount('myevent'), 0); 2066 | const remove = emitter.addNoop('myevent'); 2067 | emitter.addNoop('myevent'); 2068 | emitter.addNoop('myevent'); 2069 | assert.strictEqual(emitter.listenerCount('myevent'), 3); 2070 | 2071 | emitter.emit('myevent', 'test'); 2072 | 2073 | assert.isFunction(remove); 2074 | remove(); 2075 | assert.strictEqual(emitter.listenerCount('myevent'), 2); 2076 | 2077 | emitter.removeAllListeners('myevent'); 2078 | assert.strictEqual(emitter.listenerCount('myevent'), 0); 2079 | }); 2080 | }); 2081 | 2082 | describe('ignoreError', function () { 2083 | it('valid', function () { 2084 | const emitter = createEventEmitter(); 2085 | 2086 | assert.strictEqual(emitter.listenerCount('error'), 0); 2087 | emitter.ignoreError(); 2088 | emitter.ignoreError(); 2089 | emitter.ignoreError(); 2090 | assert.strictEqual(emitter.listenerCount('error'), 1); 2091 | 2092 | emitter.emit('error', new Error('test')); 2093 | 2094 | emitter.removeAllListeners('error'); 2095 | assert.strictEqual(emitter.listenerCount('error'), 0); 2096 | }); 2097 | }); 2098 | 2099 | describe('markEvent', function () { 2100 | it('no input', function () { 2101 | const emitter = createEventEmitter(); 2102 | 2103 | emitter.markEvent(); 2104 | }); 2105 | }); 2106 | 2107 | describe('enhance', function () { 2108 | it('enhance EventEmitter2', function (done) { 2109 | const EventEmitter2 = require('eventemitter2').EventEmitter2; 2110 | const EnhancedEventEmitter2 = EventEmitterEnhancer.extend(EventEmitter2); 2111 | 2112 | const emitter = new EnhancedEventEmitter2({ 2113 | wildcard: false, 2114 | newListener: false, 2115 | maxListeners: 20 2116 | }); 2117 | 2118 | emitter.else(function (type, eventArg1, eventArg2) { 2119 | assert.equal(type, 'test'); 2120 | assert.equal(eventArg1, 1); 2121 | assert.equal(eventArg2, 2); 2122 | assert.equal(arguments.length, 3); 2123 | 2124 | emitter.unelse(this); 2125 | 2126 | let eventDone = false; 2127 | emitter.on('test', function (arg1, arg2) { 2128 | eventDone = true; 2129 | 2130 | assert.equal(arg1, 1); 2131 | assert.equal(arg2, 2); 2132 | }); 2133 | 2134 | emitter.emitAsync('test', 1, 2, function onEmitDone(event, arg1, arg2, emitted) { 2135 | assert.equal(event, 'test'); 2136 | assert.equal(arg1, 1); 2137 | assert.equal(arg2, 2); 2138 | assert.isTrue(emitted); 2139 | 2140 | done(); 2141 | }); 2142 | 2143 | if (eventDone) { 2144 | assert.fail(); 2145 | } 2146 | }); 2147 | 2148 | emitter.emit('test', 1, 2); 2149 | }); 2150 | }); 2151 | 2152 | describe('subscriptionSeparator', function () { 2153 | it('listen to all', function () { 2154 | const emitter = createEventEmitter(); 2155 | const events = {}; 2156 | emitter.on('p1', function (arg1, arg2) { 2157 | events.p1 = true; 2158 | 2159 | assert.strictEqual(arg1, 1); 2160 | assert.strictEqual(arg2, 2); 2161 | }); 2162 | emitter.on('p1:p2', function (arg1, arg2) { 2163 | events['p1:p2'] = true; 2164 | 2165 | assert.strictEqual(arg1, 1); 2166 | assert.strictEqual(arg2, 2); 2167 | }); 2168 | emitter.on('p1:p2:p3', function (arg1, arg2) { 2169 | events['p1:p2:p3'] = true; 2170 | 2171 | assert.strictEqual(arg1, 1); 2172 | assert.strictEqual(arg2, 2); 2173 | }); 2174 | 2175 | emitter.subscriptionSeparator = ':'; 2176 | 2177 | const emitHandled = emitter.emit('p1:p2:p3', 1, 2); 2178 | 2179 | assert.isTrue(emitHandled); 2180 | assert.isTrue(events.p1); 2181 | assert.isTrue(events['p1:p2']); 2182 | assert.isTrue(events['p1:p2:p3']); 2183 | }); 2184 | 2185 | it('listen to none', function () { 2186 | const emitter = createEventEmitter(); 2187 | const events = {}; 2188 | emitter.else(function (type) { 2189 | events[type] = true; 2190 | }); 2191 | 2192 | emitter.subscriptionSeparator = ':'; 2193 | 2194 | const emitHandled = emitter.emit('p1:p2:p3'); 2195 | 2196 | assert.isTrue(emitHandled); 2197 | assert.isTrue(events.p1); 2198 | assert.isTrue(events['p1:p2']); 2199 | assert.isTrue(events['p1:p2:p3']); 2200 | }); 2201 | 2202 | it('listen to some', function () { 2203 | const emitter = createEventEmitter(); 2204 | const events = {}; 2205 | emitter.on('p1', function () { 2206 | events.p1 = 1; 2207 | }); 2208 | emitter.on('p1:p2:p3', function () { 2209 | events['p1:p2:p3'] = 1; 2210 | }); 2211 | emitter.else(function (type) { 2212 | events[type] = 2; 2213 | }); 2214 | 2215 | emitter.subscriptionSeparator = ':'; 2216 | 2217 | const emitHandled = emitter.emit('p1:p2:p3'); 2218 | 2219 | assert.isTrue(emitHandled); 2220 | assert.strictEqual(events.p1, 1); 2221 | assert.strictEqual(events['p1:p2'], 2); 2222 | assert.strictEqual(events['p1:p2:p3'], 1); 2223 | }); 2224 | 2225 | it('not handled', function () { 2226 | const emitter = createEventEmitter(); 2227 | 2228 | emitter.subscriptionSeparator = ':'; 2229 | 2230 | const emitHandled = emitter.emit('p1:p2:p3'); 2231 | 2232 | assert.isFalse(emitHandled); 2233 | }); 2234 | }); 2235 | 2236 | describe('doEmitByPath', function () { 2237 | it('no separator', function () { 2238 | const emitter = createEventEmitter(); 2239 | emitter.on('p1:p2:p3', function () { 2240 | assert.fail(); 2241 | }); 2242 | 2243 | const emitHandled = emitter.doEmitByPath('p1:p2:p3'); 2244 | 2245 | assert.isFalse(emitHandled); 2246 | }); 2247 | 2248 | it('listen to all', function () { 2249 | const emitter = createEventEmitter(); 2250 | const events = {}; 2251 | emitter.on('p1', function (arg1, arg2) { 2252 | events.p1 = true; 2253 | 2254 | assert.strictEqual(arg1, 1); 2255 | assert.strictEqual(arg2, 2); 2256 | }); 2257 | emitter.on('p1:p2', function (arg1, arg2) { 2258 | events['p1:p2'] = true; 2259 | 2260 | assert.strictEqual(arg1, 1); 2261 | assert.strictEqual(arg2, 2); 2262 | }); 2263 | emitter.on('p1:p2:p3', function (arg1, arg2) { 2264 | events['p1:p2:p3'] = true; 2265 | 2266 | assert.strictEqual(arg1, 1); 2267 | assert.strictEqual(arg2, 2); 2268 | }); 2269 | 2270 | emitter.subscriptionSeparator = ':'; 2271 | 2272 | const emitHandled = emitter.doEmitByPath('p1:p2:p3', 1, 2); 2273 | 2274 | assert.isTrue(emitHandled); 2275 | assert.isTrue(events.p1); 2276 | assert.isTrue(events['p1:p2']); 2277 | assert.isTrue(events['p1:p2:p3']); 2278 | }); 2279 | 2280 | it('listen to none', function () { 2281 | const emitter = createEventEmitter(); 2282 | const events = {}; 2283 | emitter.else(function (type) { 2284 | events[type] = true; 2285 | }); 2286 | 2287 | emitter.subscriptionSeparator = ':'; 2288 | 2289 | const emitHandled = emitter.doEmitByPath('p1:p2:p3'); 2290 | 2291 | assert.isTrue(emitHandled); 2292 | assert.isTrue(events.p1); 2293 | assert.isTrue(events['p1:p2']); 2294 | assert.isTrue(events['p1:p2:p3']); 2295 | }); 2296 | 2297 | it('listen to some', function () { 2298 | const emitter = createEventEmitter(); 2299 | const events = {}; 2300 | emitter.on('p1', function () { 2301 | events.p1 = 1; 2302 | }); 2303 | emitter.on('p1:p2:p3', function () { 2304 | events['p1:p2:p3'] = 1; 2305 | }); 2306 | emitter.else(function (type) { 2307 | events[type] = 2; 2308 | }); 2309 | 2310 | emitter.subscriptionSeparator = ':'; 2311 | 2312 | const emitHandled = emitter.doEmitByPath('p1:p2:p3'); 2313 | 2314 | assert.isTrue(emitHandled); 2315 | assert.strictEqual(events.p1, 1); 2316 | assert.strictEqual(events['p1:p2'], 2); 2317 | assert.strictEqual(events['p1:p2:p3'], 1); 2318 | }); 2319 | 2320 | it('not handled', function () { 2321 | const emitter = createEventEmitter(); 2322 | 2323 | emitter.subscriptionSeparator = ':'; 2324 | 2325 | const emitHandled = emitter.doEmitByPath('p1:p2:p3'); 2326 | 2327 | assert.isFalse(emitHandled); 2328 | }); 2329 | }); 2330 | }); 2331 | --------------------------------------------------------------------------------