├── .gitignore ├── .npmrc ├── security.md ├── .airtap.yml ├── tests ├── legacy-compat.js ├── errors.js ├── symbols.js ├── events-list.js ├── prepend.js ├── set-max-listeners-side-effects.js ├── special-event-names.js ├── method-names.js ├── listener-count.js ├── index.js ├── max-listeners.js ├── subclass.js ├── listeners-side-effects.js ├── num-args.js ├── once.js ├── modify-in-emit.js ├── common.js ├── check-listener-leaks.js ├── add-listeners.js ├── remove-all-listeners.js ├── listeners.js ├── events-once.js └── remove-listeners.js ├── .github └── FUNDING.yml ├── .travis.yml ├── package.json ├── LICENSE ├── Readme.md ├── History.md └── events.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /security.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | Only the latest major version is supported at any given time. 5 | 6 | ## Reporting a Vulnerability 7 | 8 | To report a security vulnerability, please use the 9 | [Tidelift security contact](https://tidelift.com/security). 10 | Tidelift will coordinate the fix and disclosure. 11 | -------------------------------------------------------------------------------- /.airtap.yml: -------------------------------------------------------------------------------- 1 | sauce_connect: true 2 | loopback: airtap.local 3 | browsers: 4 | - name: chrome 5 | version: latest 6 | - name: firefox 7 | version: latest 8 | - name: safari 9 | version: 9..latest 10 | - name: iphone 11 | version: latest 12 | - name: ie 13 | version: 9..latest 14 | - name: microsoftedge 15 | version: 13..latest 16 | -------------------------------------------------------------------------------- /tests/legacy-compat.js: -------------------------------------------------------------------------------- 1 | // sigh... life is hard 2 | if (!global.console) { 3 | console = {} 4 | } 5 | 6 | var fns = ['log', 'error', 'trace']; 7 | for (var i=0 ; i (http://jeditoolkit.com)", 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/browserify/events.git", 15 | "web": "https://github.com/browserify/events" 16 | }, 17 | "bugs": { 18 | "url": "http://github.com/browserify/events/issues/" 19 | }, 20 | "main": "./events.js", 21 | "engines": { 22 | "node": ">=0.8.x" 23 | }, 24 | "devDependencies": { 25 | "airtap": "^1.0.0", 26 | "functions-have-names": "^1.2.1", 27 | "has": "^1.0.3", 28 | "has-symbols": "^1.0.1", 29 | "isarray": "^2.0.5", 30 | "tape": "^5.0.0" 31 | }, 32 | "scripts": { 33 | "test": "node tests/index.js", 34 | "test:browsers": "airtap -- tests/index.js" 35 | }, 36 | "license": "MIT" 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT 2 | 3 | Copyright Joyent, Inc. and other Node contributors. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a 6 | copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to permit 10 | persons to whom the Software is furnished to do so, subject to the 11 | following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 19 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 21 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 22 | USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /tests/set-max-listeners-side-effects.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | require('./common'); 23 | var assert = require('assert'); 24 | var events = require('../'); 25 | 26 | var e = new events.EventEmitter(); 27 | 28 | if (Object.create) assert.ok(!(e._events instanceof Object)); 29 | assert.strictEqual(Object.keys(e._events).length, 0); 30 | e.setMaxListeners(5); 31 | assert.strictEqual(Object.keys(e._events).length, 0); 32 | -------------------------------------------------------------------------------- /tests/special-event-names.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var common = require('./common'); 4 | var EventEmitter = require('../'); 5 | var assert = require('assert'); 6 | 7 | var ee = new EventEmitter(); 8 | var handler = function() {}; 9 | 10 | assert.strictEqual(ee.eventNames().length, 0); 11 | 12 | assert.strictEqual(ee._events.hasOwnProperty, undefined); 13 | assert.strictEqual(ee._events.toString, undefined); 14 | 15 | ee.on('__defineGetter__', handler); 16 | ee.on('toString', handler); 17 | ee.on('__proto__', handler); 18 | 19 | assert.strictEqual(ee.eventNames()[0], '__defineGetter__'); 20 | assert.strictEqual(ee.eventNames()[1], 'toString'); 21 | 22 | assert.strictEqual(ee.listeners('__defineGetter__').length, 1); 23 | assert.strictEqual(ee.listeners('__defineGetter__')[0], handler); 24 | assert.strictEqual(ee.listeners('toString').length, 1); 25 | assert.strictEqual(ee.listeners('toString')[0], handler); 26 | 27 | // Only run __proto__ tests if that property can actually be set 28 | if ({ __proto__: 'ok' }.__proto__ === 'ok') { 29 | assert.strictEqual(ee.eventNames().length, 3); 30 | assert.strictEqual(ee.eventNames()[2], '__proto__'); 31 | assert.strictEqual(ee.listeners('__proto__').length, 1); 32 | assert.strictEqual(ee.listeners('__proto__')[0], handler); 33 | 34 | ee.on('__proto__', common.mustCall(function(val) { 35 | assert.strictEqual(val, 1); 36 | })); 37 | ee.emit('__proto__', 1); 38 | 39 | process.on('__proto__', common.mustCall(function(val) { 40 | assert.strictEqual(val, 1); 41 | })); 42 | process.emit('__proto__', 1); 43 | } else { 44 | console.log('# skipped __proto__') 45 | } 46 | -------------------------------------------------------------------------------- /tests/method-names.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 'use strict'; 23 | require('./common'); 24 | var assert = require('assert'); 25 | var events = require('../'); 26 | 27 | var E = events.EventEmitter.prototype; 28 | assert.strictEqual(E.constructor.name, 'EventEmitter'); 29 | assert.strictEqual(E.on, E.addListener); // Same method. 30 | assert.strictEqual(E.off, E.removeListener); // Same method. 31 | Object.getOwnPropertyNames(E).forEach(function(name) { 32 | if (name === 'constructor' || name === 'on' || name === 'off') return; 33 | if (typeof E[name] !== 'function') return; 34 | assert.strictEqual(E[name].name, name); 35 | }); 36 | -------------------------------------------------------------------------------- /tests/listener-count.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | require('./common'); 23 | var assert = require('assert'); 24 | var EventEmitter = require('../'); 25 | 26 | var emitter = new EventEmitter(); 27 | emitter.on('foo', function() {}); 28 | emitter.on('foo', function() {}); 29 | emitter.on('baz', function() {}); 30 | // Allow any type 31 | emitter.on(123, function() {}); 32 | 33 | assert.strictEqual(EventEmitter.listenerCount(emitter, 'foo'), 2); 34 | assert.strictEqual(emitter.listenerCount('foo'), 2); 35 | assert.strictEqual(emitter.listenerCount('bar'), 0); 36 | assert.strictEqual(emitter.listenerCount('baz'), 1); 37 | assert.strictEqual(emitter.listenerCount(123), 1); 38 | -------------------------------------------------------------------------------- /tests/index.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var functionsHaveNames = require('functions-have-names'); 3 | var hasSymbols = require('has-symbols'); 4 | 5 | require('./legacy-compat'); 6 | var common = require('./common'); 7 | 8 | // we do this to easily wrap each file in a mocha test 9 | // and also have browserify be able to statically analyze this file 10 | var orig_require = require; 11 | var require = function(file) { 12 | test(file, function(t) { 13 | // Store the tape object so tests can access it. 14 | t.on('end', function () { delete common.test; }); 15 | common.test = t; 16 | 17 | try { 18 | var exp = orig_require(file); 19 | if (exp && exp.then) { 20 | exp.then(function () { t.end(); }, t.fail); 21 | return; 22 | } 23 | } catch (err) { 24 | t.fail(err); 25 | } 26 | t.end(); 27 | }); 28 | }; 29 | 30 | require('./add-listeners.js'); 31 | require('./check-listener-leaks.js'); 32 | require('./errors.js'); 33 | require('./events-list.js'); 34 | if (typeof Promise === 'function') { 35 | require('./events-once.js'); 36 | } else { 37 | // Promise support is not available. 38 | test('./events-once.js', { skip: true }, function () {}); 39 | } 40 | require('./listener-count.js'); 41 | require('./listeners-side-effects.js'); 42 | require('./listeners.js'); 43 | require('./max-listeners.js'); 44 | if (functionsHaveNames()) { 45 | require('./method-names.js'); 46 | } else { 47 | // Function.name is not supported in IE 48 | test('./method-names.js', { skip: true }, function () {}); 49 | } 50 | require('./modify-in-emit.js'); 51 | require('./num-args.js'); 52 | require('./once.js'); 53 | require('./prepend.js'); 54 | require('./set-max-listeners-side-effects.js'); 55 | require('./special-event-names.js'); 56 | require('./subclass.js'); 57 | if (hasSymbols()) { 58 | require('./symbols.js'); 59 | } else { 60 | // Symbol is not available. 61 | test('./symbols.js', { skip: true }, function () {}); 62 | } 63 | require('./remove-all-listeners.js'); 64 | require('./remove-listeners.js'); 65 | -------------------------------------------------------------------------------- /tests/max-listeners.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | var common = require('./common'); 23 | var assert = require('assert'); 24 | var events = require('../'); 25 | var e = new events.EventEmitter(); 26 | 27 | var hasDefineProperty = !!Object.defineProperty; 28 | try { Object.defineProperty({}, 'x', { value: 0 }); } catch (err) { hasDefineProperty = false } 29 | 30 | e.on('maxListeners', common.mustCall()); 31 | 32 | // Should not corrupt the 'maxListeners' queue. 33 | e.setMaxListeners(42); 34 | 35 | var throwsObjs = [NaN, -1, 'and even this']; 36 | var maxError = /^RangeError: The value of "n" is out of range\. It must be a non-negative number\./; 37 | var defError = /^RangeError: The value of "defaultMaxListeners" is out of range\. It must be a non-negative number\./; 38 | 39 | for (var i = 0; i < throwsObjs.length; i++) { 40 | var obj = throwsObjs[i]; 41 | assert.throws(function() { e.setMaxListeners(obj); }, maxError); 42 | if (hasDefineProperty) { 43 | assert.throws(function() { events.defaultMaxListeners = obj; }, defError); 44 | } 45 | } 46 | 47 | e.emit('maxListeners'); 48 | -------------------------------------------------------------------------------- /tests/subclass.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | var common = require('./common'); 23 | var test = require('tape'); 24 | var assert = require('assert'); 25 | var EventEmitter = require('../').EventEmitter; 26 | var util = require('util'); 27 | 28 | util.inherits(MyEE, EventEmitter); 29 | 30 | function MyEE(cb) { 31 | this.once(1, cb); 32 | this.emit(1); 33 | this.removeAllListeners(); 34 | EventEmitter.call(this); 35 | } 36 | 37 | var myee = new MyEE(common.mustCall()); 38 | 39 | 40 | util.inherits(ErrorEE, EventEmitter); 41 | function ErrorEE() { 42 | this.emit('error', new Error('blerg')); 43 | } 44 | 45 | assert.throws(function() { 46 | new ErrorEE(); 47 | }, /blerg/); 48 | 49 | test.onFinish(function() { 50 | assert.ok(!(myee._events instanceof Object)); 51 | assert.strictEqual(Object.keys(myee._events).length, 0); 52 | }); 53 | 54 | 55 | function MyEE2() { 56 | EventEmitter.call(this); 57 | } 58 | 59 | MyEE2.prototype = new EventEmitter(); 60 | 61 | var ee1 = new MyEE2(); 62 | var ee2 = new MyEE2(); 63 | 64 | ee1.on('x', function() {}); 65 | 66 | assert.strictEqual(ee2.listenerCount('x'), 0); 67 | -------------------------------------------------------------------------------- /tests/listeners-side-effects.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | require('./common'); 23 | var assert = require('assert'); 24 | 25 | var EventEmitter = require('../').EventEmitter; 26 | 27 | var e = new EventEmitter(); 28 | var fl; // foo listeners 29 | 30 | fl = e.listeners('foo'); 31 | assert.ok(Array.isArray(fl)); 32 | assert.strictEqual(fl.length, 0); 33 | if (Object.create) assert.ok(!(e._events instanceof Object)); 34 | assert.strictEqual(Object.keys(e._events).length, 0); 35 | 36 | e.on('foo', assert.fail); 37 | fl = e.listeners('foo'); 38 | assert.strictEqual(e._events.foo, assert.fail); 39 | assert.ok(Array.isArray(fl)); 40 | assert.strictEqual(fl.length, 1); 41 | assert.strictEqual(fl[0], assert.fail); 42 | 43 | e.listeners('bar'); 44 | 45 | e.on('foo', assert.ok); 46 | fl = e.listeners('foo'); 47 | 48 | assert.ok(Array.isArray(e._events.foo)); 49 | assert.strictEqual(e._events.foo.length, 2); 50 | assert.strictEqual(e._events.foo[0], assert.fail); 51 | assert.strictEqual(e._events.foo[1], assert.ok); 52 | 53 | assert.ok(Array.isArray(fl)); 54 | assert.strictEqual(fl.length, 2); 55 | assert.strictEqual(fl[0], assert.fail); 56 | assert.strictEqual(fl[1], assert.ok); 57 | -------------------------------------------------------------------------------- /tests/num-args.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | require('./common'); 23 | var assert = require('assert'); 24 | var events = require('../'); 25 | 26 | var e = new events.EventEmitter(); 27 | var num_args_emitted = []; 28 | 29 | e.on('numArgs', function() { 30 | var numArgs = arguments.length; 31 | num_args_emitted.push(numArgs); 32 | }); 33 | 34 | e.on('foo', function() { 35 | num_args_emitted.push(arguments.length); 36 | }); 37 | 38 | e.on('foo', function() { 39 | num_args_emitted.push(arguments.length); 40 | }); 41 | 42 | e.emit('numArgs'); 43 | e.emit('numArgs', null); 44 | e.emit('numArgs', null, null); 45 | e.emit('numArgs', null, null, null); 46 | e.emit('numArgs', null, null, null, null); 47 | e.emit('numArgs', null, null, null, null, null); 48 | 49 | e.emit('foo', null, null, null, null); 50 | 51 | assert.ok(Array.isArray(num_args_emitted)); 52 | assert.strictEqual(num_args_emitted.length, 8); 53 | assert.strictEqual(num_args_emitted[0], 0); 54 | assert.strictEqual(num_args_emitted[1], 1); 55 | assert.strictEqual(num_args_emitted[2], 2); 56 | assert.strictEqual(num_args_emitted[3], 3); 57 | assert.strictEqual(num_args_emitted[4], 4); 58 | assert.strictEqual(num_args_emitted[5], 5); 59 | assert.strictEqual(num_args_emitted[6], 4); 60 | assert.strictEqual(num_args_emitted[6], 4); 61 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # events [![Build Status](https://travis-ci.org/browserify/events.png?branch=main)](https://travis-ci.org/browserify/events) 2 | 3 | > Node's event emitter for all engines. 4 | 5 | This implements the Node.js [`events`][node.js docs] module for environments that do not have it, like browsers. 6 | 7 | > `events` currently matches the **Node.js 11.13.0** API. 8 | 9 | Note that the `events` module uses ES5 features. If you need to support very old browsers like IE8, use a shim like [`es5-shim`](https://www.npmjs.com/package/es5-shim). You need both the shim and the sham versions of `es5-shim`. 10 | 11 | This module is maintained, but only by very few people. If you'd like to help, let us know in the [Maintainer Needed](https://github.com/browserify/events/issues/43) issue! 12 | 13 | ## Install 14 | 15 | You usually do not have to install `events` yourself! If your code runs in Node.js, `events` is built in. If your code runs in the browser, bundlers like [browserify](https://github.com/browserify/browserify) or [webpack](https://github.com/webpack/webpack) also include the `events` module. 16 | 17 | But if none of those apply, with npm do: 18 | 19 | ``` 20 | npm install events 21 | ``` 22 | 23 | ## Usage 24 | 25 | ```javascript 26 | var EventEmitter = require('events') 27 | 28 | var ee = new EventEmitter() 29 | ee.on('message', function (text) { 30 | console.log(text) 31 | }) 32 | ee.emit('message', 'hello world') 33 | ``` 34 | 35 | ## API 36 | 37 | See the [Node.js EventEmitter docs][node.js docs]. `events` currently matches the Node.js 11.13.0 API. 38 | 39 | ## Contributing 40 | 41 | PRs are very welcome! The main way to contribute to `events` is by porting features, bugfixes and tests from Node.js. Ideally, code contributions to this module are copy-pasted from Node.js and transpiled to ES5, rather than reimplemented from scratch. Matching the Node.js code as closely as possible makes maintenance simpler when new changes land in Node.js. 42 | This module intends to provide exactly the same API as Node.js, so features that are not available in the core `events` module will not be accepted. Feature requests should instead be directed at [nodejs/node](https://github.com/nodejs/node) and will be added to this module once they are implemented in Node.js. 43 | 44 | If there is a difference in behaviour between Node.js's `events` module and this module, please open an issue! 45 | 46 | ## License 47 | 48 | [MIT](./LICENSE) 49 | 50 | [node.js docs]: https://nodejs.org/dist/v11.13.0/docs/api/events.html 51 | -------------------------------------------------------------------------------- /tests/once.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | var common = require('./common'); 23 | var assert = require('assert'); 24 | var EventEmitter = require('../'); 25 | 26 | var e = new EventEmitter(); 27 | 28 | e.once('hello', common.mustCall()); 29 | 30 | e.emit('hello', 'a', 'b'); 31 | e.emit('hello', 'a', 'b'); 32 | e.emit('hello', 'a', 'b'); 33 | e.emit('hello', 'a', 'b'); 34 | 35 | function remove() { 36 | assert.fail('once->foo should not be emitted'); 37 | } 38 | 39 | e.once('foo', remove); 40 | e.removeListener('foo', remove); 41 | e.emit('foo'); 42 | 43 | e.once('e', common.mustCall(function() { 44 | e.emit('e'); 45 | })); 46 | 47 | e.once('e', common.mustCall()); 48 | 49 | e.emit('e'); 50 | 51 | // Verify that the listener must be a function 52 | assert.throws(function() { 53 | var ee = new EventEmitter(); 54 | 55 | ee.once('foo', null); 56 | }, /^TypeError: The "listener" argument must be of type Function. Received type object$/); 57 | 58 | { 59 | // once() has different code paths based on the number of arguments being 60 | // emitted. Verify that all of the cases are covered. 61 | var maxArgs = 4; 62 | 63 | for (var i = 0; i <= maxArgs; ++i) { 64 | var ee = new EventEmitter(); 65 | var args = ['foo']; 66 | 67 | for (var j = 0; j < i; ++j) 68 | args.push(j); 69 | 70 | ee.once('foo', common.mustCall(function() { 71 | var params = Array.prototype.slice.call(arguments); 72 | var restArgs = args.slice(1); 73 | assert.ok(Array.isArray(params)); 74 | assert.strictEqual(params.length, restArgs.length); 75 | for (var index = 0; index < params.length; index++) { 76 | var param = params[index]; 77 | assert.strictEqual(param, restArgs[index]); 78 | } 79 | })); 80 | 81 | EventEmitter.prototype.emit.apply(ee, args); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /tests/modify-in-emit.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | require('./common'); 23 | var assert = require('assert'); 24 | var events = require('../'); 25 | 26 | var callbacks_called = []; 27 | 28 | var e = new events.EventEmitter(); 29 | 30 | function callback1() { 31 | callbacks_called.push('callback1'); 32 | e.on('foo', callback2); 33 | e.on('foo', callback3); 34 | e.removeListener('foo', callback1); 35 | } 36 | 37 | function callback2() { 38 | callbacks_called.push('callback2'); 39 | e.removeListener('foo', callback2); 40 | } 41 | 42 | function callback3() { 43 | callbacks_called.push('callback3'); 44 | e.removeListener('foo', callback3); 45 | } 46 | 47 | e.on('foo', callback1); 48 | assert.strictEqual(e.listeners('foo').length, 1); 49 | 50 | e.emit('foo'); 51 | assert.strictEqual(e.listeners('foo').length, 2); 52 | assert.ok(Array.isArray(callbacks_called)); 53 | assert.strictEqual(callbacks_called.length, 1); 54 | assert.strictEqual(callbacks_called[0], 'callback1'); 55 | 56 | e.emit('foo'); 57 | assert.strictEqual(e.listeners('foo').length, 0); 58 | assert.ok(Array.isArray(callbacks_called)); 59 | assert.strictEqual(callbacks_called.length, 3); 60 | assert.strictEqual(callbacks_called[0], 'callback1'); 61 | assert.strictEqual(callbacks_called[1], 'callback2'); 62 | assert.strictEqual(callbacks_called[2], 'callback3'); 63 | 64 | e.emit('foo'); 65 | assert.strictEqual(e.listeners('foo').length, 0); 66 | assert.ok(Array.isArray(callbacks_called)); 67 | assert.strictEqual(callbacks_called.length, 3); 68 | assert.strictEqual(callbacks_called[0], 'callback1'); 69 | assert.strictEqual(callbacks_called[1], 'callback2'); 70 | assert.strictEqual(callbacks_called[2], 'callback3'); 71 | 72 | e.on('foo', callback1); 73 | e.on('foo', callback2); 74 | assert.strictEqual(e.listeners('foo').length, 2); 75 | e.removeAllListeners('foo'); 76 | assert.strictEqual(e.listeners('foo').length, 0); 77 | 78 | // Verify that removing callbacks while in emit allows emits to propagate to 79 | // all listeners 80 | callbacks_called = []; 81 | 82 | e.on('foo', callback2); 83 | e.on('foo', callback3); 84 | assert.strictEqual(2, e.listeners('foo').length); 85 | e.emit('foo'); 86 | assert.ok(Array.isArray(callbacks_called)); 87 | assert.strictEqual(callbacks_called.length, 2); 88 | assert.strictEqual(callbacks_called[0], 'callback2'); 89 | assert.strictEqual(callbacks_called[1], 'callback3'); 90 | assert.strictEqual(0, e.listeners('foo').length); 91 | -------------------------------------------------------------------------------- /tests/common.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | var test = require('tape'); 23 | var assert = require('assert'); 24 | 25 | var noop = function() {}; 26 | 27 | var mustCallChecks = []; 28 | 29 | function runCallChecks(exitCode) { 30 | if (exitCode !== 0) return; 31 | 32 | var failed = filter(mustCallChecks, function(context) { 33 | if ('minimum' in context) { 34 | context.messageSegment = 'at least ' + context.minimum; 35 | return context.actual < context.minimum; 36 | } else { 37 | context.messageSegment = 'exactly ' + context.exact; 38 | return context.actual !== context.exact; 39 | } 40 | }); 41 | 42 | for (var i = 0; i < failed.length; i++) { 43 | var context = failed[i]; 44 | console.log('Mismatched %s function calls. Expected %s, actual %d.', 45 | context.name, 46 | context.messageSegment, 47 | context.actual); 48 | // IE8 has no .stack 49 | if (context.stack) console.log(context.stack.split('\n').slice(2).join('\n')); 50 | } 51 | 52 | assert.strictEqual(failed.length, 0); 53 | } 54 | 55 | exports.mustCall = function(fn, exact) { 56 | return _mustCallInner(fn, exact, 'exact'); 57 | }; 58 | 59 | function _mustCallInner(fn, criteria, field) { 60 | if (typeof criteria == 'undefined') criteria = 1; 61 | 62 | if (typeof fn === 'number') { 63 | criteria = fn; 64 | fn = noop; 65 | } else if (fn === undefined) { 66 | fn = noop; 67 | } 68 | 69 | if (typeof criteria !== 'number') 70 | throw new TypeError('Invalid ' + field + ' value: ' + criteria); 71 | 72 | var context = { 73 | actual: 0, 74 | stack: (new Error()).stack, 75 | name: fn.name || '' 76 | }; 77 | 78 | context[field] = criteria; 79 | 80 | // add the exit listener only once to avoid listener leak warnings 81 | if (mustCallChecks.length === 0) test.onFinish(function() { runCallChecks(0); }); 82 | 83 | mustCallChecks.push(context); 84 | 85 | return function() { 86 | context.actual++; 87 | return fn.apply(this, arguments); 88 | }; 89 | } 90 | 91 | exports.mustNotCall = function(msg) { 92 | return function mustNotCall() { 93 | assert.fail(msg || 'function should not have been called'); 94 | }; 95 | }; 96 | 97 | function filter(arr, fn) { 98 | if (arr.filter) return arr.filter(fn); 99 | var filtered = []; 100 | for (var i = 0; i < arr.length; i++) { 101 | if (fn(arr[i], i, arr)) filtered.push(arr[i]); 102 | } 103 | return filtered 104 | } 105 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | # 3.3.0 2 | 3 | - Support EventTarget emitters in `events.once` from Node.js 12.11.0. 4 | 5 | Now you can use the `events.once` function with objects that implement the EventTarget interface. This interface is used widely in 6 | the DOM and other web APIs. 7 | 8 | ```js 9 | var events = require('events'); 10 | var assert = require('assert'); 11 | 12 | async function connect() { 13 | var ws = new WebSocket('wss://example.com'); 14 | await events.once(ws, 'open'); 15 | assert(ws.readyState === WebSocket.OPEN); 16 | } 17 | 18 | async function onClick() { 19 | await events.once(document.body, 'click'); 20 | alert('you clicked the page!'); 21 | } 22 | ``` 23 | 24 | # 3.2.0 25 | 26 | - Add `events.once` from Node.js 11.13.0. 27 | 28 | To use this function, Promises must be supported in the environment. Use a polyfill like `es6-promise` if you support older browsers. 29 | 30 | # 3.1.0 (2020-01-08) 31 | 32 | `events` now matches the Node.js 11.12.0 API. 33 | 34 | - pass through return value in wrapped `emitter.once()` listeners 35 | 36 | Now, this works: 37 | ```js 38 | emitter.once('myevent', function () { return 1; }); 39 | var listener = emitter.rawListeners('myevent')[0] 40 | assert(listener() === 1); 41 | ``` 42 | Previously, `listener()` would return undefined regardless of the implementation. 43 | 44 | Ported from https://github.com/nodejs/node/commit/acc506c2d2771dab8d7bba6d3452bc5180dff7cf 45 | 46 | - Reduce code duplication in listener type check ([#67](https://github.com/Gozala/events/pull/67) by [@friederbluemle](https://github.com/friederbluemle)). 47 | - Improve `emitter.once()` performance in some engines 48 | 49 | # 3.0.0 (2018-05-25) 50 | 51 | **This version drops support for IE8.** `events` no longer includes polyfills 52 | for ES5 features. If you need to support older environments, use an ES5 shim 53 | like [es5-shim](https://npmjs.com/package/es5-shim). Both the shim and sham 54 | versions of es5-shim are necessary. 55 | 56 | - Update to events code from Node.js 10.x 57 | - (semver major) Adds `off()` method 58 | - Port more tests from Node.js 59 | - Switch browser tests to airtap, making things more reliable 60 | 61 | # 2.1.0 (2018-05-25) 62 | 63 | - add Emitter#rawListeners from Node.js v9.4 64 | 65 | # 2.0.0 (2018-02-02) 66 | 67 | - Update to events code from node.js 8.x 68 | - Adds `prependListener()` and `prependOnceListener()` 69 | - Adds `eventNames()` method 70 | - (semver major) Unwrap `once()` listeners in `listeners()` 71 | - copy tests from node.js 72 | 73 | Note that this version doubles the gzipped size, jumping from 1.1KB to 2.1KB, 74 | due to new methods and runtime performance improvements. Be aware of that when 75 | upgrading. 76 | 77 | # 1.1.1 (2016-06-22) 78 | 79 | - add more context to errors if they are not instanceof Error 80 | 81 | # 1.1.0 (2015-09-29) 82 | 83 | - add Emitter#listerCount (to match node v4 api) 84 | 85 | # 1.0.2 (2014-08-28) 86 | 87 | - remove un-reachable code 88 | - update devDeps 89 | 90 | ## 1.0.1 / 2014-05-11 91 | 92 | - check for console.trace before using it 93 | 94 | ## 1.0.0 / 2013-12-10 95 | 96 | - Update to latest events code from node.js 0.10 97 | - copy tests from node.js 98 | 99 | ## 0.4.0 / 2011-07-03 ## 100 | 101 | - Switching to graphquire@0.8.0 102 | 103 | ## 0.3.0 / 2011-07-03 ## 104 | 105 | - Switching to URL based module require. 106 | 107 | ## 0.2.0 / 2011-06-10 ## 108 | 109 | - Simplified package structure. 110 | - Graphquire for dependency management. 111 | 112 | ## 0.1.1 / 2011-05-16 ## 113 | 114 | - Unhandled errors are logged via console.error 115 | 116 | ## 0.1.0 / 2011-04-22 ## 117 | 118 | - Initial release 119 | -------------------------------------------------------------------------------- /tests/check-listener-leaks.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | var common = require('./common'); 23 | var assert = require('assert'); 24 | var events = require('../'); 25 | 26 | // Redirect warning output to tape. 27 | var consoleWarn = console.warn; 28 | console.warn = common.test.comment; 29 | 30 | common.test.on('end', function () { 31 | console.warn = consoleWarn; 32 | }); 33 | 34 | // default 35 | { 36 | var e = new events.EventEmitter(); 37 | 38 | for (var i = 0; i < 10; i++) { 39 | e.on('default', common.mustNotCall()); 40 | } 41 | assert.ok(!e._events['default'].hasOwnProperty('warned')); 42 | e.on('default', common.mustNotCall()); 43 | assert.ok(e._events['default'].warned); 44 | 45 | // specific 46 | e.setMaxListeners(5); 47 | for (var i = 0; i < 5; i++) { 48 | e.on('specific', common.mustNotCall()); 49 | } 50 | assert.ok(!e._events['specific'].hasOwnProperty('warned')); 51 | e.on('specific', common.mustNotCall()); 52 | assert.ok(e._events['specific'].warned); 53 | 54 | // only one 55 | e.setMaxListeners(1); 56 | e.on('only one', common.mustNotCall()); 57 | assert.ok(!e._events['only one'].hasOwnProperty('warned')); 58 | e.on('only one', common.mustNotCall()); 59 | assert.ok(e._events['only one'].hasOwnProperty('warned')); 60 | 61 | // unlimited 62 | e.setMaxListeners(0); 63 | for (var i = 0; i < 1000; i++) { 64 | e.on('unlimited', common.mustNotCall()); 65 | } 66 | assert.ok(!e._events['unlimited'].hasOwnProperty('warned')); 67 | } 68 | 69 | // process-wide 70 | { 71 | events.EventEmitter.defaultMaxListeners = 42; 72 | var e = new events.EventEmitter(); 73 | 74 | for (var i = 0; i < 42; ++i) { 75 | e.on('fortytwo', common.mustNotCall()); 76 | } 77 | assert.ok(!e._events['fortytwo'].hasOwnProperty('warned')); 78 | e.on('fortytwo', common.mustNotCall()); 79 | assert.ok(e._events['fortytwo'].hasOwnProperty('warned')); 80 | delete e._events['fortytwo'].warned; 81 | 82 | events.EventEmitter.defaultMaxListeners = 44; 83 | e.on('fortytwo', common.mustNotCall()); 84 | assert.ok(!e._events['fortytwo'].hasOwnProperty('warned')); 85 | e.on('fortytwo', common.mustNotCall()); 86 | assert.ok(e._events['fortytwo'].hasOwnProperty('warned')); 87 | } 88 | 89 | // but _maxListeners still has precedence over defaultMaxListeners 90 | { 91 | events.EventEmitter.defaultMaxListeners = 42; 92 | var e = new events.EventEmitter(); 93 | e.setMaxListeners(1); 94 | e.on('uno', common.mustNotCall()); 95 | assert.ok(!e._events['uno'].hasOwnProperty('warned')); 96 | e.on('uno', common.mustNotCall()); 97 | assert.ok(e._events['uno'].hasOwnProperty('warned')); 98 | 99 | // chainable 100 | assert.strictEqual(e, e.setMaxListeners(1)); 101 | } 102 | -------------------------------------------------------------------------------- /tests/add-listeners.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | var common = require('./common'); 23 | var assert = require('assert'); 24 | var EventEmitter = require('../'); 25 | 26 | { 27 | var ee = new EventEmitter(); 28 | var events_new_listener_emitted = []; 29 | var listeners_new_listener_emitted = []; 30 | 31 | // Sanity check 32 | assert.strictEqual(ee.addListener, ee.on); 33 | 34 | ee.on('newListener', function(event, listener) { 35 | // Don't track newListener listeners. 36 | if (event === 'newListener') 37 | return; 38 | 39 | events_new_listener_emitted.push(event); 40 | listeners_new_listener_emitted.push(listener); 41 | }); 42 | 43 | var hello = common.mustCall(function(a, b) { 44 | assert.strictEqual('a', a); 45 | assert.strictEqual('b', b); 46 | }); 47 | 48 | ee.once('newListener', function(name, listener) { 49 | assert.strictEqual(name, 'hello'); 50 | assert.strictEqual(listener, hello); 51 | 52 | var listeners = this.listeners('hello'); 53 | assert.ok(Array.isArray(listeners)); 54 | assert.strictEqual(listeners.length, 0); 55 | }); 56 | 57 | ee.on('hello', hello); 58 | ee.once('foo', assert.fail); 59 | 60 | assert.ok(Array.isArray(events_new_listener_emitted)); 61 | assert.strictEqual(events_new_listener_emitted.length, 2); 62 | assert.strictEqual(events_new_listener_emitted[0], 'hello'); 63 | assert.strictEqual(events_new_listener_emitted[1], 'foo'); 64 | 65 | assert.ok(Array.isArray(listeners_new_listener_emitted)); 66 | assert.strictEqual(listeners_new_listener_emitted.length, 2); 67 | assert.strictEqual(listeners_new_listener_emitted[0], hello); 68 | assert.strictEqual(listeners_new_listener_emitted[1], assert.fail); 69 | 70 | ee.emit('hello', 'a', 'b'); 71 | } 72 | 73 | // just make sure that this doesn't throw: 74 | { 75 | var f = new EventEmitter(); 76 | 77 | f.setMaxListeners(0); 78 | } 79 | 80 | { 81 | var listen1 = function() {}; 82 | var listen2 = function() {}; 83 | var ee = new EventEmitter(); 84 | 85 | ee.once('newListener', function() { 86 | var listeners = ee.listeners('hello'); 87 | assert.ok(Array.isArray(listeners)); 88 | assert.strictEqual(listeners.length, 0); 89 | ee.once('newListener', function() { 90 | var listeners = ee.listeners('hello'); 91 | assert.ok(Array.isArray(listeners)); 92 | assert.strictEqual(listeners.length, 0); 93 | }); 94 | ee.on('hello', listen2); 95 | }); 96 | ee.on('hello', listen1); 97 | // The order of listeners on an event is not always the order in which the 98 | // listeners were added. 99 | var listeners = ee.listeners('hello'); 100 | assert.ok(Array.isArray(listeners)); 101 | assert.strictEqual(listeners.length, 2); 102 | assert.strictEqual(listeners[0], listen2); 103 | assert.strictEqual(listeners[1], listen1); 104 | } 105 | 106 | // Verify that the listener must be a function 107 | assert.throws(function() { 108 | var ee = new EventEmitter(); 109 | 110 | ee.on('foo', null); 111 | }, /^TypeError: The "listener" argument must be of type Function. Received type object$/); 112 | -------------------------------------------------------------------------------- /tests/remove-all-listeners.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | var common = require('./common'); 23 | var assert = require('assert'); 24 | var events = require('../'); 25 | var test = require('tape'); 26 | 27 | function expect(expected) { 28 | var actual = []; 29 | test.onFinish(function() { 30 | var sortedActual = actual.sort(); 31 | var sortedExpected = expected.sort(); 32 | assert.strictEqual(sortedActual.length, sortedExpected.length); 33 | for (var index = 0; index < sortedActual.length; index++) { 34 | var value = sortedActual[index]; 35 | assert.strictEqual(value, sortedExpected[index]); 36 | } 37 | }); 38 | function listener(name) { 39 | actual.push(name); 40 | } 41 | return common.mustCall(listener, expected.length); 42 | } 43 | 44 | { 45 | var ee = new events.EventEmitter(); 46 | var noop = common.mustNotCall(); 47 | ee.on('foo', noop); 48 | ee.on('bar', noop); 49 | ee.on('baz', noop); 50 | ee.on('baz', noop); 51 | var fooListeners = ee.listeners('foo'); 52 | var barListeners = ee.listeners('bar'); 53 | var bazListeners = ee.listeners('baz'); 54 | ee.on('removeListener', expect(['bar', 'baz', 'baz'])); 55 | ee.removeAllListeners('bar'); 56 | ee.removeAllListeners('baz'); 57 | 58 | var listeners = ee.listeners('foo'); 59 | assert.ok(Array.isArray(listeners)); 60 | assert.strictEqual(listeners.length, 1); 61 | assert.strictEqual(listeners[0], noop); 62 | 63 | listeners = ee.listeners('bar'); 64 | assert.ok(Array.isArray(listeners)); 65 | assert.strictEqual(listeners.length, 0); 66 | listeners = ee.listeners('baz'); 67 | assert.ok(Array.isArray(listeners)); 68 | assert.strictEqual(listeners.length, 0); 69 | // After calling removeAllListeners(), 70 | // the old listeners array should stay unchanged. 71 | assert.strictEqual(fooListeners.length, 1); 72 | assert.strictEqual(fooListeners[0], noop); 73 | assert.strictEqual(barListeners.length, 1); 74 | assert.strictEqual(barListeners[0], noop); 75 | assert.strictEqual(bazListeners.length, 2); 76 | assert.strictEqual(bazListeners[0], noop); 77 | assert.strictEqual(bazListeners[1], noop); 78 | // After calling removeAllListeners(), 79 | // new listeners arrays is different from the old. 80 | assert.notStrictEqual(ee.listeners('bar'), barListeners); 81 | assert.notStrictEqual(ee.listeners('baz'), bazListeners); 82 | } 83 | 84 | { 85 | var ee = new events.EventEmitter(); 86 | ee.on('foo', common.mustNotCall()); 87 | ee.on('bar', common.mustNotCall()); 88 | // Expect LIFO order 89 | ee.on('removeListener', expect(['foo', 'bar', 'removeListener'])); 90 | ee.on('removeListener', expect(['foo', 'bar'])); 91 | ee.removeAllListeners(); 92 | 93 | var listeners = ee.listeners('foo'); 94 | assert.ok(Array.isArray(listeners)); 95 | assert.strictEqual(listeners.length, 0); 96 | listeners = ee.listeners('bar'); 97 | assert.ok(Array.isArray(listeners)); 98 | assert.strictEqual(listeners.length, 0); 99 | } 100 | 101 | { 102 | var ee = new events.EventEmitter(); 103 | ee.on('removeListener', common.mustNotCall()); 104 | // Check for regression where removeAllListeners() throws when 105 | // there exists a 'removeListener' listener, but there exists 106 | // no listeners for the provided event type. 107 | assert.doesNotThrow(function () { ee.removeAllListeners(ee, 'foo') }); 108 | } 109 | 110 | { 111 | var ee = new events.EventEmitter(); 112 | var expectLength = 2; 113 | ee.on('removeListener', function() { 114 | assert.strictEqual(expectLength--, this.listeners('baz').length); 115 | }); 116 | ee.on('baz', common.mustNotCall()); 117 | ee.on('baz', common.mustNotCall()); 118 | ee.on('baz', common.mustNotCall()); 119 | assert.strictEqual(ee.listeners('baz').length, expectLength + 1); 120 | ee.removeAllListeners('baz'); 121 | assert.strictEqual(ee.listeners('baz').length, 0); 122 | } 123 | 124 | { 125 | var ee = new events.EventEmitter(); 126 | assert.strictEqual(ee, ee.removeAllListeners()); 127 | } 128 | 129 | { 130 | var ee = new events.EventEmitter(); 131 | ee._events = undefined; 132 | assert.strictEqual(ee, ee.removeAllListeners()); 133 | } 134 | -------------------------------------------------------------------------------- /tests/listeners.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | require('./common'); 23 | var assert = require('assert'); 24 | var events = require('../'); 25 | var util = require('util'); 26 | 27 | function listener() {} 28 | function listener2() {} 29 | function listener3() { 30 | return 0; 31 | } 32 | function listener4() { 33 | return 1; 34 | } 35 | 36 | function TestStream() {} 37 | util.inherits(TestStream, events.EventEmitter); 38 | 39 | { 40 | var ee = new events.EventEmitter(); 41 | ee.on('foo', listener); 42 | var fooListeners = ee.listeners('foo'); 43 | 44 | var listeners = ee.listeners('foo'); 45 | assert.ok(Array.isArray(listeners)); 46 | assert.strictEqual(listeners.length, 1); 47 | assert.strictEqual(listeners[0], listener); 48 | 49 | ee.removeAllListeners('foo'); 50 | listeners = ee.listeners('foo'); 51 | assert.ok(Array.isArray(listeners)); 52 | assert.strictEqual(listeners.length, 0); 53 | 54 | assert.ok(Array.isArray(fooListeners)); 55 | assert.strictEqual(fooListeners.length, 1); 56 | assert.strictEqual(fooListeners[0], listener); 57 | } 58 | 59 | { 60 | var ee = new events.EventEmitter(); 61 | ee.on('foo', listener); 62 | 63 | var eeListenersCopy = ee.listeners('foo'); 64 | assert.ok(Array.isArray(eeListenersCopy)); 65 | assert.strictEqual(eeListenersCopy.length, 1); 66 | assert.strictEqual(eeListenersCopy[0], listener); 67 | 68 | var listeners = ee.listeners('foo'); 69 | assert.ok(Array.isArray(listeners)); 70 | assert.strictEqual(listeners.length, 1); 71 | assert.strictEqual(listeners[0], listener); 72 | 73 | eeListenersCopy.push(listener2); 74 | listeners = ee.listeners('foo'); 75 | 76 | assert.ok(Array.isArray(listeners)); 77 | assert.strictEqual(listeners.length, 1); 78 | assert.strictEqual(listeners[0], listener); 79 | 80 | assert.strictEqual(eeListenersCopy.length, 2); 81 | assert.strictEqual(eeListenersCopy[0], listener); 82 | assert.strictEqual(eeListenersCopy[1], listener2); 83 | } 84 | 85 | { 86 | var ee = new events.EventEmitter(); 87 | ee.on('foo', listener); 88 | var eeListenersCopy = ee.listeners('foo'); 89 | ee.on('foo', listener2); 90 | 91 | var listeners = ee.listeners('foo'); 92 | assert.ok(Array.isArray(listeners)); 93 | assert.strictEqual(listeners.length, 2); 94 | assert.strictEqual(listeners[0], listener); 95 | assert.strictEqual(listeners[1], listener2); 96 | 97 | assert.ok(Array.isArray(eeListenersCopy)); 98 | assert.strictEqual(eeListenersCopy.length, 1); 99 | assert.strictEqual(eeListenersCopy[0], listener); 100 | } 101 | 102 | { 103 | var ee = new events.EventEmitter(); 104 | ee.once('foo', listener); 105 | var listeners = ee.listeners('foo'); 106 | assert.ok(Array.isArray(listeners)); 107 | assert.strictEqual(listeners.length, 1); 108 | assert.strictEqual(listeners[0], listener); 109 | } 110 | 111 | { 112 | var ee = new events.EventEmitter(); 113 | ee.on('foo', listener); 114 | ee.once('foo', listener2); 115 | 116 | var listeners = ee.listeners('foo'); 117 | assert.ok(Array.isArray(listeners)); 118 | assert.strictEqual(listeners.length, 2); 119 | assert.strictEqual(listeners[0], listener); 120 | assert.strictEqual(listeners[1], listener2); 121 | } 122 | 123 | { 124 | var ee = new events.EventEmitter(); 125 | ee._events = undefined; 126 | var listeners = ee.listeners('foo'); 127 | assert.ok(Array.isArray(listeners)); 128 | assert.strictEqual(listeners.length, 0); 129 | } 130 | 131 | { 132 | var s = new TestStream(); 133 | var listeners = s.listeners('foo'); 134 | assert.ok(Array.isArray(listeners)); 135 | assert.strictEqual(listeners.length, 0); 136 | } 137 | 138 | 139 | { 140 | var ee = new events.EventEmitter(); 141 | ee.on('foo', listener); 142 | var wrappedListener = ee.rawListeners('foo'); 143 | assert.strictEqual(wrappedListener.length, 1); 144 | assert.strictEqual(wrappedListener[0], listener); 145 | assert.notStrictEqual(wrappedListener, ee.rawListeners('foo')); 146 | ee.once('foo', listener); 147 | var wrappedListeners = ee.rawListeners('foo'); 148 | assert.strictEqual(wrappedListeners.length, 2); 149 | assert.strictEqual(wrappedListeners[0], listener); 150 | assert.notStrictEqual(wrappedListeners[1], listener); 151 | assert.strictEqual(wrappedListeners[1].listener, listener); 152 | assert.notStrictEqual(wrappedListeners, ee.rawListeners('foo')); 153 | ee.emit('foo'); 154 | assert.strictEqual(wrappedListeners.length, 2); 155 | assert.strictEqual(wrappedListeners[1].listener, listener); 156 | } 157 | 158 | { 159 | var ee = new events.EventEmitter(); 160 | ee.once('foo', listener3); 161 | ee.on('foo', listener4); 162 | var rawListeners = ee.rawListeners('foo'); 163 | assert.strictEqual(rawListeners.length, 2); 164 | assert.strictEqual(rawListeners[0](), 0); 165 | var rawListener = ee.rawListeners('foo'); 166 | assert.strictEqual(rawListener.length, 1); 167 | assert.strictEqual(rawListener[0](), 1); 168 | } 169 | -------------------------------------------------------------------------------- /tests/events-once.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var common = require('./common'); 4 | var EventEmitter = require('../').EventEmitter; 5 | var once = require('../').once; 6 | var has = require('has'); 7 | var assert = require('assert'); 8 | 9 | function Event(type) { 10 | this.type = type; 11 | } 12 | 13 | function EventTargetMock() { 14 | this.events = {}; 15 | 16 | this.addEventListener = common.mustCall(this.addEventListener); 17 | this.removeEventListener = common.mustCall(this.removeEventListener); 18 | } 19 | 20 | EventTargetMock.prototype.addEventListener = function addEventListener(name, listener, options) { 21 | if (!(name in this.events)) { 22 | this.events[name] = { listeners: [], options: options || {} } 23 | } 24 | this.events[name].listeners.push(listener); 25 | }; 26 | 27 | EventTargetMock.prototype.removeEventListener = function removeEventListener(name, callback) { 28 | if (!(name in this.events)) { 29 | return; 30 | } 31 | var event = this.events[name]; 32 | var stack = event.listeners; 33 | 34 | for (var i = 0, l = stack.length; i < l; i++) { 35 | if (stack[i] === callback) { 36 | stack.splice(i, 1); 37 | if (stack.length === 0) { 38 | delete this.events[name]; 39 | } 40 | return; 41 | } 42 | } 43 | }; 44 | 45 | EventTargetMock.prototype.dispatchEvent = function dispatchEvent(arg) { 46 | if (!(arg.type in this.events)) { 47 | return true; 48 | } 49 | 50 | var event = this.events[arg.type]; 51 | var stack = event.listeners.slice(); 52 | 53 | for (var i = 0, l = stack.length; i < l; i++) { 54 | stack[i].call(null, arg); 55 | if (event.options.once) { 56 | this.removeEventListener(arg.type, stack[i]); 57 | } 58 | } 59 | return !arg.defaultPrevented; 60 | }; 61 | 62 | function onceAnEvent() { 63 | var ee = new EventEmitter(); 64 | 65 | process.nextTick(function () { 66 | ee.emit('myevent', 42); 67 | }); 68 | 69 | return once(ee, 'myevent').then(function (args) { 70 | var value = args[0] 71 | assert.strictEqual(value, 42); 72 | assert.strictEqual(ee.listenerCount('error'), 0); 73 | assert.strictEqual(ee.listenerCount('myevent'), 0); 74 | }); 75 | } 76 | 77 | function onceAnEventWithTwoArgs() { 78 | var ee = new EventEmitter(); 79 | 80 | process.nextTick(function () { 81 | ee.emit('myevent', 42, 24); 82 | }); 83 | 84 | return once(ee, 'myevent').then(function (value) { 85 | assert.strictEqual(value.length, 2); 86 | assert.strictEqual(value[0], 42); 87 | assert.strictEqual(value[1], 24); 88 | }); 89 | } 90 | 91 | function catchesErrors() { 92 | var ee = new EventEmitter(); 93 | 94 | var expected = new Error('kaboom'); 95 | var err; 96 | process.nextTick(function () { 97 | ee.emit('error', expected); 98 | }); 99 | 100 | return once(ee, 'myevent').then(function () { 101 | throw new Error('should reject') 102 | }, function (err) { 103 | assert.strictEqual(err, expected); 104 | assert.strictEqual(ee.listenerCount('error'), 0); 105 | assert.strictEqual(ee.listenerCount('myevent'), 0); 106 | }); 107 | } 108 | 109 | function stopListeningAfterCatchingError() { 110 | var ee = new EventEmitter(); 111 | 112 | var expected = new Error('kaboom'); 113 | var err; 114 | process.nextTick(function () { 115 | ee.emit('error', expected); 116 | ee.emit('myevent', 42, 24); 117 | }); 118 | 119 | // process.on('multipleResolves', common.mustNotCall()); 120 | 121 | return once(ee, 'myevent').then(common.mustNotCall, function (err) { 122 | // process.removeAllListeners('multipleResolves'); 123 | assert.strictEqual(err, expected); 124 | assert.strictEqual(ee.listenerCount('error'), 0); 125 | assert.strictEqual(ee.listenerCount('myevent'), 0); 126 | }); 127 | } 128 | 129 | function onceError() { 130 | var ee = new EventEmitter(); 131 | 132 | var expected = new Error('kaboom'); 133 | process.nextTick(function () { 134 | ee.emit('error', expected); 135 | }); 136 | 137 | var promise = once(ee, 'error'); 138 | assert.strictEqual(ee.listenerCount('error'), 1); 139 | return promise.then(function (args) { 140 | var err = args[0] 141 | assert.strictEqual(err, expected); 142 | assert.strictEqual(ee.listenerCount('error'), 0); 143 | assert.strictEqual(ee.listenerCount('myevent'), 0); 144 | }); 145 | } 146 | 147 | function onceWithEventTarget() { 148 | var et = new EventTargetMock(); 149 | var event = new Event('myevent'); 150 | process.nextTick(function () { 151 | et.dispatchEvent(event); 152 | }); 153 | return once(et, 'myevent').then(function (args) { 154 | var value = args[0]; 155 | assert.strictEqual(value, event); 156 | assert.strictEqual(has(et.events, 'myevent'), false); 157 | }); 158 | } 159 | 160 | function onceWithEventTargetError() { 161 | var et = new EventTargetMock(); 162 | var error = new Event('error'); 163 | process.nextTick(function () { 164 | et.dispatchEvent(error); 165 | }); 166 | return once(et, 'error').then(function (args) { 167 | var err = args[0]; 168 | assert.strictEqual(err, error); 169 | assert.strictEqual(has(et.events, 'error'), false); 170 | }); 171 | } 172 | 173 | function prioritizesEventEmitter() { 174 | var ee = new EventEmitter(); 175 | ee.addEventListener = assert.fail; 176 | ee.removeAllListeners = assert.fail; 177 | process.nextTick(function () { 178 | ee.emit('foo'); 179 | }); 180 | return once(ee, 'foo'); 181 | } 182 | 183 | var allTests = [ 184 | onceAnEvent(), 185 | onceAnEventWithTwoArgs(), 186 | catchesErrors(), 187 | stopListeningAfterCatchingError(), 188 | onceError(), 189 | onceWithEventTarget(), 190 | onceWithEventTargetError(), 191 | prioritizesEventEmitter() 192 | ]; 193 | 194 | var hasBrowserEventTarget = false; 195 | try { 196 | hasBrowserEventTarget = typeof (new window.EventTarget().addEventListener) === 'function' && 197 | new window.Event('xyz').type === 'xyz'; 198 | } catch (err) {} 199 | 200 | if (hasBrowserEventTarget) { 201 | var onceWithBrowserEventTarget = function onceWithBrowserEventTarget() { 202 | var et = new window.EventTarget(); 203 | var event = new window.Event('myevent'); 204 | process.nextTick(function () { 205 | et.dispatchEvent(event); 206 | }); 207 | return once(et, 'myevent').then(function (args) { 208 | var value = args[0]; 209 | assert.strictEqual(value, event); 210 | assert.strictEqual(has(et.events, 'myevent'), false); 211 | }); 212 | } 213 | 214 | var onceWithBrowserEventTargetError = function onceWithBrowserEventTargetError() { 215 | var et = new window.EventTarget(); 216 | var error = new window.Event('error'); 217 | process.nextTick(function () { 218 | et.dispatchEvent(error); 219 | }); 220 | return once(et, 'error').then(function (args) { 221 | var err = args[0]; 222 | assert.strictEqual(err, error); 223 | assert.strictEqual(has(et.events, 'error'), false); 224 | }); 225 | } 226 | 227 | common.test.comment('Testing with browser built-in EventTarget'); 228 | allTests.push([ 229 | onceWithBrowserEventTarget(), 230 | onceWithBrowserEventTargetError() 231 | ]); 232 | } 233 | 234 | module.exports = Promise.all(allTests); 235 | -------------------------------------------------------------------------------- /tests/remove-listeners.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | var common = require('./common'); 23 | var assert = require('assert'); 24 | var EventEmitter = require('../'); 25 | 26 | var listener1 = function listener1() {}; 27 | var listener2 = function listener2() {}; 28 | 29 | { 30 | var ee = new EventEmitter(); 31 | ee.on('hello', listener1); 32 | ee.on('removeListener', common.mustCall(function(name, cb) { 33 | assert.strictEqual(name, 'hello'); 34 | assert.strictEqual(cb, listener1); 35 | })); 36 | ee.removeListener('hello', listener1); 37 | var listeners = ee.listeners('hello'); 38 | assert.ok(Array.isArray(listeners)); 39 | assert.strictEqual(listeners.length, 0); 40 | } 41 | 42 | { 43 | var ee = new EventEmitter(); 44 | ee.on('hello', listener1); 45 | ee.on('removeListener', common.mustNotCall()); 46 | ee.removeListener('hello', listener2); 47 | 48 | var listeners = ee.listeners('hello'); 49 | assert.ok(Array.isArray(listeners)); 50 | assert.strictEqual(listeners.length, 1); 51 | assert.strictEqual(listeners[0], listener1); 52 | } 53 | 54 | { 55 | var ee = new EventEmitter(); 56 | ee.on('hello', listener1); 57 | ee.on('hello', listener2); 58 | 59 | var listeners; 60 | ee.once('removeListener', common.mustCall(function(name, cb) { 61 | assert.strictEqual(name, 'hello'); 62 | assert.strictEqual(cb, listener1); 63 | listeners = ee.listeners('hello'); 64 | assert.ok(Array.isArray(listeners)); 65 | assert.strictEqual(listeners.length, 1); 66 | assert.strictEqual(listeners[0], listener2); 67 | })); 68 | ee.removeListener('hello', listener1); 69 | listeners = ee.listeners('hello'); 70 | assert.ok(Array.isArray(listeners)); 71 | assert.strictEqual(listeners.length, 1); 72 | assert.strictEqual(listeners[0], listener2); 73 | ee.once('removeListener', common.mustCall(function(name, cb) { 74 | assert.strictEqual(name, 'hello'); 75 | assert.strictEqual(cb, listener2); 76 | listeners = ee.listeners('hello'); 77 | assert.ok(Array.isArray(listeners)); 78 | assert.strictEqual(listeners.length, 0); 79 | })); 80 | ee.removeListener('hello', listener2); 81 | listeners = ee.listeners('hello'); 82 | assert.ok(Array.isArray(listeners)); 83 | assert.strictEqual(listeners.length, 0); 84 | } 85 | 86 | { 87 | var ee = new EventEmitter(); 88 | 89 | function remove1() { 90 | assert.fail('remove1 should not have been called'); 91 | } 92 | 93 | function remove2() { 94 | assert.fail('remove2 should not have been called'); 95 | } 96 | 97 | ee.on('removeListener', common.mustCall(function(name, cb) { 98 | if (cb !== remove1) return; 99 | this.removeListener('quux', remove2); 100 | this.emit('quux'); 101 | }, 2)); 102 | ee.on('quux', remove1); 103 | ee.on('quux', remove2); 104 | ee.removeListener('quux', remove1); 105 | } 106 | 107 | { 108 | var ee = new EventEmitter(); 109 | ee.on('hello', listener1); 110 | ee.on('hello', listener2); 111 | 112 | var listeners; 113 | ee.once('removeListener', common.mustCall(function(name, cb) { 114 | assert.strictEqual(name, 'hello'); 115 | assert.strictEqual(cb, listener1); 116 | listeners = ee.listeners('hello'); 117 | assert.ok(Array.isArray(listeners)); 118 | assert.strictEqual(listeners.length, 1); 119 | assert.strictEqual(listeners[0], listener2); 120 | ee.once('removeListener', common.mustCall(function(name, cb) { 121 | assert.strictEqual(name, 'hello'); 122 | assert.strictEqual(cb, listener2); 123 | listeners = ee.listeners('hello'); 124 | assert.ok(Array.isArray(listeners)); 125 | assert.strictEqual(listeners.length, 0); 126 | })); 127 | ee.removeListener('hello', listener2); 128 | listeners = ee.listeners('hello'); 129 | assert.ok(Array.isArray(listeners)); 130 | assert.strictEqual(listeners.length, 0); 131 | })); 132 | ee.removeListener('hello', listener1); 133 | listeners = ee.listeners('hello'); 134 | assert.ok(Array.isArray(listeners)); 135 | assert.strictEqual(listeners.length, 0); 136 | } 137 | 138 | { 139 | var ee = new EventEmitter(); 140 | var listener3 = common.mustCall(function() { 141 | ee.removeListener('hello', listener4); 142 | }, 2); 143 | var listener4 = common.mustCall(); 144 | 145 | ee.on('hello', listener3); 146 | ee.on('hello', listener4); 147 | 148 | // listener4 will still be called although it is removed by listener 3. 149 | ee.emit('hello'); 150 | // This is so because the interal listener array at time of emit 151 | // was [listener3,listener4] 152 | 153 | // Interal listener array [listener3] 154 | ee.emit('hello'); 155 | } 156 | 157 | { 158 | var ee = new EventEmitter(); 159 | 160 | ee.once('hello', listener1); 161 | ee.on('removeListener', common.mustCall(function(eventName, listener) { 162 | assert.strictEqual(eventName, 'hello'); 163 | assert.strictEqual(listener, listener1); 164 | })); 165 | ee.emit('hello'); 166 | } 167 | 168 | { 169 | var ee = new EventEmitter(); 170 | 171 | assert.strictEqual(ee, ee.removeListener('foo', function() {})); 172 | } 173 | 174 | // Verify that the removed listener must be a function 175 | assert.throws(function() { 176 | var ee = new EventEmitter(); 177 | 178 | ee.removeListener('foo', null); 179 | }, /^TypeError: The "listener" argument must be of type Function\. Received type object$/); 180 | 181 | { 182 | var ee = new EventEmitter(); 183 | var listener = function() {}; 184 | ee._events = undefined; 185 | var e = ee.removeListener('foo', listener); 186 | assert.strictEqual(e, ee); 187 | } 188 | 189 | { 190 | var ee = new EventEmitter(); 191 | 192 | ee.on('foo', listener1); 193 | ee.on('foo', listener2); 194 | var listeners = ee.listeners('foo'); 195 | assert.ok(Array.isArray(listeners)); 196 | assert.strictEqual(listeners.length, 2); 197 | assert.strictEqual(listeners[0], listener1); 198 | assert.strictEqual(listeners[1], listener2); 199 | 200 | ee.removeListener('foo', listener1); 201 | assert.strictEqual(ee._events.foo, listener2); 202 | 203 | ee.on('foo', listener1); 204 | listeners = ee.listeners('foo'); 205 | assert.ok(Array.isArray(listeners)); 206 | assert.strictEqual(listeners.length, 2); 207 | assert.strictEqual(listeners[0], listener2); 208 | assert.strictEqual(listeners[1], listener1); 209 | 210 | ee.removeListener('foo', listener1); 211 | assert.strictEqual(ee._events.foo, listener2); 212 | } 213 | -------------------------------------------------------------------------------- /events.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 'use strict'; 23 | 24 | var R = typeof Reflect === 'object' ? Reflect : null 25 | var ReflectApply = R && typeof R.apply === 'function' 26 | ? R.apply 27 | : function ReflectApply(target, receiver, args) { 28 | return Function.prototype.apply.call(target, receiver, args); 29 | } 30 | 31 | var ReflectOwnKeys 32 | if (R && typeof R.ownKeys === 'function') { 33 | ReflectOwnKeys = R.ownKeys 34 | } else if (Object.getOwnPropertySymbols) { 35 | ReflectOwnKeys = function ReflectOwnKeys(target) { 36 | return Object.getOwnPropertyNames(target) 37 | .concat(Object.getOwnPropertySymbols(target)); 38 | }; 39 | } else { 40 | ReflectOwnKeys = function ReflectOwnKeys(target) { 41 | return Object.getOwnPropertyNames(target); 42 | }; 43 | } 44 | 45 | function ProcessEmitWarning(warning) { 46 | if (console && console.warn) console.warn(warning); 47 | } 48 | 49 | var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) { 50 | return value !== value; 51 | } 52 | 53 | function EventEmitter() { 54 | EventEmitter.init.call(this); 55 | } 56 | module.exports = EventEmitter; 57 | module.exports.once = once; 58 | 59 | // Backwards-compat with node 0.10.x 60 | EventEmitter.EventEmitter = EventEmitter; 61 | 62 | EventEmitter.prototype._events = undefined; 63 | EventEmitter.prototype._eventsCount = 0; 64 | EventEmitter.prototype._maxListeners = undefined; 65 | 66 | // By default EventEmitters will print a warning if more than 10 listeners are 67 | // added to it. This is a useful default which helps finding memory leaks. 68 | var defaultMaxListeners = 10; 69 | 70 | function checkListener(listener) { 71 | if (typeof listener !== 'function') { 72 | throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); 73 | } 74 | } 75 | 76 | Object.defineProperty(EventEmitter, 'defaultMaxListeners', { 77 | enumerable: true, 78 | get: function() { 79 | return defaultMaxListeners; 80 | }, 81 | set: function(arg) { 82 | if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) { 83 | throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.'); 84 | } 85 | defaultMaxListeners = arg; 86 | } 87 | }); 88 | 89 | EventEmitter.init = function() { 90 | 91 | if (this._events === undefined || 92 | this._events === Object.getPrototypeOf(this)._events) { 93 | this._events = Object.create(null); 94 | this._eventsCount = 0; 95 | } 96 | 97 | this._maxListeners = this._maxListeners || undefined; 98 | }; 99 | 100 | // Obviously not all Emitters should be limited to 10. This function allows 101 | // that to be increased. Set to zero for unlimited. 102 | EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { 103 | if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) { 104 | throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.'); 105 | } 106 | this._maxListeners = n; 107 | return this; 108 | }; 109 | 110 | function _getMaxListeners(that) { 111 | if (that._maxListeners === undefined) 112 | return EventEmitter.defaultMaxListeners; 113 | return that._maxListeners; 114 | } 115 | 116 | EventEmitter.prototype.getMaxListeners = function getMaxListeners() { 117 | return _getMaxListeners(this); 118 | }; 119 | 120 | EventEmitter.prototype.emit = function emit(type) { 121 | var args = []; 122 | for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); 123 | var doError = (type === 'error'); 124 | 125 | var events = this._events; 126 | if (events !== undefined) 127 | doError = (doError && events.error === undefined); 128 | else if (!doError) 129 | return false; 130 | 131 | // If there is no 'error' event listener then throw. 132 | if (doError) { 133 | var er; 134 | if (args.length > 0) 135 | er = args[0]; 136 | if (er instanceof Error) { 137 | // Note: The comments on the `throw` lines are intentional, they show 138 | // up in Node's output if this results in an unhandled exception. 139 | throw er; // Unhandled 'error' event 140 | } 141 | // At least give some kind of context to the user 142 | var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : '')); 143 | err.context = er; 144 | throw err; // Unhandled 'error' event 145 | } 146 | 147 | var handler = events[type]; 148 | 149 | if (handler === undefined) 150 | return false; 151 | 152 | if (typeof handler === 'function') { 153 | ReflectApply(handler, this, args); 154 | } else { 155 | var len = handler.length; 156 | var listeners = arrayClone(handler, len); 157 | for (var i = 0; i < len; ++i) 158 | ReflectApply(listeners[i], this, args); 159 | } 160 | 161 | return true; 162 | }; 163 | 164 | function _addListener(target, type, listener, prepend) { 165 | var m; 166 | var events; 167 | var existing; 168 | 169 | checkListener(listener); 170 | 171 | events = target._events; 172 | if (events === undefined) { 173 | events = target._events = Object.create(null); 174 | target._eventsCount = 0; 175 | } else { 176 | // To avoid recursion in the case that type === "newListener"! Before 177 | // adding it to the listeners, first emit "newListener". 178 | if (events.newListener !== undefined) { 179 | target.emit('newListener', type, 180 | listener.listener ? listener.listener : listener); 181 | 182 | // Re-assign `events` because a newListener handler could have caused the 183 | // this._events to be assigned to a new object 184 | events = target._events; 185 | } 186 | existing = events[type]; 187 | } 188 | 189 | if (existing === undefined) { 190 | // Optimize the case of one listener. Don't need the extra array object. 191 | existing = events[type] = listener; 192 | ++target._eventsCount; 193 | } else { 194 | if (typeof existing === 'function') { 195 | // Adding the second element, need to change to array. 196 | existing = events[type] = 197 | prepend ? [listener, existing] : [existing, listener]; 198 | // If we've already got an array, just append. 199 | } else if (prepend) { 200 | existing.unshift(listener); 201 | } else { 202 | existing.push(listener); 203 | } 204 | 205 | // Check for listener leak 206 | m = _getMaxListeners(target); 207 | if (m > 0 && existing.length > m && !existing.warned) { 208 | existing.warned = true; 209 | // No error code for this since it is a Warning 210 | // eslint-disable-next-line no-restricted-syntax 211 | var w = new Error('Possible EventEmitter memory leak detected. ' + 212 | existing.length + ' ' + String(type) + ' listeners ' + 213 | 'added. Use emitter.setMaxListeners() to ' + 214 | 'increase limit'); 215 | w.name = 'MaxListenersExceededWarning'; 216 | w.emitter = target; 217 | w.type = type; 218 | w.count = existing.length; 219 | ProcessEmitWarning(w); 220 | } 221 | } 222 | 223 | return target; 224 | } 225 | 226 | EventEmitter.prototype.addListener = function addListener(type, listener) { 227 | return _addListener(this, type, listener, false); 228 | }; 229 | 230 | EventEmitter.prototype.on = EventEmitter.prototype.addListener; 231 | 232 | EventEmitter.prototype.prependListener = 233 | function prependListener(type, listener) { 234 | return _addListener(this, type, listener, true); 235 | }; 236 | 237 | function onceWrapper() { 238 | if (!this.fired) { 239 | this.target.removeListener(this.type, this.wrapFn); 240 | this.fired = true; 241 | if (arguments.length === 0) 242 | return this.listener.call(this.target); 243 | return this.listener.apply(this.target, arguments); 244 | } 245 | } 246 | 247 | function _onceWrap(target, type, listener) { 248 | var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; 249 | var wrapped = onceWrapper.bind(state); 250 | wrapped.listener = listener; 251 | state.wrapFn = wrapped; 252 | return wrapped; 253 | } 254 | 255 | EventEmitter.prototype.once = function once(type, listener) { 256 | checkListener(listener); 257 | this.on(type, _onceWrap(this, type, listener)); 258 | return this; 259 | }; 260 | 261 | EventEmitter.prototype.prependOnceListener = 262 | function prependOnceListener(type, listener) { 263 | checkListener(listener); 264 | this.prependListener(type, _onceWrap(this, type, listener)); 265 | return this; 266 | }; 267 | 268 | // Emits a 'removeListener' event if and only if the listener was removed. 269 | EventEmitter.prototype.removeListener = 270 | function removeListener(type, listener) { 271 | var list, events, position, i, originalListener; 272 | 273 | checkListener(listener); 274 | 275 | events = this._events; 276 | if (events === undefined) 277 | return this; 278 | 279 | list = events[type]; 280 | if (list === undefined) 281 | return this; 282 | 283 | if (list === listener || list.listener === listener) { 284 | if (--this._eventsCount === 0) 285 | this._events = Object.create(null); 286 | else { 287 | delete events[type]; 288 | if (events.removeListener) 289 | this.emit('removeListener', type, list.listener || listener); 290 | } 291 | } else if (typeof list !== 'function') { 292 | position = -1; 293 | 294 | for (i = list.length - 1; i >= 0; i--) { 295 | if (list[i] === listener || list[i].listener === listener) { 296 | originalListener = list[i].listener; 297 | position = i; 298 | break; 299 | } 300 | } 301 | 302 | if (position < 0) 303 | return this; 304 | 305 | if (position === 0) 306 | list.shift(); 307 | else { 308 | spliceOne(list, position); 309 | } 310 | 311 | if (list.length === 1) 312 | events[type] = list[0]; 313 | 314 | if (events.removeListener !== undefined) 315 | this.emit('removeListener', type, originalListener || listener); 316 | } 317 | 318 | return this; 319 | }; 320 | 321 | EventEmitter.prototype.off = EventEmitter.prototype.removeListener; 322 | 323 | EventEmitter.prototype.removeAllListeners = 324 | function removeAllListeners(type) { 325 | var listeners, events, i; 326 | 327 | events = this._events; 328 | if (events === undefined) 329 | return this; 330 | 331 | // not listening for removeListener, no need to emit 332 | if (events.removeListener === undefined) { 333 | if (arguments.length === 0) { 334 | this._events = Object.create(null); 335 | this._eventsCount = 0; 336 | } else if (events[type] !== undefined) { 337 | if (--this._eventsCount === 0) 338 | this._events = Object.create(null); 339 | else 340 | delete events[type]; 341 | } 342 | return this; 343 | } 344 | 345 | // emit removeListener for all listeners on all events 346 | if (arguments.length === 0) { 347 | var keys = Object.keys(events); 348 | var key; 349 | for (i = 0; i < keys.length; ++i) { 350 | key = keys[i]; 351 | if (key === 'removeListener') continue; 352 | this.removeAllListeners(key); 353 | } 354 | this.removeAllListeners('removeListener'); 355 | this._events = Object.create(null); 356 | this._eventsCount = 0; 357 | return this; 358 | } 359 | 360 | listeners = events[type]; 361 | 362 | if (typeof listeners === 'function') { 363 | this.removeListener(type, listeners); 364 | } else if (listeners !== undefined) { 365 | // LIFO order 366 | for (i = listeners.length - 1; i >= 0; i--) { 367 | this.removeListener(type, listeners[i]); 368 | } 369 | } 370 | 371 | return this; 372 | }; 373 | 374 | function _listeners(target, type, unwrap) { 375 | var events = target._events; 376 | 377 | if (events === undefined) 378 | return []; 379 | 380 | var evlistener = events[type]; 381 | if (evlistener === undefined) 382 | return []; 383 | 384 | if (typeof evlistener === 'function') 385 | return unwrap ? [evlistener.listener || evlistener] : [evlistener]; 386 | 387 | return unwrap ? 388 | unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); 389 | } 390 | 391 | EventEmitter.prototype.listeners = function listeners(type) { 392 | return _listeners(this, type, true); 393 | }; 394 | 395 | EventEmitter.prototype.rawListeners = function rawListeners(type) { 396 | return _listeners(this, type, false); 397 | }; 398 | 399 | EventEmitter.listenerCount = function(emitter, type) { 400 | if (typeof emitter.listenerCount === 'function') { 401 | return emitter.listenerCount(type); 402 | } else { 403 | return listenerCount.call(emitter, type); 404 | } 405 | }; 406 | 407 | EventEmitter.prototype.listenerCount = listenerCount; 408 | function listenerCount(type) { 409 | var events = this._events; 410 | 411 | if (events !== undefined) { 412 | var evlistener = events[type]; 413 | 414 | if (typeof evlistener === 'function') { 415 | return 1; 416 | } else if (evlistener !== undefined) { 417 | return evlistener.length; 418 | } 419 | } 420 | 421 | return 0; 422 | } 423 | 424 | EventEmitter.prototype.eventNames = function eventNames() { 425 | return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : []; 426 | }; 427 | 428 | function arrayClone(arr, n) { 429 | var copy = new Array(n); 430 | for (var i = 0; i < n; ++i) 431 | copy[i] = arr[i]; 432 | return copy; 433 | } 434 | 435 | function spliceOne(list, index) { 436 | for (; index + 1 < list.length; index++) 437 | list[index] = list[index + 1]; 438 | list.pop(); 439 | } 440 | 441 | function unwrapListeners(arr) { 442 | var ret = new Array(arr.length); 443 | for (var i = 0; i < ret.length; ++i) { 444 | ret[i] = arr[i].listener || arr[i]; 445 | } 446 | return ret; 447 | } 448 | 449 | function once(emitter, name) { 450 | return new Promise(function (resolve, reject) { 451 | function errorListener(err) { 452 | emitter.removeListener(name, resolver); 453 | reject(err); 454 | } 455 | 456 | function resolver() { 457 | if (typeof emitter.removeListener === 'function') { 458 | emitter.removeListener('error', errorListener); 459 | } 460 | resolve([].slice.call(arguments)); 461 | }; 462 | 463 | eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); 464 | if (name !== 'error') { 465 | addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }); 466 | } 467 | }); 468 | } 469 | 470 | function addErrorHandlerIfEventEmitter(emitter, handler, flags) { 471 | if (typeof emitter.on === 'function') { 472 | eventTargetAgnosticAddListener(emitter, 'error', handler, flags); 473 | } 474 | } 475 | 476 | function eventTargetAgnosticAddListener(emitter, name, listener, flags) { 477 | if (typeof emitter.on === 'function') { 478 | if (flags.once) { 479 | emitter.once(name, listener); 480 | } else { 481 | emitter.on(name, listener); 482 | } 483 | } else if (typeof emitter.addEventListener === 'function') { 484 | // EventTarget does not have `error` event semantics like Node 485 | // EventEmitters, we do not listen for `error` events here. 486 | emitter.addEventListener(name, function wrapListener(arg) { 487 | // IE does not have builtin `{ once: true }` support so we 488 | // have to do it manually. 489 | if (flags.once) { 490 | emitter.removeEventListener(name, wrapListener); 491 | } 492 | listener(arg); 493 | }); 494 | } else { 495 | throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter); 496 | } 497 | } 498 | --------------------------------------------------------------------------------