pax_global_header00006660000000000000000000000064130605660710014516gustar00rootroot0000000000000052 comment=12558bc80c7d0b3600107108b51e260713319cda d3-dispatch-1.0.3/000077500000000000000000000000001306056607100136225ustar00rootroot00000000000000d3-dispatch-1.0.3/.eslintrc000066400000000000000000000001471306056607100154500ustar00rootroot00000000000000parserOptions: sourceType: module extends: "eslint:recommended" rules: no-cond-assign: 0 d3-dispatch-1.0.3/.gitignore000066400000000000000000000001001306056607100156010ustar00rootroot00000000000000*.sublime-workspace .DS_Store build/ node_modules npm-debug.log d3-dispatch-1.0.3/.npmignore000066400000000000000000000000361306056607100156200ustar00rootroot00000000000000*.sublime-* build/*.zip test/ d3-dispatch-1.0.3/LICENSE000066400000000000000000000027031306056607100146310ustar00rootroot00000000000000Copyright 2010-2016 Mike Bostock All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. d3-dispatch-1.0.3/README.md000066400000000000000000000122341306056607100151030ustar00rootroot00000000000000# d3-dispatch Dispatching is a convenient mechanism for separating concerns with loosely-coupled code: register named callbacks and then call them with arbitrary arguments. A variety of D3 components, such as [d3-request](https://github.com/d3/d3-request), use this mechanism to emit events to listeners. Think of this like Node’s [EventEmitter](https://nodejs.org/api/events.html), except every listener has a well-defined name so it’s easy to remove or replace them. For example, to create a dispatch for *start* and *end* events: ```js var dispatch = d3.dispatch("start", "end"); ``` You can then register callbacks for these events using [*dispatch*.on](#dispatch_on): ```js dispatch.on("start", callback1); dispatch.on("start.foo", callback2); dispatch.on("end", callback3); ``` Then, you can invoke all the *start* callbacks using [*dispatch*.call](#dispatch_call) or [*dispatch*.apply](#dispatch_apply): ```js dispatch.call("start"); ``` Like *function*.call, you may also specify the `this` context and any arguments: ```js dispatch.call("start", {about: "I am a context object"}, "I am an argument"); ``` Want a more involved example? See how to use [d3-dispatch for coordinated views](http://bl.ocks.org/mbostock/5872848). ## Installing If you use NPM, `npm install d3-dispatch`. Otherwise, download the [latest release](https://github.com/d3/d3-dispatch/releases/latest). You can also load directly from [d3js.org](https://d3js.org), either as a [standalone library](https://d3js.org/d3-dispatch.v1.min.js) or as part of [D3 4.0](https://github.com/d3/d3). AMD, CommonJS, and vanilla environments are supported. In vanilla, a `d3` global is exported: ```html ``` [Try d3-dispatch in your browser.](https://tonicdev.com/npm/d3-dispatch) ## API Reference # d3.dispatch(types…) [<>](https://github.com/d3/d3-dispatch/blob/master/src/dispatch.js "Source") Creates a new dispatch for the specified event *types*. Each *type* is a string, such as `"start"` or `"end"`. # *dispatch*.on(typenames[, callback]) [<>](https://github.com/d3/d3-dispatch/blob/master/src/dispatch.js#L26 "Source") Adds, removes or gets the *callback* for the specified *typenames*. If a *callback* function is specified, it is registered for the specified (fully-qualified) *typenames*. If a callback was already registered for the given *typenames*, the existing callback is removed before the new callback is added. The specified *typenames* is a string, such as `start` or `end.foo`. The type may be optionally followed by a period (`.`) and a name; the optional name allows multiple callbacks to be registered to receive events of the same type, such as `start.foo` and `start.bar`. To specify multiple typenames, separate typenames with spaces, such as `start end` or `start.foo start.bar`. To remove all callbacks for a given name `foo`, say `dispatch.on(".foo", null)`. If *callback* is not specified, returns the current callback for the specified *typenames*, if any. If multiple typenames are specified, the first matching callback is returned. # *dispatch*.copy() [<>](https://github.com/d3/d3-dispatch/blob/master/src/dispatch.js#L49 "Source") Returns a copy of this dispatch object. Changes to this dispatch do not affect the returned copy and vice versa. # *dispatch*.call(type[, that[, arguments…]]) [<>](https://github.com/d3/d3-dispatch/blob/master/src/dispatch.js#L54 "Source") Like [*function*.call](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call), invokes each registered callback for the specified *type*, passing the callback the specified *arguments*, with *that* as the `this` context. See [*dispatch*.apply](#dispatch_apply) for more information. # *dispatch*.apply(type[, that[, arguments]]) [<>](https://github.com/d3/d3-dispatch/blob/master/src/dispatch.js#L59 "Source") Like [*function*.apply](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call), invokes each registered callback for the specified *type*, passing the callback the specified *arguments*, with *that* as the `this` context. For example, if you wanted to dispatch your *custom* callbacks after handling a native *click* event, while preserving the current `this` context and arguments, you could say: ```js selection.on("click", function() { dispatch.apply("custom", this, arguments); }); ``` You can pass whatever arguments you want to callbacks; most commonly, you might create an object that represents an event, or pass the current datum (*d*) and index (*i*). See [function.call](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Call) and [function.apply](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Apply) for further information. d3-dispatch-1.0.3/d3-dispatch.sublime-project000066400000000000000000000002711306056607100207530ustar00rootroot00000000000000{ "folders": [ { "path": ".", "file_exclude_patterns": [ "*.sublime-workspace" ], "folder_exclude_patterns": [ "build" ] } ] } d3-dispatch-1.0.3/index.js000066400000000000000000000000641306056607100152670ustar00rootroot00000000000000export {default as dispatch} from "./src/dispatch"; d3-dispatch-1.0.3/package.json000066400000000000000000000027041306056607100161130ustar00rootroot00000000000000{ "name": "d3-dispatch", "version": "1.0.3", "description": "Register named callbacks and call them with arguments.", "keywords": [ "d3", "d3-module", "event", "listener", "dispatch" ], "homepage": "https://d3js.org/d3-dispatch/", "license": "BSD-3-Clause", "author": { "name": "Mike Bostock", "url": "http://bost.ocks.org/mike" }, "main": "build/d3-dispatch.js", "module": "index", "jsnext:main": "index", "repository": { "type": "git", "url": "https://github.com/d3/d3-dispatch.git" }, "scripts": { "pretest": "rm -rf build && mkdir build && rollup --banner \"$(preamble)\" -f umd -n d3 -o build/d3-dispatch.js -- index.js", "test": "tape 'test/**/*-test.js' && eslint index.js src", "prepublish": "npm run test && uglifyjs --preamble \"$(preamble)\" build/d3-dispatch.js -c -m -o build/d3-dispatch.min.js", "postpublish": "git push && git push --tags && cd ../d3.github.com && git pull && cp ../d3-dispatch/build/d3-dispatch.js d3-dispatch.v1.js && cp ../d3-dispatch/build/d3-dispatch.min.js d3-dispatch.v1.min.js && git add d3-dispatch.v1.js d3-dispatch.v1.min.js && git commit -m \"d3-dispatch ${npm_package_version}\" && git push && cd - && zip -j build/d3-dispatch.zip -- LICENSE README.md build/d3-dispatch.js build/d3-dispatch.min.js" }, "devDependencies": { "eslint": "3", "package-preamble": "0.0", "rollup": "0.41", "tape": "4", "uglify-js": "^2.8.11" } } d3-dispatch-1.0.3/src/000077500000000000000000000000001306056607100144115ustar00rootroot00000000000000d3-dispatch-1.0.3/src/dispatch.js000066400000000000000000000052511306056607100165510ustar00rootroot00000000000000var noop = {value: function() {}}; function dispatch() { for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) { if (!(t = arguments[i] + "") || (t in _)) throw new Error("illegal type: " + t); _[t] = []; } return new Dispatch(_); } function Dispatch(_) { this._ = _; } function parseTypenames(typenames, types) { return typenames.trim().split(/^|\s+/).map(function(t) { var name = "", i = t.indexOf("."); if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t); return {type: t, name: name}; }); } Dispatch.prototype = dispatch.prototype = { constructor: Dispatch, on: function(typename, callback) { var _ = this._, T = parseTypenames(typename + "", _), t, i = -1, n = T.length; // If no callback was specified, return the callback of the given type and name. if (arguments.length < 2) { while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t; return; } // If a type was specified, set the callback for the given type and name. // Otherwise, if a null callback was specified, remove callbacks of the given name. if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback); while (++i < n) { if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback); else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null); } return this; }, copy: function() { var copy = {}, _ = this._; for (var t in _) copy[t] = _[t].slice(); return new Dispatch(copy); }, call: function(type, that) { if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2]; if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); }, apply: function(type, that, args) { if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); } }; function get(type, name) { for (var i = 0, n = type.length, c; i < n; ++i) { if ((c = type[i]).name === name) { return c.value; } } } function set(type, name, callback) { for (var i = 0, n = type.length; i < n; ++i) { if (type[i].name === name) { type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1)); break; } } if (callback != null) type.push({name: name, value: callback}); return type; } export default dispatch; d3-dispatch-1.0.3/test/000077500000000000000000000000001306056607100146015ustar00rootroot00000000000000d3-dispatch-1.0.3/test/dispatch-test.js000066400000000000000000000316131306056607100177170ustar00rootroot00000000000000var tape = require("tape"), dispatch = require("../"); tape("dispatch(type…) returns a dispatch object with the specified types", function(test) { var d = dispatch.dispatch("foo", "bar"); test.ok(d instanceof dispatch.dispatch); test.equal(d.constructor.name, "Dispatch"); test.end(); }); tape("dispatch(type…) does not throw an error if a specified type name collides with a dispatch method", function(test) { var d = dispatch.dispatch("on"); test.ok(d instanceof dispatch.dispatch); test.end(); }); tape("dispatch(type…) throws an error if a specified type name is illegal", function(test) { test.throws(function() { dispatch.dispatch("__proto__"); }); test.throws(function() { dispatch.dispatch("hasOwnProperty"); }); test.throws(function() { dispatch.dispatch(""); }); test.end(); }); tape("dispatch(type…) throws an error if a specified type name is a duplicate", function(test) { test.throws(function() { dispatch.dispatch("foo", "foo"); }); test.end(); }); tape("dispatch(type).call(type, object, arguments…) invokes callbacks of the specified type", function(test) { var foo = 0, bar = 0, d = dispatch.dispatch("foo", "bar").on("foo", function() { ++foo; }).on("bar", function() { ++bar; }); d.call("foo"); test.equal(foo, 1); test.equal(bar, 0); d.call("foo"); d.call("bar"); test.equal(foo, 2); test.equal(bar, 1); test.end(); }); tape("dispatch(type).call(type, object, arguments…) invokes callbacks with specified arguments and context", function(test) { var results = [], foo = {}, bar = {}, d = dispatch.dispatch("foo").on("foo", function() { results.push({this: this, arguments: [].slice.call(arguments)}); }); d.call("foo", foo, bar); test.deepEqual(results, [{this: foo, arguments: [bar]}]); d.call("foo", bar, foo, 42, "baz"); test.deepEqual(results, [{this: foo, arguments: [bar]}, {this: bar, arguments: [foo, 42, "baz"]}]); test.end(); }); tape("dispatch(type).call(type, object, arguments…) invokes callbacks in the order they were added", function(test) { var results = [], d = dispatch.dispatch("foo"); d.on("foo.a", function() { results.push("A"); }); d.on("foo.b", function() { results.push("B"); }); d.call("foo"); d.on("foo.c", function() { results.push("C"); }); d.on("foo.a", function() { results.push("A"); }); // move to end d.call("foo"); test.deepEqual(results, ["A", "B", "B", "C", "A"]); test.end(); }); tape("dispatch(type).call(type, object, arguments…) returns undefined", function(test) { var d = dispatch.dispatch("foo"); test.equal(d.call("foo"), undefined); test.end(); }); tape("dispatch(type).apply(type, object, arguments) invokes callbacks of the specified type", function(test) { var foo = 0, bar = 0, d = dispatch.dispatch("foo", "bar").on("foo", function() { ++foo; }).on("bar", function() { ++bar; }); d.apply("foo"); test.equal(foo, 1); test.equal(bar, 0); d.apply("foo"); d.apply("bar"); test.equal(foo, 2); test.equal(bar, 1); test.end(); }); tape("dispatch(type).apply(type, object, arguments) invokes callbacks with specified arguments and context", function(test) { var results = [], foo = {}, bar = {}, d = dispatch.dispatch("foo").on("foo", function() { results.push({this: this, arguments: [].slice.call(arguments)}); }); d.apply("foo", foo, [bar]); test.deepEqual(results, [{this: foo, arguments: [bar]}]); d.apply("foo", bar, [foo, 42, "baz"]); test.deepEqual(results, [{this: foo, arguments: [bar]}, {this: bar, arguments: [foo, 42, "baz"]}]); test.end(); }); tape("dispatch(type).apply(type, object, arguments) invokes callbacks in the order they were added", function(test) { var results = [], d = dispatch.dispatch("foo"); d.on("foo.a", function() { results.push("A"); }); d.on("foo.b", function() { results.push("B"); }); d.apply("foo"); d.on("foo.c", function() { results.push("C"); }); d.on("foo.a", function() { results.push("A"); }); // move to end d.apply("foo"); test.deepEqual(results, ["A", "B", "B", "C", "A"]); test.end(); }); tape("dispatch(type).apply(type, object, arguments) returns undefined", function(test) { var d = dispatch.dispatch("foo"); test.equal(d.apply("foo"), undefined); test.end(); }); tape("dispatch(type).on(type, f) returns the dispatch object", function(test) { var d = dispatch.dispatch("foo"); test.equal(d.on("foo", function() {}), d); test.end(); }); tape("dispatch(type).on(type, f) replaces an existing callback, if present", function(test) { var foo = 0, bar = 0, d = dispatch.dispatch("foo", "bar"); d.on("foo", function() { ++foo; }); d.call("foo"); test.equal(foo, 1); test.equal(bar, 0); d.on("foo", function() { ++bar; }); d.call("foo"); test.equal(foo, 1); test.equal(bar, 1); test.end(); }); tape("dispatch(type).on(type, f) replacing an existing callback with itself has no effect", function(test) { var foo = 0, FOO = function() { ++foo; }, d = dispatch.dispatch("foo").on("foo", FOO); d.call("foo"); test.equal(foo, 1); d.on("foo", FOO).on("foo", FOO).on("foo", FOO); d.call("foo"); test.equal(foo, 2); test.end(); }); tape("dispatch(type).on(type., …) is equivalent to dispatch(type).on(type, …)", function(test) { var d = dispatch.dispatch("foo"), foos = 0, bars = 0, foo = function() { ++foos; }, bar = function() { ++bars; }; test.equal(d.on("foo.", foo), d); test.equal(d.on("foo."), foo); test.equal(d.on("foo"), foo); test.equal(d.on("foo.", bar), d); test.equal(d.on("foo."), bar); test.equal(d.on("foo"), bar); test.equal(d.call("foo"), undefined); test.equal(foos, 0); test.equal(bars, 1); test.equal(d.on(".", null), d); test.equal(d.on("foo"), undefined); test.equal(d.call("foo"), undefined); test.equal(foos, 0); test.equal(bars, 1); test.end(); }); tape("dispatch(type).on(type, null) removes an existing callback, if present", function(test) { var foo = 0, d = dispatch.dispatch("foo", "bar"); d.on("foo", function() { ++foo; }); d.call("foo"); test.equal(foo, 1); d.on("foo", null); d.call("foo"); test.equal(foo, 1); test.end(); }); tape("dispatch(type).on(type, null) does not remove a shared callback", function(test) { var a = 0, A = function() { ++a; }, d = dispatch.dispatch("foo", "bar").on("foo", A).on("bar", A); d.call("foo"); d.call("bar"); test.equal(a, 2); d.on("foo", null); d.call("bar"); test.equal(a, 3); test.end(); }); tape("dispatch(type).on(type, null) removing a missing callback has no effect", function(test) { var d = dispatch.dispatch("foo"), a = 0; function A() { ++a; } d.on("foo.a", null).on("foo", A).on("foo", null).on("foo", null); d.call("foo"); test.equal(a, 0); test.end(); }); tape("dispatch(type).on(type, null) during a callback does not invoke the old callback", function(test) { var a = 0, b = 0, c = 0, A = function() { ++a; d.on("foo.B", null); }, // remove B B = function() { ++b; }, C = function() { ++c; }, d = dispatch.dispatch("foo").on("foo.A", A).on("foo.B", B); d.call("foo"); test.equal(a, 1); test.equal(b, 0); test.equal(c, 0); test.end(); }); tape("dispatch(type).on(type, f) during a callback does not invoke the old or the new callback", function(test) { var a = 0, b = 0, c = 0, A = function() { ++a; d.on("foo.B", C); }, // replace B with C B = function() { ++b; }, C = function() { ++c; }, d = dispatch.dispatch("foo").on("foo.A", A).on("foo.B", B); d.call("foo"); test.equal(a, 1); test.equal(b, 0); test.equal(c, 0); test.end(); }); tape("dispatch(type).on(type, f) during a callback does not invoke the new callback", function(test) { var a = 0, b = 0, A = function() { ++a; d.on("foo.B", B); }, // add B B = function() { ++b; }, d = dispatch.dispatch("foo").on("foo.A", A); d.call("foo"); test.equal(a, 1); test.equal(b, 0); test.end(); }); tape("dispatch(type).on(type, f) coerces type to a string", function(test) { var f = function() {}, g = function() {}, d = dispatch.dispatch(null, undefined).on(null, f).on(undefined, g); test.equal(d.on(null), f); test.equal(d.on(undefined), g); test.end(); }); tape("dispatch(\"foo\", \"bar\").on(\"foo bar\", f) adds a callback for both types", function(test) { var foos = 0, foo = function() { ++foos; }, d = dispatch.dispatch("foo", "bar").on("foo bar", foo); test.equal(d.on("foo"), foo); test.equal(d.on("bar"), foo); d.call("foo"); test.equal(foos, 1); d.call("bar"); test.equal(foos, 2); test.end(); }); tape("dispatch(\"foo\").on(\"foo.one foo.two\", f) adds a callback for both typenames", function(test) { var foos = 0, foo = function() { ++foos; }, d = dispatch.dispatch("foo").on("foo.one foo.two", foo); test.equal(d.on("foo.one"), foo); test.equal(d.on("foo.two"), foo); d.call("foo"); test.equal(foos, 2); test.end(); }); tape("dispatch(\"foo\", \"bar\").on(\"foo bar\") returns the callback for either type", function(test) { var foos = 0, foo = function() { ++foos; }, d = dispatch.dispatch("foo", "bar"); d.on("foo", foo); test.equal(d.on("foo bar"), foo); test.equal(d.on("bar foo"), foo); d.on("foo", null).on("bar", foo); test.equal(d.on("foo bar"), foo); test.equal(d.on("bar foo"), foo); test.end(); }); tape("dispatch(\"foo\").on(\"foo.one foo.two\") returns the callback for either typename", function(test) { var foos = 0, foo = function() { ++foos; }, d = dispatch.dispatch("foo"); d.on("foo.one", foo); test.equal(d.on("foo.one foo.two"), foo); test.equal(d.on("foo.two foo.one"), foo); test.equal(d.on("foo foo.one"), foo); test.equal(d.on("foo.one foo"), foo); d.on("foo.one", null).on("foo.two", foo); test.equal(d.on("foo.one foo.two"), foo); test.equal(d.on("foo.two foo.one"), foo); test.equal(d.on("foo foo.two"), foo); test.equal(d.on("foo.two foo"), foo); test.end(); }); tape("dispatch(\"foo\").on(\".one .two\", null) removes the callback for either typename", function(test) { var foos = 0, foo = function() { ++foos; }, d = dispatch.dispatch("foo"); d.on("foo.one", foo); d.on("foo.two", foo); d.on("foo.one foo.two", null); test.equal(d.on("foo.one"), undefined); test.equal(d.on("foo.two"), undefined); test.end(); }); tape("dispatch(type).on(type, f) throws an error if f is not a function", function(test) { test.throws(function() { dispatch.dispatch("foo").on("foo", 42); }); test.end(); }); tape("dispatch(…).on(type, f) throws an error if the type is unknown", function(test) { test.throws(function() { dispatch.dispatch("foo").on("bar", function() {}); }); test.throws(function() { dispatch.dispatch("foo").on("__proto__", function() {}); }); test.end(); }); tape("dispatch(…).on(type) throws an error if the type is unknown", function(test) { test.throws(function() { dispatch.dispatch("foo").on("bar"); }); test.throws(function() { dispatch.dispatch("foo").on("__proto__"); }); test.end(); }); tape("dispatch(type).on(type) returns the expected callback", function(test) { var d = dispatch.dispatch("foo"); function A() {} function B() {} function C() {} d.on("foo.a", A).on("foo.b", B).on("foo", C); test.equal(d.on("foo.a"), A); test.equal(d.on("foo.b"), B); test.equal(d.on("foo"), C); test.end(); }); tape("dispatch(type).on(.name) returns undefined when retrieving a callback", function(test) { var d = dispatch.dispatch("foo").on("foo.a", function() {}); test.equal(d.on(".a"), undefined); test.end(); }); tape("dispatch(type).on(.name, null) removes all callbacks with the specified name", function(test) { var d = dispatch.dispatch("foo", "bar"), a = {}, b = {}, c = {}, those = []; function A() { those.push(a); } function B() { those.push(b); } function C() { those.push(c); } d.on("foo.a", A).on("bar.a", B).on("foo", C).on(".a", null); d.call("foo"); d.call("bar"); test.deepEqual(those, [c]); test.end(); }); tape("dispatch(type).on(.name, f) has no effect", function(test) { var d = dispatch.dispatch("foo", "bar"), a = {}, b = {}, those = []; function A() { those.push(a); } function B() { those.push(b); } d.on(".a", A).on("foo.a", B).on("bar", B); d.call("foo"); d.call("bar"); test.deepEqual(those, [b, b]); test.equal(d.on(".a"), undefined); test.end(); }); tape("dispatch(type…).copy() returns an isolated copy", function(test) { var foo = function() {}, bar = function() {}, d0 = dispatch.dispatch("foo", "bar").on("foo", foo).on("bar", bar), d1 = d0.copy(); test.equal(d1.on("foo"), foo); test.equal(d1.on("bar"), bar); // Changes to d1 don’t affect d0. test.equal(d1.on("bar", null), d1); test.equal(d1.on("bar"), undefined); test.equal(d0.on("bar"), bar); // Changes to d0 don’t affect d1. test.equal(d0.on("foo", null), d0); test.equal(d0.on("foo"), undefined); test.equal(d1.on("foo"), foo); test.end(); });