on master help sign in
Switch to a custom way of attaching event listeners that makes it easier to detach them if necessary.
commit90cf4218a8b6569512f04435acb2593e073899aa authorevancz <info@elm-lang.org> dateSun 2 Jun 2013, 23:46 (13 years ago) parentbb719ab
7 files changed+86−85
libraries/Native/Signal/Keyboard.js+14−25 libraries/Native/Signal/Mouse.js+14−27 libraries/Native/Signal/Touch.js+5−6 libraries/Native/Signal/Window.js+2−3 libraries/Native/Utils.js+1−1 runtime/HotSwap.js+1−3 runtime/Init.js+49−20
libraries/Native/Signal/Keyboard.js+14−25
@@ -10,31 +10,20 @@ Elm.Native.Keyboard = function(elm) {
1010 var keysDown = Signal.constant(NList.Nil);
1111 var lastKey = Signal.constant('\0');
1212
13− function down(e) {
14− if (NList.member(e.keyCode)(keysDown.value)) return;
15− var list = NList.Cons(e.keyCode, keysDown.value);
16− var hasListener = elm.notify(keysDown.id, list);
17− if (!hasListener) document.removeEventListener('keydown', down);
18− }
19− function up(e) {
20− function notEq(kc) { return kc !== e.keyCode; }
21− var codes = NList.filter(notEq)(keysDown.value);
22− var hasListener = elm.notify(keysDown.id, codes);
23− if (!hasListener) document.removeEventListener('keyup', up);
24− }
25− function blur(e) {
26− var hasListener = elm.notify(keysDown.id, NList.Nil);
27− if (!hasListener) document.removeEventListener('blur', blur);
28− }
29− function press(e) {
30− var hasListener = elm.notify(lastKey.id, e.charCode || e.keyCode);
31− if (!hasListener) document.removeEventListener('keypress', press);
32− }
33−
34− document.addEventListener('keydown' , down );
35− document.addEventListener('keyup' , up );
36− document.addEventListener('blur' , blur );
37− document.addEventListener('keypress', press);
13+ elm.addListener([keysDown.id], document, 'keydown', function down(e) {
14+ if (NList.member(e.keyCode)(keysDown.value)) return;
15+ elm.notify(keysDown.id, NList.Cons(e.keyCode, keysDown.value));
16+ });
17+ elm.addListener([keysDown.id], document, 'keyup', function up(e) {
18+ function notEq(kc) { return kc !== e.keyCode; }
19+ elm.notify(keysDown.id, NList.filter(notEq)(keysDown.value));
20+ });
21+ elm.addListener([keysDown.id], document, 'blur', function blur(e) {
22+ elm.notify(keysDown.id, NList.Nil);
23+ });
24+ elm.addListener([lastKey.id], document, 'keypress', function press(e) {
25+ elm.notify(lastKey.id, e.charCode || e.keyCode);
26+ });
3827
3928 function keySignal(f) {
4029 var signal = A2( Signal.lift, f, keysDown );
libraries/Native/Signal/Mouse.js+14−27
@@ -40,33 +40,20 @@ Elm.Native.Mouse = function(elm) {
4040
4141 var node = elm.display === ElmRuntime.Display.FULLSCREEN ? document : elm.node;
4242
43− function click(e) {
44− var hasListener1 = elm.notify(isClicked.id, true);
45− var hasListener2 = elm.notify(clicks.id, Utils.Tuple0);
46− elm.notify(isClicked.id, false);
47− if (!hasListener1 && !hasListener2)
48− node.removeEventListener('click', click);
49− }
50−
51− function down(e) {
52− var hasListener = elm.notify(isDown.id, true);
53− if (!hasListener) node.removeEventListener('mousedown', down);
54− }
55−
56− function up(e) {
57− var hasListener = elm.notify(isDown.id, false);
58− if (!hasListener) node.removeEventListener('mouseup', up);
59− }
60−
61− function move(e) {
62− var hasListener = elm.notify(position.id, getXY(e));
63− if (!hasListener) node.removeEventListener('mousemove', move);
64− }
65−
66− node.addEventListener('click' , click);
67− node.addEventListener('mousedown', down);
68− node.addEventListener('mouseup' , up);
69− node.addEventListener('mousemove', move);
43+ elm.addListener([isClicked.id, clicks.id], node, 'click', function click() {
44+ elm.notify(isClicked.id, true);
45+ elm.notify(clicks.id, Utils.Tuple0);
46+ elm.notify(isClicked.id, false);
47+ });
48+ elm.addListener([isDown.id], node, 'mousedown', function down() {
49+ elm.notify(isDown.id, true);
50+ });
51+ elm.addListener([isDown.id], node, 'mouseup', function up() {
52+ elm.notify(isDown.id, false);
53+ });
54+ elm.addListener([position.id], node, 'mousemove', function move(e) {
55+ elm.notify(position.id, getXY(e));
56+ });
7057
7158 return elm.Native.Mouse = {
7259 position: position,
libraries/Native/Signal/Touch.js+5−6
@@ -74,11 +74,10 @@ Elm.Native.Touch = function(elm) {
7474 for (var i = e.changedTouches.length; i--; ) { f(e.changedTouches[i]); }
7575 var ts = new Array(e.touches.length);
7676 for (var i = e.touches.length; i--; ) { ts[i] = touch(e.touches[i]); }
77− var hasListener = elm.notify(root.id, ts);
78− if (!hasListener) return node.removeEventListener(name, update);
77+ elm.notify(root.id, ts);
7978 e.preventDefault();
8079 }
81− node.addEventListener(name, update);
80+ elm.addListener([root.id], node, name, update);
8281 }
8382
8483 listen("touchstart", start);
@@ -98,14 +97,14 @@ Elm.Native.Touch = function(elm) {
9897 }
9998 }
10099 }
101− node.addEventListener("mousedown", function(e) {
100+ elm.addListener([root.id], node, "mousedown", function down(e) {
102101 node.addEventListener("mousemove", move);
103102 e.identifier = mouseID;
104103 start(e);
105104 root.value.push(touch(e));
106105 elm.notify(root.id, root.value);
107106 });
108− node.addEventListener("mouseup", function(e) {
107+ elm.addListener([root.id], node, "mouseup", function up(e) {
109108 node.removeEventListener("mousemove", move);
110109 e.identifier = mouseID;
111110 end(e);
@@ -118,7 +117,7 @@ Elm.Native.Touch = function(elm) {
118117 }
119118 elm.notify(root.id, root.value);
120119 });
121− node.addEventListener("blur", function() {
120+ elm.addListener([root.id], node, "blur", function blur(e) {
122121 node.removeEventListener("mousemove", move);
123122 if (root.values.length > 0) {
124123 elm.notify(root.id, []);
libraries/Native/Signal/Window.js+2−3
@@ -31,10 +31,9 @@ Elm.Native.Window = function(elm) {
3131 var w = getWidth();
3232 var h = getHeight();
3333 if (dimensions.value._0 === w && dimensions.value._1 === h) return;
34− var hasListener = elm.notify(dimensions.id, Tuple2(w,h));
35− if (!hasListener) window.removeEventListener('resize', resizeIfNeeded);
34+ elm.notify(dimensions.id, Tuple2(w,h));
3635 }
37− window.addEventListener('resize', resizeIfNeeded);
36+ elm.addListener([dimensions.id], window, 'resize', resizeIfNeeded);
3837
3938 return elm.Native.Window = {
4039 dimensions:dimensions,
libraries/Native/Utils.js+1−1
@@ -135,7 +135,7 @@ Elm.Native.Utils = function(elm) {
135135 }
136136
137137 if (elm.display === ElmRuntime.Display.COMPONENT) {
138− elm.node.addEventListener('mouseover', adjustOffset);
138+ elm.addListener(elm.inputs, elm.node, 'mouseover', adjustOffset);
139139 }
140140
141141 return elm.Native.Utils = {
runtime/HotSwap.js+1−3
@@ -15,9 +15,7 @@ ElmRuntime.swap = function(from, to) {
1515 }
1616 var canSwap = depthFirstTraversals(similar, from.inputs, to.inputs);
1717 if (canSwap) { depthFirstTraversals(swap, from.inputs, to.inputs); }
18− ElmRuntime.draw(function() {
19− from.node.parentNode.replaceChild(to.node, from.node)
20− });
18+ from.node.parentNode.replaceChild(to.node, from.node);
2119 return canSwap;
2220}
2321
runtime/Init.js+49−20
@@ -13,14 +13,12 @@ Elm.fullscreen = function(module) {
1313 return init(ElmRuntime.Display.FULLSCREEN, container, module);
1414};
1515
16−Elm.byId = function(id, module) {
17− var container = document.getElementById(id);
16+Elm.domNode = function(container, module) {
1817 var tag = container.tagName;
1918 if (tag !== 'DIV') {
20− throw new Error('Elm.byId must be given a div, not a ' + tag + '.');
21− }
22− while (container.hasChildNodes()) {
23− container.removeChild(container.lastChild);
19+ throw new Error('Elm.node must be given a DIV, not a ' + tag + '.');
20+ } else if (container.hasChildNodes()) {
21+ throw new Error('Elm.node must be given an empty DIV. No children allowed!');
2422 }
2523 return init(ElmRuntime.Display.COMPONENT, container, module);
2624};
@@ -34,26 +32,36 @@ function init(display, container, module, moduleToReplace) {
3432 var inputs = [];
3533
3634 function notify(id, v) {
37− var timestep = Date.now();
38− var hasListener = false;
39− for (var i = inputs.length; i--; ) {
40− // must maintain the order of this stmt to avoid having the ||
41− // short-circuiting the necessary work of recv
42− hasListener = inputs[i].recv(timestep, id, v) || hasListener;
43− }
44− return hasListener;
35+ var timestep = Date.now();
36+ for (var i = inputs.length; i--; ) {
37+ inputs[i].recv(timestep, id, v);
38+ }
4539 }
4640
4741 container.offsetX = 0;
4842 container.offsetY = 0;
4943
44+ var listeners = [];
45+ function addListener(relevantInputs, domNode, eventName, func) {
46+ domNode.addEventListener(eventName, func);
47+ var listener = {
48+ relevantInputs: relevantInputs,
49+ domNode: domNode,
50+ eventName: eventName,
51+ func: func
52+ };
53+ listeners.push(listener);
54+ }
55+
5056 // create the actual RTS. Any impure modules will attach themselves to this
5157 // object. This permits many Elm programs to be embedded per document.
52− var elm = { notify:notify,
53− node:container,
54− display:display,
55− id:ElmRuntime.guid(),
56− inputs:inputs
58+ var elm = {
59+ notify:notify,
60+ node:container,
61+ display:display,
62+ id:ElmRuntime.guid(),
63+ addListener:addListener,
64+ inputs:inputs
5765 };
5866
5967 // Set up methods to communicate with Elm program from JS.
@@ -75,8 +83,8 @@ function init(display, container, module, moduleToReplace) {
7583 });
7684
7785 function swap(newModule) {
86+ removeListeners(listeners);
7887 var div = document.createElement('div');
79− if (container.id) { div.id = container.id; }
8088 var newElm = init(display, div, newModule, elm);
8189 // elm.send = newElm.send;
8290 // elm.recv = newElm.recv;
@@ -86,6 +94,7 @@ function init(display, container, module, moduleToReplace) {
8694
8795 var Module = module(elm);
8896 inputs = ElmRuntime.filterDeadInputs(inputs);
97+ filterListeners(inputs, listeners);
8998 if (display !== ElmRuntime.Display.NONE) {
9099 var graphicsNode = initGraphics(elm, Module);
91100 }
@@ -102,6 +111,26 @@ function init(display, container, module, moduleToReplace) {
102111 return { send:send, recv:recv, swap:swap };
103112};
104113
114+function filterListeners(inputs, listeners) {
115+ loop:
116+ for (var i = listeners.length; i--; ) {
117+ var listener = listeners[i];
118+ for (var j = inputs.length; j--; ) {
119+ if (listener.relevantInputs.indexOf(inputs[j].id) >= 0) {
120+ continue loop;
121+ }
122+ }
123+ listener.domNode.removeEventListener(listener.eventName, listener.func);
124+ }
125+}
126+
127+function removeListeners(listeners) {
128+ for (var i = listeners.length; i--; ) {
129+ var listener = listeners[i];
130+ listener.domNode.removeEventListener(listener.eventName, listener.func);
131+ }
132+}
133+
105134function initGraphics(elm, Module) {
106135 if (!('main' in Module))
107136 throw new Error("'main' is missing! What do I display?!");