├── .gitignore
├── .jshintignore
├── .jshintrc
├── .travis.yml
├── LICENCE
├── README.md
├── add-event.js
├── docs.mli
├── dom-delegator.js
├── index.js
├── package.json
├── proxy-event.js
├── remove-event.js
└── test
├── dom-add-event.js
├── dom-add-multiple.js
├── dom-data-events.js
├── dom-delegator.js
├── dom-duplicate-event.js
├── dom-global-listeners.js
├── dom-handle.js
├── dom-propagation.js
├── dom-remove-event.js
├── dom-targets.js
├── dom-unlisten-to.js
├── index.js
└── lib
├── add-sink-event.js
├── create-event.js
├── h.js
└── sink-handler.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .monitor
3 | .*.swp
4 | .nodemonignore
5 | releases
6 | *.log
7 | *.err
8 | fleet.json
9 | public/browserify
10 | bin/*.json
11 | .bin
12 | build
13 | compile
14 | .lock-wscript
15 | coverage
16 | node_modules
17 |
--------------------------------------------------------------------------------
/.jshintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "maxdepth": 4,
3 | "maxstatements": 200,
4 | "maxcomplexity": 12,
5 | "maxlen": 80,
6 | "maxparams": 5,
7 |
8 | "curly": true,
9 | "eqeqeq": true,
10 | "immed": true,
11 | "latedef": false,
12 | "noarg": true,
13 | "noempty": true,
14 | "nonew": true,
15 | "undef": true,
16 | "unused": "vars",
17 | "trailing": true,
18 |
19 | "quotmark": true,
20 | "expr": true,
21 | "asi": true,
22 |
23 | "browser": false,
24 | "esnext": true,
25 | "devel": false,
26 | "node": false,
27 | "nonstandard": false,
28 |
29 | "predef": ["require", "module", "__dirname", "__filename"]
30 | }
31 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 0.8
4 | - "0.10"
5 | before_script:
6 | - npm install
7 | - npm install istanbul coveralls
8 | script: npm run travis-test
9 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Raynos.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # dom-delegator
2 |
3 |
10 |
11 |
12 |
13 | Decorate elements with delegated events
14 |
15 | `dom-delegator` allows you to attach an `EventHandler` to
16 | a dom element.
17 |
18 | When event of the correct type occurs `dom-delegator` will
19 | invoke your `EventHandler`
20 |
21 | This allows you to seperate your event listeners from your
22 | event writers. Sprinkle your event writers in the template
23 | in one part of your codebase. Attach listeners to the event
24 | sources in some other part of the code base.
25 |
26 | This decouples the event definition in the DOM from your event
27 | listeners in your application code.
28 |
29 | Also see [`html-delegator`](https://github.com/Raynos/html-delegator)
30 | for the same idea using html `data-` attributes.
31 |
32 | ## Example
33 |
34 | ```html
35 |
39 | ```
40 |
41 | ```js
42 | var document = require("global/document")
43 | var Delegator = require("dom-delegator")
44 | var EventEmitter = require("events").EventEmitter
45 |
46 | var del = Delegator()
47 | var emitter = EventEmitter()
48 | emitter.on('textClicked', function (value) {
49 | // either 'bar' or 'baz' depending on which
50 | // `` was clicked
51 | console.log("doSomething", value.type)
52 | })
53 |
54 | var elem = document.querySelector(".foo")
55 |
56 | // add individual elems. (in a different file?)
57 | del.addEventListener(elem.querySelector(".bar"), "click", function (ev) {
58 | emmitter.emit('textClicked', { type: 'bar' })
59 | })
60 | del.addEventListener(elem.querySelector(".baz"), "click", function (ev) {
61 | emitter.emit('textClicked', { type: 'baz' })
62 | })
63 | ```
64 |
65 | ## Example (global listeners)
66 |
67 | Sometimes you don't want to add events bound to an element but
68 | instead listen to them globally.
69 |
70 | ```js
71 | var Delegator = require("dom-delegator")
72 |
73 | var d = Delegator()
74 | d.addGlobalEventListener("keydown", function (ev) {
75 | // hit for every global key press
76 | // can implement keyboard shortcuts
77 |
78 |
79 | })
80 |
81 | d.addEventListener(document.documentElement, "keydown", function (ev) {
82 | // hit for every keydown that is not captured
83 | // by an element listener lower in the tree
84 |
85 | // by default dom-delegator does not bubble events up
86 | // to other listeners on parent nodes
87 |
88 | // you can use global event listeners to intercept everything
89 | // even if there are listeners lower in the tree
90 | })
91 | ```
92 |
93 | ## Installation
94 |
95 | `npm install dom-delegator`
96 |
97 | ## Contributors
98 |
99 | - Raynos
100 |
101 | ## MIT Licenced
102 |
103 | [1]: https://secure.travis-ci.org/Raynos/dom-delegator.png
104 | [2]: https://travis-ci.org/Raynos/dom-delegator
105 | [3]: https://badge.fury.io/js/dom-delegator.png
106 | [4]: https://badge.fury.io/js/dom-delegator
107 | [5]: https://coveralls.io/repos/Raynos/dom-delegator/badge.png
108 | [6]: https://coveralls.io/r/Raynos/dom-delegator
109 | [7]: https://gemnasium.com/Raynos/dom-delegator.png
110 | [8]: https://gemnasium.com/Raynos/dom-delegator
111 | [9]: https://david-dm.org/Raynos/dom-delegator.png
112 | [10]: https://david-dm.org/Raynos/dom-delegator
113 | [11]: https://ci.testling.com/Raynos/dom-delegator.png
114 | [12]: https://ci.testling.com/Raynos/dom-delegator
115 |
--------------------------------------------------------------------------------
/add-event.js:
--------------------------------------------------------------------------------
1 | var EvStore = require("ev-store")
2 |
3 | module.exports = addEvent
4 |
5 | function addEvent(target, type, handler) {
6 | var events = EvStore(target)
7 | var event = events[type]
8 |
9 | if (!event) {
10 | events[type] = handler
11 | } else if (Array.isArray(event)) {
12 | if (event.indexOf(handler) === -1) {
13 | event.push(handler)
14 | }
15 | } else if (event !== handler) {
16 | events[type] = [event, handler]
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/docs.mli:
--------------------------------------------------------------------------------
1 | type ProxyEvent := DOMEvent & {
2 | _rawEvent: DOMEvent,
3 | currentTarget: DOMNode,
4 | preventDefault: () => void,
5 | startPropagation: () => void
6 | }
7 |
8 | type Handle
: {
9 | type: "dom-delegator-handle"
10 | }
11 |
12 | type EventHandler := Function | {
13 | handleEvent: Function
14 | } | Handle
15 |
16 | type Delegator := {
17 | target: DOMNode,
18 | listenTo: (eventName: String) => void,
19 | unlistenTo: (eventName: String) => void,
20 |
21 | addEventListener: (DOMNode, String, EventHandler) => void,
22 | removeEventListener: (DOMNode, String, EventHandler) => void,
23 | addGlobalEventListener: (String, EventHandler) => void,
24 | removeGlobalEventListener: (String, EventHandler) => void
25 |
26 | allocateHandle : (fn: (T) => void) => Handle,
27 | transformHandle : (
28 | handle: Handle,
29 | lambda: (T, broadcast: (S) => void) => void
30 | ) => Handle
31 | }
32 |
33 | dom-delegator := (opts?: {
34 | defaultEvents?: Boolean,
35 | document?: Document
36 | }) => Delegator
37 |
38 | dom-delegator/dom-delegator := (doc?: Document) => Delegator
39 |
40 | dom-delegator/add-event := (
41 | target: DOMNode,
42 | type: String,
43 | fn: EventHandler
44 | ) => void
45 |
46 | dom-delegator/proxy-event := (DOMEvent) => ProxyEvent
47 |
48 | dom-delegator/remove-event := (
49 | target: DOMNode,
50 | type: String,
51 | fn: EventHandler
52 | ) => void
53 |
--------------------------------------------------------------------------------
/dom-delegator.js:
--------------------------------------------------------------------------------
1 | var globalDocument = require("global/document")
2 | var EvStore = require("ev-store")
3 | var createStore = require("weakmap-shim/create-store")
4 |
5 | var addEvent = require("./add-event.js")
6 | var removeEvent = require("./remove-event.js")
7 | var ProxyEvent = require("./proxy-event.js")
8 |
9 | var HANDLER_STORE = createStore()
10 |
11 | module.exports = DOMDelegator
12 |
13 | function DOMDelegator(document) {
14 | if (!(this instanceof DOMDelegator)) {
15 | return new DOMDelegator(document);
16 | }
17 |
18 | document = document || globalDocument
19 |
20 | this.target = document.documentElement
21 | this.events = {}
22 | this.rawEventListeners = {}
23 | this.globalListeners = {}
24 | }
25 |
26 | DOMDelegator.prototype.addEventListener = addEvent
27 | DOMDelegator.prototype.removeEventListener = removeEvent
28 |
29 | DOMDelegator.allocateHandle =
30 | function allocateHandle(func) {
31 | var handle = new Handle()
32 |
33 | HANDLER_STORE(handle).func = func;
34 |
35 | return handle
36 | }
37 |
38 | DOMDelegator.transformHandle =
39 | function transformHandle(handle, broadcast) {
40 | var func = HANDLER_STORE(handle).func
41 |
42 | return this.allocateHandle(function (ev) {
43 | broadcast(ev, func);
44 | })
45 | }
46 |
47 | DOMDelegator.prototype.addGlobalEventListener =
48 | function addGlobalEventListener(eventName, fn) {
49 | var listeners = this.globalListeners[eventName] || [];
50 | if (listeners.indexOf(fn) === -1) {
51 | listeners.push(fn)
52 | }
53 |
54 | this.globalListeners[eventName] = listeners;
55 | }
56 |
57 | DOMDelegator.prototype.removeGlobalEventListener =
58 | function removeGlobalEventListener(eventName, fn) {
59 | var listeners = this.globalListeners[eventName] || [];
60 |
61 | var index = listeners.indexOf(fn)
62 | if (index !== -1) {
63 | listeners.splice(index, 1)
64 | }
65 | }
66 |
67 | DOMDelegator.prototype.listenTo = function listenTo(eventName) {
68 | if (!(eventName in this.events)) {
69 | this.events[eventName] = 0;
70 | }
71 |
72 | this.events[eventName]++;
73 |
74 | if (this.events[eventName] !== 1) {
75 | return
76 | }
77 |
78 | var listener = this.rawEventListeners[eventName]
79 | if (!listener) {
80 | listener = this.rawEventListeners[eventName] =
81 | createHandler(eventName, this)
82 | }
83 |
84 | this.target.addEventListener(eventName, listener, true)
85 | }
86 |
87 | DOMDelegator.prototype.unlistenTo = function unlistenTo(eventName) {
88 | if (!(eventName in this.events)) {
89 | this.events[eventName] = 0;
90 | }
91 |
92 | if (this.events[eventName] === 0) {
93 | throw new Error("already unlistened to event.");
94 | }
95 |
96 | this.events[eventName]--;
97 |
98 | if (this.events[eventName] !== 0) {
99 | return
100 | }
101 |
102 | var listener = this.rawEventListeners[eventName]
103 |
104 | if (!listener) {
105 | throw new Error("dom-delegator#unlistenTo: cannot " +
106 | "unlisten to " + eventName)
107 | }
108 |
109 | this.target.removeEventListener(eventName, listener, true)
110 | }
111 |
112 | function createHandler(eventName, delegator) {
113 | var globalListeners = delegator.globalListeners;
114 | var delegatorTarget = delegator.target;
115 |
116 | return handler
117 |
118 | function handler(ev) {
119 | var globalHandlers = globalListeners[eventName] || []
120 |
121 | if (globalHandlers.length > 0) {
122 | var globalEvent = new ProxyEvent(ev);
123 | globalEvent.currentTarget = delegatorTarget;
124 | callListeners(globalHandlers, globalEvent)
125 | }
126 |
127 | findAndInvokeListeners(ev.target, ev, eventName)
128 | }
129 | }
130 |
131 | function findAndInvokeListeners(elem, ev, eventName) {
132 | var listener = getListener(elem, eventName)
133 |
134 | if (listener && listener.handlers.length > 0) {
135 | var listenerEvent = new ProxyEvent(ev);
136 | listenerEvent.currentTarget = listener.currentTarget
137 | callListeners(listener.handlers, listenerEvent)
138 |
139 | if (listenerEvent._bubbles) {
140 | var nextTarget = listener.currentTarget.parentNode
141 | findAndInvokeListeners(nextTarget, ev, eventName)
142 | }
143 | }
144 | }
145 |
146 | function getListener(target, type) {
147 | // terminate recursion if parent is `null`
148 | if (target === null || typeof target === "undefined") {
149 | return null
150 | }
151 |
152 | var events = EvStore(target)
153 | // fetch list of handler fns for this event
154 | var handler = events[type]
155 | var allHandler = events.event
156 |
157 | if (!handler && !allHandler) {
158 | return getListener(target.parentNode, type)
159 | }
160 |
161 | var handlers = [].concat(handler || [], allHandler || [])
162 | return new Listener(target, handlers)
163 | }
164 |
165 | function callListeners(handlers, ev) {
166 | handlers.forEach(function (handler) {
167 | if (typeof handler === "function") {
168 | handler(ev)
169 | } else if (typeof handler.handleEvent === "function") {
170 | handler.handleEvent(ev)
171 | } else if (handler.type === "dom-delegator-handle") {
172 | HANDLER_STORE(handler).func(ev)
173 | } else {
174 | throw new Error("dom-delegator: unknown handler " +
175 | "found: " + JSON.stringify(handlers));
176 | }
177 | })
178 | }
179 |
180 | function Listener(target, handlers) {
181 | this.currentTarget = target
182 | this.handlers = handlers
183 | }
184 |
185 | function Handle() {
186 | this.type = "dom-delegator-handle"
187 | }
188 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var Individual = require("individual")
2 | var cuid = require("cuid")
3 | var globalDocument = require("global/document")
4 |
5 | var DOMDelegator = require("./dom-delegator.js")
6 |
7 | var versionKey = "13"
8 | var cacheKey = "__DOM_DELEGATOR_CACHE@" + versionKey
9 | var cacheTokenKey = "__DOM_DELEGATOR_CACHE_TOKEN@" + versionKey
10 | var delegatorCache = Individual(cacheKey, {
11 | delegators: {}
12 | })
13 | var commonEvents = [
14 | "blur", "change", "click", "contextmenu", "dblclick",
15 | "error","focus", "focusin", "focusout", "input", "keydown",
16 | "keypress", "keyup", "load", "mousedown", "mouseup",
17 | "resize", "select", "submit", "touchcancel",
18 | "touchend", "touchstart", "unload"
19 | ]
20 |
21 | /* Delegator is a thin wrapper around a singleton `DOMDelegator`
22 | instance.
23 |
24 | Only one DOMDelegator should exist because we do not want
25 | duplicate event listeners bound to the DOM.
26 |
27 | `Delegator` will also `listenTo()` all events unless
28 | every caller opts out of it
29 | */
30 | module.exports = Delegator
31 |
32 | function Delegator(opts) {
33 | opts = opts || {}
34 | var document = opts.document || globalDocument
35 |
36 | var cacheKey = document[cacheTokenKey]
37 |
38 | if (!cacheKey) {
39 | cacheKey =
40 | document[cacheTokenKey] = cuid()
41 | }
42 |
43 | var delegator = delegatorCache.delegators[cacheKey]
44 |
45 | if (!delegator) {
46 | delegator = delegatorCache.delegators[cacheKey] =
47 | new DOMDelegator(document)
48 | }
49 |
50 | if (opts.defaultEvents !== false) {
51 | for (var i = 0; i < commonEvents.length; i++) {
52 | delegator.listenTo(commonEvents[i])
53 | }
54 | }
55 |
56 | return delegator
57 | }
58 |
59 | Delegator.allocateHandle = DOMDelegator.allocateHandle;
60 | Delegator.transformHandle = DOMDelegator.transformHandle;
61 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dom-delegator",
3 | "version": "13.1.0",
4 | "description": "Decorate elements with delegated events",
5 | "keywords": [],
6 | "author": "Raynos ",
7 | "repository": "git://github.com/Raynos/dom-delegator.git",
8 | "main": "index",
9 | "homepage": "https://github.com/Raynos/dom-delegator",
10 | "contributors": [
11 | {
12 | "name": "Raynos"
13 | }
14 | ],
15 | "bugs": {
16 | "url": "https://github.com/Raynos/dom-delegator/issues",
17 | "email": "raynos2@gmail.com"
18 | },
19 | "dependencies": {
20 | "cuid": "^1.2.4",
21 | "ev-store": "^7.0.0",
22 | "global": "^4.2.1",
23 | "individual": "^2.0.0",
24 | "inherits": "^2.0.1",
25 | "weakmap-shim": "^1.0.0",
26 | "xtend": "^2.2.0"
27 | },
28 | "devDependencies": {
29 | "cuid": "^1.2.4",
30 | "event-sinks": "~1.0.1",
31 | "istanbul": "^0.2.6",
32 | "jshint": "^2.5.0",
33 | "pre-commit": "0.0.5",
34 | "run-browser": "^1.3.1",
35 | "synthetic-dom-events": "git://github.com/Raynos/synthetic-dom-events",
36 | "tap-spec": "^0.1.9",
37 | "tape": "^2.12.3"
38 | },
39 | "licenses": [
40 | {
41 | "type": "MIT",
42 | "url": "http://github.com/Raynos/dom-delegator/raw/master/LICENSE"
43 | }
44 | ],
45 | "scripts": {
46 | "test": "jshint . && node ./test/index.js | tap-spec",
47 | "browser": "run-browser ./test/index.js",
48 | "phantom": "run-browser ./test/index.js -b | tap-spec",
49 | "cover": "istanbul cover --print detail ./test/index.js",
50 | "view-cover": "istanbul report html && google-chrome ./coverage/index.html"
51 | },
52 | "testling": {
53 | "files": "test/index.js",
54 | "browsers": [
55 | "ie/8..latest",
56 | "firefox/16..latest",
57 | "firefox/nightly",
58 | "chrome/22..latest",
59 | "chrome/canary",
60 | "opera/12..latest",
61 | "opera/next",
62 | "safari/5.1..latest",
63 | "ipad/6.0..latest",
64 | "iphone/6.0..latest",
65 | "android-browser/4.2..latest"
66 | ]
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/proxy-event.js:
--------------------------------------------------------------------------------
1 | var inherits = require("inherits")
2 |
3 | var ALL_PROPS = [
4 | "altKey", "bubbles", "cancelable", "ctrlKey",
5 | "eventPhase", "metaKey", "relatedTarget", "shiftKey",
6 | "target", "timeStamp", "type", "view", "which"
7 | ]
8 | var KEY_PROPS = ["char", "charCode", "key", "keyCode"]
9 | var MOUSE_PROPS = [
10 | "button", "buttons", "clientX", "clientY", "layerX",
11 | "layerY", "offsetX", "offsetY", "pageX", "pageY",
12 | "screenX", "screenY", "toElement"
13 | ]
14 |
15 | var rkeyEvent = /^key|input/
16 | var rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/
17 |
18 | module.exports = ProxyEvent
19 |
20 | function ProxyEvent(ev) {
21 | if (!(this instanceof ProxyEvent)) {
22 | return new ProxyEvent(ev)
23 | }
24 |
25 | if (rkeyEvent.test(ev.type)) {
26 | return new KeyEvent(ev)
27 | } else if (rmouseEvent.test(ev.type)) {
28 | return new MouseEvent(ev)
29 | }
30 |
31 | for (var i = 0; i < ALL_PROPS.length; i++) {
32 | var propKey = ALL_PROPS[i]
33 | this[propKey] = ev[propKey]
34 | }
35 |
36 | this._rawEvent = ev
37 | this._bubbles = false;
38 | }
39 |
40 | ProxyEvent.prototype.preventDefault = function () {
41 | this._rawEvent.preventDefault()
42 | }
43 |
44 | ProxyEvent.prototype.startPropagation = function () {
45 | this._bubbles = true;
46 | }
47 |
48 | function MouseEvent(ev) {
49 | for (var i = 0; i < ALL_PROPS.length; i++) {
50 | var propKey = ALL_PROPS[i]
51 | this[propKey] = ev[propKey]
52 | }
53 |
54 | for (var j = 0; j < MOUSE_PROPS.length; j++) {
55 | var mousePropKey = MOUSE_PROPS[j]
56 | this[mousePropKey] = ev[mousePropKey]
57 | }
58 |
59 | this._rawEvent = ev
60 | }
61 |
62 | inherits(MouseEvent, ProxyEvent)
63 |
64 | function KeyEvent(ev) {
65 | for (var i = 0; i < ALL_PROPS.length; i++) {
66 | var propKey = ALL_PROPS[i]
67 | this[propKey] = ev[propKey]
68 | }
69 |
70 | for (var j = 0; j < KEY_PROPS.length; j++) {
71 | var keyPropKey = KEY_PROPS[j]
72 | this[keyPropKey] = ev[keyPropKey]
73 | }
74 |
75 | this._rawEvent = ev
76 | }
77 |
78 | inherits(KeyEvent, ProxyEvent)
79 |
--------------------------------------------------------------------------------
/remove-event.js:
--------------------------------------------------------------------------------
1 | var EvStore = require("ev-store")
2 |
3 | module.exports = removeEvent
4 |
5 | function removeEvent(target, type, handler) {
6 | var events = EvStore(target)
7 | var event = events[type]
8 |
9 | if (!event) {
10 | return
11 | } else if (Array.isArray(event)) {
12 | var index = event.indexOf(handler)
13 | if (index !== -1) {
14 | event.splice(index, 1)
15 | }
16 | } else if (event === handler) {
17 | events[type] = null
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/test/dom-add-event.js:
--------------------------------------------------------------------------------
1 | var test = require("tape")
2 | var cuid = require("cuid")
3 | var setImmediate = require("timers").setImmediate
4 | var document = require("global/document")
5 | var EventSinks = require("event-sinks/geval")
6 |
7 | var h = require("./lib/h.js")
8 | var addSinkEvent = require("./lib/add-sink-event.js")
9 | var createEvent = require("./lib/create-event.js")
10 |
11 | var Delegator = require("../index.js")
12 |
13 | test("Delegator is a function", function (assert) {
14 | assert.equal(typeof Delegator, "function")
15 | assert.end()
16 | })
17 |
18 | test("can listen to events", function (assert) {
19 | var elem = h("div")
20 | document.body.appendChild(elem)
21 |
22 | var d = Delegator(elem)
23 | var events = EventSinks(d.id, ["foo"])
24 | var called = 0
25 | var id = cuid()
26 |
27 | addSinkEvent(elem, "click", events.sinks.foo, {
28 | id: id
29 | })
30 |
31 | events.foo(function (value) {
32 | called++
33 |
34 | assert.equal(value.id, id)
35 | })
36 |
37 | var ev = createEvent("click")
38 | elem.dispatchEvent(ev)
39 |
40 | setImmediate(function () {
41 | assert.equal(called, 1)
42 |
43 | document.body.removeChild(elem)
44 | assert.end()
45 | })
46 | })
47 |
48 | test("can set different data on same sink", function (assert) {
49 | var elem = h("foo", [
50 | h("bar"),
51 | h("baz")
52 | ])
53 | document.body.appendChild(elem)
54 |
55 | var d = Delegator(elem)
56 | var events = EventSinks(d.id, ["foo"])
57 | var foo = events.sinks.foo
58 | var values = []
59 |
60 | addSinkEvent(elem.childNodes[0], "click", foo, {
61 | name: "bar"
62 | })
63 |
64 | addSinkEvent(elem.childNodes[1], "click", foo, {
65 | name: "baz"
66 | })
67 |
68 | events.foo(function (value) {
69 | values.push(value)
70 | })
71 |
72 | var ev = createEvent("click")
73 | elem.childNodes[0].dispatchEvent(ev)
74 |
75 | var ev2 = createEvent("click")
76 | elem.childNodes[1].dispatchEvent(ev2)
77 |
78 | setImmediate(function () {
79 | assert.equal(values.length, 2)
80 | assert.equal(values[0].name, "bar")
81 | assert.equal(values[1].name, "baz")
82 |
83 | document.body.removeChild(elem)
84 | assert.end()
85 | })
86 | })
87 |
88 | test("can register multiple sinks", function (assert) {
89 | var elem = h("foo", [
90 | h("bar"),
91 | h("baz")
92 | ])
93 | document.body.appendChild(elem)
94 |
95 | var d = Delegator(elem)
96 | var events = EventSinks(d.id, ["bar", "baz"])
97 |
98 | var bar = events.sinks.bar, baz = events.sinks.baz
99 | var hash = {}
100 |
101 | addSinkEvent(elem.childNodes[0], "click", bar, {
102 | name: "baz"
103 | })
104 |
105 | addSinkEvent(elem.childNodes[1], "click", baz, {
106 | name: "bar"
107 | })
108 |
109 | events.bar(function (value) {
110 | hash.bar = value
111 | })
112 |
113 | events.baz(function (value) {
114 | hash.baz = value
115 | })
116 |
117 | var ev = createEvent("click")
118 | elem.childNodes[0].dispatchEvent(ev)
119 |
120 | var ev2 = createEvent("click")
121 | elem.childNodes[1].dispatchEvent(ev2)
122 |
123 | setImmediate(function () {
124 | assert.ok("bar" in hash)
125 | assert.ok("baz" in hash)
126 | assert.equal(hash.bar.name, "baz")
127 | assert.equal(hash.baz.name, "bar")
128 |
129 | document.body.removeChild(elem)
130 | assert.end()
131 | })
132 | })
133 |
--------------------------------------------------------------------------------
/test/dom-add-multiple.js:
--------------------------------------------------------------------------------
1 | var test = require("tape")
2 | var setImmediate = require("timers").setImmediate
3 | var document = require("global/document")
4 | var Sink = require("event-sinks/sink")
5 |
6 | var h = require("./lib/h.js")
7 | var addSinkEvent = require("./lib/add-sink-event.js")
8 | var createEvent = require("./lib/create-event.js")
9 |
10 | var Delegator = require("../index.js")
11 |
12 | test("adding multiple listeners", function (assert) {
13 | var elem = h("div")
14 | document.body.appendChild(elem)
15 |
16 | var d = Delegator(elem)
17 | var values = []
18 | var sink = Sink(d.id, "", function (value) {
19 | values.push(value)
20 | })
21 |
22 | addSinkEvent(elem, "event", sink, { key: "foo" })
23 | addSinkEvent(elem, "event", sink, { key: "bar" })
24 |
25 | var ev = createEvent("click")
26 | elem.dispatchEvent(ev)
27 |
28 | setImmediate(function () {
29 | assert.equal(values.length, 2)
30 | assert.equal(values[0].key, "foo")
31 | assert.equal(values[1].key, "bar")
32 |
33 | document.body.removeChild(elem)
34 | assert.end()
35 | })
36 | })
37 |
38 | test("add multiple listeners of different types", function (assert) {
39 | var elem = h("div")
40 | document.body.appendChild(elem)
41 |
42 | var d = Delegator(elem)
43 | var values = []
44 | var sink = Sink(d.id, "", function (value) {
45 | values.push(value)
46 | })
47 |
48 | addSinkEvent(elem, "event", sink, { key: "foo" })
49 | addSinkEvent(elem, "click", sink, { key: "bar" })
50 |
51 | var ev = createEvent("click")
52 | elem.dispatchEvent(ev)
53 |
54 | setImmediate(function () {
55 | assert.equal(values.length, 2)
56 | assert.equal(values[0].key, "bar")
57 | assert.equal(values[1].key, "foo")
58 |
59 | document.body.removeChild(elem)
60 | assert.end()
61 | })
62 | })
63 |
--------------------------------------------------------------------------------
/test/dom-data-events.js:
--------------------------------------------------------------------------------
1 | var test = require("tape")
2 | var setImmediate = require("timers").setImmediate
3 | var document = require("global/document")
4 | var EvStore = require("ev-store")
5 |
6 | var h = require("./lib/h.js")
7 | var createEvent = require("./lib/create-event.js")
8 |
9 | var Delegator = require("../index.js")
10 |
11 | test("setting event listeners with ev-store directly", function (assert) {
12 | var elem = h("div")
13 | document.body.appendChild(elem)
14 |
15 | Delegator()
16 | var values = []
17 |
18 | EvStore(elem).click = function (ev) {
19 | values.push(ev)
20 | }
21 |
22 | var ev = createEvent("click")
23 | elem.dispatchEvent(ev)
24 |
25 | setImmediate(function () {
26 | assert.equal(values.length, 1)
27 | assert.equal(values[0].target, elem)
28 | assert.equal(typeof values[0].preventDefault, "function")
29 | assert.equal(typeof values[0].startPropagation, "function")
30 |
31 | document.body.removeChild(elem)
32 | assert.end()
33 | })
34 | })
35 |
36 | test("setting an id'd event handler", function (assert) {
37 | var elem = h("div")
38 | document.body.appendChild(elem)
39 |
40 | var d = Delegator()
41 | var values = []
42 | var eventValues = []
43 |
44 | var handler = {
45 | handleEvent: function (ev) {
46 | values.push(ev)
47 | },
48 | id: d.id
49 | }
50 | var eventHandler = {
51 | handleEvent: function (ev) {
52 | eventValues.push(ev)
53 | },
54 | id: d.id
55 | }
56 |
57 | EvStore(elem).click = handler
58 | EvStore(elem).event = eventHandler
59 |
60 | var ev = createEvent("click")
61 | elem.dispatchEvent(ev)
62 |
63 | setImmediate(function () {
64 | assert.equal(values.length, 1)
65 | assert.equal(values[0].target, elem)
66 |
67 | assert.equal(eventValues.length, 1)
68 | assert.equal(eventValues[0].target, elem)
69 | assert.equal(eventValues[0].type, "click")
70 |
71 | document.body.removeChild(elem)
72 | assert.end()
73 | })
74 | })
75 |
76 | test("setting data-event to array", function (assert) {
77 | var elem = h("div")
78 | document.body.appendChild(elem)
79 |
80 | Delegator()
81 | var values = []
82 |
83 | EvStore(elem).click = [function (ev) {
84 | values.push(ev)
85 | }, function (ev) {
86 | values.push(ev)
87 | }]
88 |
89 | var ev = createEvent("click")
90 | elem.dispatchEvent(ev)
91 |
92 | setImmediate(function () {
93 | assert.equal(values.length, 2)
94 | assert.equal(values[0], values[1])
95 | assert.equal(values[0].target, elem)
96 |
97 | document.body.removeChild(elem)
98 | assert.end()
99 | })
100 | })
101 |
102 | test("data-event to array of id'd handlers", function (assert) {
103 | var elem = h("div")
104 | document.body.appendChild(elem)
105 |
106 | var d = Delegator()
107 | var changes = []
108 | var submits = []
109 |
110 | var submitHandler = {
111 | handleEvent: function (ev) {
112 | submits.push(ev)
113 | },
114 | id: d.id
115 | }
116 | var changeHandler = {
117 | handleEvent: function (ev) {
118 | changes.push(ev)
119 | },
120 | id: d.id
121 | }
122 |
123 | EvStore(elem).event = [submitHandler, changeHandler]
124 |
125 | var ev = createEvent("click")
126 | elem.dispatchEvent(ev)
127 |
128 | setImmediate(function () {
129 | assert.equal(submits.length, 1)
130 | assert.equal(changes.length, 1)
131 |
132 | assert.equal(submits[0], changes[0])
133 | assert.equal(submits[0].target, elem)
134 |
135 | document.body.removeChild(elem)
136 | assert.end()
137 | })
138 | })
139 |
--------------------------------------------------------------------------------
/test/dom-delegator.js:
--------------------------------------------------------------------------------
1 | var test = require("tape")
2 | var setImmediate = require("timers").setImmediate
3 | var document = require("global/document")
4 |
5 | var h = require("./lib/h.js")
6 | var createEvent = require("./lib/create-event.js")
7 |
8 | var addEvent = require("../add-event.js")
9 | var Delegator = require("../index.js")
10 |
11 | test("delegator with no args", function (assert) {
12 | var elem = h("div")
13 | document.body.appendChild(elem)
14 |
15 | Delegator()
16 | var values = []
17 |
18 | addEvent(elem, "click", function (ev) {
19 | values.push(ev)
20 | })
21 |
22 | var ev = createEvent("click")
23 | elem.dispatchEvent(ev)
24 |
25 | setImmediate(function () {
26 | assert.equal(values.length, 1)
27 | assert.equal(values[0].target, elem)
28 |
29 | document.body.removeChild(elem)
30 | assert.end()
31 | })
32 | })
33 |
34 | test("delegator with addEventListener", function (assert) {
35 | var elem = h("div")
36 | document.body.appendChild(elem)
37 |
38 | var d = Delegator()
39 | var values = []
40 |
41 | d.addEventListener(elem, "click", function (ev) {
42 | values.push(ev)
43 | })
44 |
45 | var ev = createEvent("click")
46 | elem.dispatchEvent(ev)
47 |
48 | setImmediate(function () {
49 | assert.equal(values.length, 1)
50 | assert.equal(values[0].target, elem)
51 |
52 | document.body.removeChild(elem)
53 | assert.end()
54 | })
55 | })
56 |
57 | test("delegator with no args", function (assert) {
58 | var elem = h("div")
59 | document.body.appendChild(elem)
60 |
61 | var d = Delegator({
62 | defaultEvents: false
63 | })
64 | d.listenTo("click")
65 | var values = []
66 |
67 | addEvent(elem, "click", function (ev) {
68 | values.push(ev)
69 | })
70 |
71 | var ev = createEvent("click")
72 | elem.dispatchEvent(ev)
73 |
74 | setImmediate(function () {
75 | assert.equal(values.length, 1)
76 | assert.equal(values[0].target, elem)
77 |
78 | document.body.removeChild(elem)
79 | assert.end()
80 | })
81 | })
82 |
--------------------------------------------------------------------------------
/test/dom-duplicate-event.js:
--------------------------------------------------------------------------------
1 | var test = require("tape")
2 | var setImmediate = require("timers").setImmediate
3 | var document = require("global/document")
4 |
5 | var h = require("./lib/h.js")
6 | var createEvent = require("./lib/create-event.js")
7 |
8 | var removeEvent = require("../remove-event.js")
9 | var addEvent = require("../add-event.js")
10 | var Delegator = require("../index.js")
11 |
12 | test("adding same function twice", function (assert) {
13 | var elem = h("div")
14 | document.body.appendChild(elem)
15 |
16 | Delegator()
17 | var values = []
18 |
19 | var fn = function (ev) {
20 | values.push(ev)
21 | }
22 |
23 | addEvent(elem, "click", fn)
24 | addEvent(elem, "click", fn)
25 |
26 | var ev = createEvent("click")
27 | elem.dispatchEvent(ev)
28 |
29 | setImmediate(function () {
30 | assert.equal(values.length, 1)
31 | assert.equal(values[0] && values[0].target, elem)
32 |
33 | document.body.removeChild(elem)
34 | assert.end()
35 | })
36 | })
37 |
38 | test("adding function twice for multi events", function (assert) {
39 | var elem = h("div")
40 | document.body.appendChild(elem)
41 |
42 | Delegator()
43 | var values = []
44 |
45 | var fn = function (ev) {
46 | values.push(["fn", ev])
47 | }
48 |
49 | var fn2 = function (ev) {
50 | values.push(["fn2", ev])
51 | }
52 |
53 | addEvent(elem, "click", fn2)
54 | addEvent(elem, "click", fn2)
55 |
56 | addEvent(elem, "click", fn)
57 | addEvent(elem, "click", fn)
58 |
59 | var ev = createEvent("click")
60 | elem.dispatchEvent(ev)
61 |
62 | setImmediate(function () {
63 | assert.equal(values.length, 2)
64 | assert.equal(values[0][0], "fn2")
65 | assert.equal(values[1][0], "fn")
66 | assert.equal(values[0] && values[0][1].target, elem)
67 |
68 | document.body.removeChild(elem)
69 | assert.end()
70 | })
71 | })
72 |
73 | test("adding same event handler twice", function (assert) {
74 | var elem = h("div")
75 | document.body.appendChild(elem)
76 |
77 | Delegator()
78 | var values = []
79 |
80 | var handler = {
81 | handleEvent: function (ev) {
82 | values.push(ev)
83 | }
84 | }
85 |
86 | addEvent(elem, "click", handler)
87 | addEvent(elem, "click", handler)
88 |
89 | var ev = createEvent("click")
90 | elem.dispatchEvent(ev)
91 |
92 | setImmediate(function () {
93 | assert.equal(values.length, 1)
94 | assert.equal(values[0] && values[0].target, elem)
95 |
96 | document.body.removeChild(elem)
97 | assert.end()
98 | })
99 | })
100 |
101 | test("double removing multiple events", function (assert) {
102 | var elem = h("div")
103 | document.body.appendChild(elem)
104 |
105 | Delegator()
106 | var values = []
107 |
108 | var fn = function (ev) {
109 | values.push(["fn", ev])
110 | }
111 |
112 | var fn2 = function (ev) {
113 | values.push(["fn2", ev])
114 | }
115 |
116 | addEvent(elem, "click", fn2)
117 | addEvent(elem, "click", fn2)
118 |
119 | addEvent(elem, "click", fn)
120 | addEvent(elem, "click", fn)
121 | removeEvent(elem, "click", fn)
122 | removeEvent(elem, "click", fn)
123 |
124 | var ev = createEvent("click")
125 | elem.dispatchEvent(ev)
126 |
127 | setImmediate(function () {
128 | assert.equal(values.length, 1)
129 | assert.equal(values[0][0], "fn2")
130 | assert.equal(values[0] && values[0][1].target, elem)
131 |
132 | document.body.removeChild(elem)
133 | assert.end()
134 | })
135 | })
136 |
--------------------------------------------------------------------------------
/test/dom-global-listeners.js:
--------------------------------------------------------------------------------
1 | var test = require("tape")
2 | var setImmediate = require("timers").setImmediate
3 | var document = require("global/document")
4 |
5 | var createEvent = require("./lib/create-event.js")
6 |
7 | var Delegator = require("../index.js")
8 |
9 | test("global listeners", function (assert) {
10 | var d = Delegator()
11 | var values = []
12 |
13 | function fn(ev) {
14 | values.push(["fn", ev])
15 | }
16 |
17 | function fn2(ev) {
18 | values.push(["fn2", ev])
19 | }
20 |
21 | d.addGlobalEventListener("click", fn)
22 | d.addGlobalEventListener("click", fn2)
23 |
24 |
25 | var ev2 = createEvent("click")
26 | document.documentElement.dispatchEvent(ev2)
27 |
28 | d.removeGlobalEventListener("click", fn)
29 |
30 | var ev3 = createEvent("click")
31 | document.documentElement.dispatchEvent(ev3)
32 |
33 | setImmediate(function () {
34 | assert.equal(values.length, 3)
35 | assert.equal(values[0][0], "fn")
36 | assert.equal(values[1][0], "fn2")
37 | assert.equal(values[2][0], "fn2")
38 |
39 | assert.end()
40 | })
41 | })
42 |
43 | test("duplicates", function (assert) {
44 | var d = Delegator()
45 | var values = []
46 |
47 | function fn(ev) {
48 | values.push(["fn", ev])
49 | }
50 |
51 | function fn2(ev) {
52 | values.push(["fn2", ev])
53 | }
54 |
55 | d.addGlobalEventListener("click", fn)
56 | d.addGlobalEventListener("click", fn)
57 | d.addGlobalEventListener("click", fn2)
58 | d.addGlobalEventListener("click", fn2)
59 |
60 |
61 | var ev2 = createEvent("click")
62 | document.documentElement.dispatchEvent(ev2)
63 |
64 | setImmediate(function () {
65 | assert.equal(values.length, 2)
66 | assert.equal(values[0][0], "fn")
67 | assert.equal(values[1][0], "fn2")
68 |
69 | assert.end()
70 | })
71 | })
72 |
73 | test("duplicate removes", function (assert) {
74 | var d = Delegator()
75 | var values = []
76 |
77 | function fn(ev) {
78 | values.push(["fn", ev])
79 | }
80 |
81 | function fn2(ev) {
82 | values.push(["fn2", ev])
83 | }
84 |
85 | d.addGlobalEventListener("click", fn)
86 | d.addGlobalEventListener("click", fn2)
87 |
88 |
89 | var ev2 = createEvent("click")
90 | document.documentElement.dispatchEvent(ev2)
91 |
92 | assert.doesNotThrow(function () {
93 | d.removeGlobalEventListener("badevent", fn)
94 | })
95 |
96 | d.removeGlobalEventListener("click", fn)
97 | d.removeGlobalEventListener("click", fn)
98 |
99 | var ev3 = createEvent("click")
100 | document.documentElement.dispatchEvent(ev3)
101 |
102 | setImmediate(function () {
103 | assert.equal(values.length, 3)
104 | assert.equal(values[0][0], "fn")
105 | assert.equal(values[1][0], "fn2")
106 | assert.equal(values[2][0], "fn2")
107 |
108 | assert.end()
109 | })
110 | })
111 |
--------------------------------------------------------------------------------
/test/dom-handle.js:
--------------------------------------------------------------------------------
1 | var test = require("tape")
2 | var document = require("global/document")
3 | var setImmediate = require("timers").setImmediate
4 |
5 | var h = require("./lib/h.js")
6 | var createEvent = require("./lib/create-event.js")
7 |
8 | var Delegator = require("../index.js")
9 |
10 | test("can listen to handles", function (assert) {
11 | var elem = allocElem()
12 | var d = Delegator()
13 |
14 | var results = []
15 |
16 | var handle = Delegator.allocateHandle(function (ev) {
17 | results.push(ev)
18 | })
19 | d.addEventListener(elem, "click", handle)
20 |
21 | dispatchClick(elem, function () {
22 | assert.ok(true)
23 | assert.equal(results.length, 1)
24 | assert.equal(results[0].type, "click")
25 |
26 | freeElem(elem)
27 | assert.end()
28 | })
29 | })
30 |
31 | test("can transform a handle", function (assert) {
32 | var elem = allocElem()
33 | var d = Delegator()
34 |
35 | var results = []
36 |
37 | var handle = Delegator.allocateHandle(function (ev) {
38 | results.push(ev)
39 | })
40 |
41 | var handle2 = Delegator.transformHandle(handle, function (ev, write) {
42 | write({ foo: "bar", type: ev.type })
43 | })
44 | d.addEventListener(elem, "click", handle2)
45 |
46 | dispatchClick(elem, function () {
47 | assert.ok(true)
48 | assert.equal(results.length, 1)
49 |
50 | freeElem(elem)
51 | assert.end()
52 | })
53 | })
54 |
55 | test("transform handle respects falsey", function (assert) {
56 | var elem = allocElem()
57 | var d = Delegator()
58 |
59 | var results = []
60 |
61 | var handle = Delegator.allocateHandle(function (ev) {
62 | results.push(ev)
63 | })
64 |
65 | var handle2 = Delegator.transformHandle(handle, function (ev) {
66 | return null
67 | })
68 | d.addEventListener(elem, "click", handle2)
69 |
70 | dispatchClick(elem, function () {
71 | assert.ok(true)
72 | assert.equal(results.length, 0)
73 |
74 | freeElem(elem)
75 | assert.end()
76 | })
77 | })
78 |
79 | function allocElem() {
80 | var elem = h("div")
81 | document.body.appendChild(elem)
82 |
83 | return elem
84 | }
85 |
86 | function freeElem(elem) {
87 | document.body.removeChild(elem)
88 | }
89 |
90 | function dispatchClick(elem, cb) {
91 | var ev = createEvent("click")
92 | elem.dispatchEvent(ev)
93 |
94 | setImmediate(cb)
95 | }
96 |
--------------------------------------------------------------------------------
/test/dom-propagation.js:
--------------------------------------------------------------------------------
1 | var test = require("tape")
2 | var setImmediate = require("timers").setImmediate
3 | var document = require("global/document")
4 |
5 | var createEvent = require("./lib/create-event.js")
6 | var h = require("./lib/h.js")
7 |
8 | var Delegator = require("../index.js")
9 |
10 | test("multiple listeners and propagation", function (assert) {
11 | var elem = h("div", [
12 | h("p", [
13 | h("span")
14 | ])
15 | ])
16 | document.body.appendChild(elem)
17 |
18 | var d = Delegator()
19 | var tuples = []
20 |
21 | d.addEventListener(elem, "click", onParent)
22 | d.addEventListener(elem.childNodes[0], "click", onChild)
23 | d.addEventListener(elem.childNodes[0].childNodes[0],
24 | "click", onGrandChild)
25 |
26 |
27 | var ev = createEvent("click")
28 | elem.childNodes[0].childNodes[0].dispatchEvent(ev)
29 |
30 | setImmediate(function () {
31 | assert.equal(tuples.length, 3)
32 | assert.equal(tuples[0][0], "grandchild")
33 | assert.equal(tuples[1][0], "child")
34 | assert.equal(tuples[2][0], "parent")
35 |
36 | assert.equal(tuples[0][1].currentTarget.tagName, "SPAN")
37 | assert.equal(tuples[1][1].currentTarget.tagName, "P")
38 | assert.equal(tuples[2][1].currentTarget.tagName, "DIV")
39 |
40 | document.body.removeChild(elem)
41 | assert.end()
42 | })
43 |
44 | function onParent(ev) {
45 | tuples.push(["parent", ev])
46 | }
47 | function onChild(ev) {
48 | tuples.push(["child", ev])
49 | ev.startPropagation()
50 | }
51 | function onGrandChild(ev) {
52 | tuples.push(["grandchild", ev])
53 | ev.startPropagation()
54 | }
55 | })
56 |
57 | test("multiple listeners and partial propagation", function (assert) {
58 | var elem = h("div", [
59 | h("p", [
60 | h("span")
61 | ])
62 | ])
63 | document.body.appendChild(elem)
64 |
65 | var d = Delegator()
66 | var tuples = []
67 |
68 | d.addEventListener(elem, "click", onParent)
69 | d.addEventListener(elem.childNodes[0], "click", onChild)
70 | d.addEventListener(elem.childNodes[0].childNodes[0],
71 | "click", onGrandChild)
72 |
73 |
74 | var ev = createEvent("click")
75 | elem.childNodes[0].childNodes[0].dispatchEvent(ev)
76 |
77 | setImmediate(function () {
78 | assert.equal(tuples.length, 2)
79 | assert.equal(tuples[0][0], "grandchild")
80 | assert.equal(tuples[1][0], "child")
81 |
82 | assert.equal(tuples[0][1].currentTarget.tagName, "SPAN")
83 | assert.equal(tuples[1][1].currentTarget.tagName, "P")
84 |
85 | document.body.removeChild(elem)
86 | assert.end()
87 | })
88 |
89 | function onParent(ev) {
90 | tuples.push(["parent", ev])
91 | }
92 | function onChild(ev) {
93 | tuples.push(["child", ev])
94 | }
95 | function onGrandChild(ev) {
96 | tuples.push(["grandchild", ev])
97 | ev.startPropagation()
98 | }
99 | })
100 |
101 | test("multiple listeners and no propagation", function (assert) {
102 | var elem = h("div", [
103 | h("p", [
104 | h("span")
105 | ])
106 | ])
107 | document.body.appendChild(elem)
108 |
109 | var d = Delegator()
110 | var tuples = []
111 |
112 | d.addEventListener(elem, "click", onParent)
113 | d.addEventListener(elem.childNodes[0], "click", onChild)
114 | d.addEventListener(elem.childNodes[0].childNodes[0],
115 | "click", onGrandChild)
116 |
117 |
118 | var ev = createEvent("click")
119 | elem.childNodes[0].childNodes[0].dispatchEvent(ev)
120 |
121 | setImmediate(function () {
122 | assert.equal(tuples.length, 1)
123 | assert.equal(tuples[0][0], "grandchild")
124 |
125 | assert.equal(tuples[0][1].currentTarget.tagName, "SPAN")
126 |
127 | document.body.removeChild(elem)
128 | assert.end()
129 | })
130 |
131 | function onParent(ev) {
132 | tuples.push(["parent", ev])
133 | }
134 | function onChild(ev) {
135 | tuples.push(["child", ev])
136 | }
137 | function onGrandChild(ev) {
138 | tuples.push(["grandchild", ev])
139 | }
140 | })
141 |
--------------------------------------------------------------------------------
/test/dom-remove-event.js:
--------------------------------------------------------------------------------
1 | var test = require("tape")
2 | var setImmediate = require("timers").setImmediate
3 | var document = require("global/document")
4 |
5 | var h = require("./lib/h.js")
6 | var createEvent = require("./lib/create-event.js")
7 |
8 | var addEvent = require("../add-event.js")
9 | var removeEvent = require("../remove-event.js")
10 | var Delegator = require("../index.js")
11 |
12 | test("removing event", function (assert) {
13 | var elem = h("div")
14 | document.body.appendChild(elem)
15 |
16 | Delegator()
17 | var values = []
18 |
19 | function fn(ev) {
20 | values.push(ev)
21 | }
22 |
23 | addEvent(elem, "click", fn)
24 | removeEvent(elem, "click", fn)
25 |
26 |
27 | var ev = createEvent("click")
28 | elem.dispatchEvent(ev)
29 |
30 | setImmediate(function () {
31 | assert.equal(values.length, 0)
32 |
33 | document.body.removeChild(elem)
34 | assert.end()
35 | })
36 | })
37 |
38 | test("remove one of multiple events", function (assert) {
39 | var elem = h("div")
40 | document.body.appendChild(elem)
41 |
42 | var d = Delegator()
43 | var values = []
44 |
45 | function fn1(ev) {
46 | values.push(["fn1", ev])
47 | }
48 |
49 | function fn2(ev) {
50 | values.push(["fn2", ev])
51 | }
52 |
53 | function fn3(ev) {
54 | values.push(["fn3", ev])
55 | }
56 |
57 | d.addEventListener(elem, "click", fn1)
58 | d.addEventListener(elem, "click", fn2)
59 | d.addEventListener(elem, "click", fn3)
60 |
61 | d.removeEventListener(elem, "click", fn2)
62 |
63 | var ev = createEvent("click")
64 | elem.dispatchEvent(ev)
65 |
66 | setImmediate(function () {
67 | assert.equal(values.length, 2)
68 | assert.equal(values[0][0], "fn1")
69 | assert.equal(values[1][0], "fn3")
70 | assert.equal(values[0][1].target, elem)
71 |
72 | document.body.removeChild(elem)
73 | assert.end()
74 | })
75 | })
76 |
77 | test("removing event doesn't throw", function (assert) {
78 | var elem = h("div")
79 | document.body.appendChild(elem)
80 | var d = Delegator()
81 |
82 | assert.doesNotThrow(function () {
83 | d.removeEventListener(elem, "click", function () {})
84 | })
85 |
86 | document.body.removeChild(elem)
87 | assert.end()
88 | })
89 |
90 | test("removing other listener", function (assert) {
91 | var elem = h("div")
92 | document.body.appendChild(elem)
93 | var d = Delegator()
94 |
95 | d.addEventListener(elem, "click", function () {})
96 |
97 | assert.doesNotThrow(function () {
98 | d.removeEventListener(elem, "click", function () {})
99 | })
100 |
101 | document.body.removeChild(elem)
102 | assert.end()
103 | })
104 |
--------------------------------------------------------------------------------
/test/dom-targets.js:
--------------------------------------------------------------------------------
1 | var test = require("tape")
2 | var setImmediate = require("timers").setImmediate
3 | var document = require("global/document")
4 |
5 | var h = require("./lib/h.js")
6 | var createEvent = require("./lib/create-event.js")
7 |
8 | var addEvent = require("../add-event.js")
9 | var Delegator = require("../index.js")
10 |
11 | test("dispatched events have correct targets", function (assert) {
12 | var elem = h("div", [ h("div") ])
13 | document.body.appendChild(elem)
14 |
15 | Delegator()
16 | var values = []
17 |
18 | addEvent(elem, "click", function (ev) {
19 | values.push(ev)
20 | })
21 |
22 | var ev = createEvent("click")
23 | elem.childNodes[0].dispatchEvent(ev)
24 |
25 | setImmediate(function () {
26 | assert.equal(values.length, 1)
27 | assert.equal(values[0].target, elem.childNodes[0])
28 | assert.equal(values[0].currentTarget, elem)
29 |
30 | document.body.removeChild(elem)
31 | assert.end()
32 | })
33 | })
34 |
35 | test("dispatch event with no handler", function (assert) {
36 | var elem = h("div")
37 | document.body.appendChild(elem)
38 |
39 | Delegator()
40 | var values = []
41 |
42 | addEvent(elem, "click", function (ev) {
43 | values.push(ev)
44 | })
45 |
46 | var ev = createEvent("keypress")
47 | elem.dispatchEvent(ev)
48 |
49 | setImmediate(function () {
50 | assert.equal(values.length, 0)
51 |
52 | document.body.removeChild(elem)
53 | assert.end()
54 | })
55 | })
56 |
--------------------------------------------------------------------------------
/test/dom-unlisten-to.js:
--------------------------------------------------------------------------------
1 | var test = require("tape")
2 | var setImmediate = require("timers").setImmediate
3 | var Document = require("global/document").constructor
4 |
5 | var h = require("./lib/h.js")
6 | var createEvent = require("./lib/create-event.js")
7 |
8 | var addEvent = require("../add-event.js")
9 | var Delegator = require("../dom-delegator.js")
10 |
11 | test("unlistening to event", function (assert) {
12 | var elem = h("div")
13 | var doc = new Document()
14 | doc.body.appendChild(elem)
15 |
16 | var d = Delegator(doc)
17 | d.listenTo("click")
18 | var values = []
19 |
20 | var fn = function (ev) {
21 | values.push(ev)
22 | }
23 |
24 | addEvent(elem, "click", fn)
25 |
26 | d.unlistenTo("click")
27 |
28 | assert.throws(function () {
29 | d.unlistenTo("click")
30 | })
31 |
32 | var ev = createEvent("click")
33 | elem.dispatchEvent(ev)
34 |
35 | setImmediate(function () {
36 | assert.equal(values.length, 0)
37 |
38 | doc.body.removeChild(elem)
39 | assert.end()
40 | })
41 | })
42 |
43 | test("delegator throws if mutated manually", function (assert) {
44 | var d = Delegator()
45 |
46 | d.listenTo("foobar")
47 |
48 | // NAUGHTY MUTATE
49 | d.rawEventListeners.foobar = null
50 |
51 | assert.throws(function () {
52 | d.unlistenTo("foobar")
53 | }, /cannot unlisten to foobar/)
54 |
55 | assert.end()
56 | })
57 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | require("./dom-add-event.js")
2 | require("./dom-remove-event.js")
3 | require("./dom-add-multiple.js")
4 | require("./dom-targets.js")
5 | require("./dom-delegator.js")
6 | require("./dom-data-events.js")
7 | require("./dom-duplicate-event.js")
8 | require("./dom-unlisten-to.js")
9 | require("./dom-global-listeners.js")
10 | require("./dom-propagation.js")
11 | require("./dom-handle.js")
12 |
--------------------------------------------------------------------------------
/test/lib/add-sink-event.js:
--------------------------------------------------------------------------------
1 | var addEvent = require("../../add-event.js")
2 | var SinkHandler = require("./sink-handler.js")
3 |
4 | module.exports = addSinkEvent
5 |
6 | function addSinkEvent(elem, eventName, sink, data) {
7 | return addEvent(elem, eventName,
8 | new SinkHandler(sink, data))
9 | }
10 |
--------------------------------------------------------------------------------
/test/lib/create-event.js:
--------------------------------------------------------------------------------
1 | var DOMEvent = require("synthetic-dom-events")
2 |
3 | module.exports = createEvent
4 |
5 | function createEvent(type, attrs) {
6 | attrs = attrs || {}
7 | attrs.bubbles = true
8 |
9 | return DOMEvent(type, attrs)
10 | }
11 |
--------------------------------------------------------------------------------
/test/lib/h.js:
--------------------------------------------------------------------------------
1 | var document = require("global/document")
2 |
3 | module.exports = h
4 |
5 | function h(tagName, children) {
6 | var elem = document.createElement(tagName)
7 | if (children) {
8 | children.forEach(function (child) {
9 | elem.appendChild(child)
10 | })
11 | }
12 | return elem
13 | }
14 |
--------------------------------------------------------------------------------
/test/lib/sink-handler.js:
--------------------------------------------------------------------------------
1 | module.exports = SinkHandler
2 |
3 | function SinkHandler(sink, data) {
4 | if (!(this instanceof SinkHandler)) {
5 | return new SinkHandler(sink, data)
6 | }
7 |
8 | this.sink = sink
9 | this.data = data
10 | }
11 |
12 | SinkHandler.prototype.handleEvent = function handleEvent(ev) {
13 | this.sink.write(this.data)
14 | }
15 |
--------------------------------------------------------------------------------