on master help sign in
First draft addition of hot-swapping. Clean up Init.js a bit as well.
6ddb826evancz
libraries/Native/Signal/Signal.js+17−8
@@ -5,7 +5,7 @@ Elm.Native.Signal = function(elm) {
5 elm.Native = elm.Native || {};
5 elm.Native = elm.Native || {};
6 if (elm.Native.Signal) return elm.Native.Signal;
6 if (elm.Native.Signal) return elm.Native.Signal;
7
7
8− var Utils = Elm.Native.Utils(elm);
8+ var Utils = Elm.Native.Utils(elm);
9 var foldl1 = Elm.List(elm).foldl1;
9 var foldl1 = Elm.List(elm).foldl1;
10
10
11 function send(node, timestep, changed) {
11 function send(node, timestep, changed) {
@@ -84,13 +84,22 @@ Elm.Native.Signal = function(elm) {
84 return new LiftN(update, [a,b,c,d,e,f,g,h]);
84 return new LiftN(update, [a,b,c,d,e,f,g,h]);
85 }
85 }
86
86
87− function foldp(func,state,input) {
87+ function Foldp(step, state, input) {
88− var first = true;
88+ this.id = Utils.guid();
89− function update() {
89+ this.value = state;
90− first ? first = false : state = A2(func, input.value, state);
90+ this.kids = [];
91− return state;
91+
92− }
92+ this.recv = function(timestep, changed, parentID) {
93+ if (changed) {
94+ this.value = A2( step, input.value, this.value );
95+ }
96+ send(this, timestep, changed);
97+ };
98+ input.kids.push(this);
99+ }
100+
101+ function foldp(step, state, input) {
93− return new LiftN(update, [input]);
102+ return new Foldp(step, state, input);
94 }
103 }
95
104
96 function DropIf(pred,base,input) {
105 function DropIf(pred,base,input) {
runtime/HotSwap.jsnew file+37
@@ -0,0 +1,37 @@
1+(function() {
2+
3+// Returns boolean indicating if the swap was successful.
4+// Requires that the two signal graphs have exactly the same
5+// structure.
6+ElmRuntime.swap = function(from, to) {
7+ function similar(nodeOld,nodeNew) {
8+ return nodeOld.kids.length === nodeNew.kids.length;
9+ }
10+ function swap(nodeOld,nodeNew) {
11+ nodeNew.value = nodeOld.value;
12+ return true;
13+ }
14+ from_ = from.__private__;
15+ to_ = to.__private__;
16+ var canSwap = depthFirstTraversals(similar, from_.inputs, to_.inputs);
17+ if (canSwap) {
18+ depthFirstTraversals(swap, from_.inputs, to_.inputs);
19+ to.send("(!@#$%^&*)", null);
20+ }
21+ from_.container.parentNode.replaceChild(to_.container, from_.container);
22+ return canSwap;
23+}
24+
25+// Returns false if the node operation f ever fails.
26+function depthFirstTraversals(f, queueOld, queueNew) {
27+ while (queueOld.length > 0 && queueNew.length > 0) {
28+ var nodeOld = queueOld.pop();
29+ var nodeNew = queueNew.pop();
30+ if (!f(nodeOld, nodeNew)) return false;
31+ queueOld = queueOld.concat(nodeOld.kids);
32+ queueNew = queueNew.concat(nodeNew.kids);
33+ }
34+ return queueOld.length === queueNew.length;
35+}
36+
37+}())
runtime/Init.js+25−27
@@ -31,9 +31,7 @@ Elm.worker = function(module) {
31
31
32function init(display, container, module) {
32function init(display, container, module) {
33 // defining state needed for an instance of the Elm RTS
33 // defining state needed for an instance of the Elm RTS
34− var signalGraph = null;
35 var inputs = [];
34 var inputs = [];
36− var visualModel = null;
37
35
38 function notify(id, v) {
36 function notify(id, v) {
39 var timestep = Date.now();
37 var timestep = Date.now();
@@ -76,42 +74,42 @@ function init(display, container, module) {
76 if (e.value.length > 0) { window.location = e.value; }
74 if (e.value.length > 0) { window.location = e.value; }
77 });
75 });
78
76
77+ var Module = module(elm);
78+ inputs = ElmRuntime.filterDeadInputs(inputs);
79+
79 // If graphics are not enabled, escape early, skip over setting up DOM stuff.
80 // If graphics are not enabled, escape early, skip over setting up DOM stuff.
80− if (display === ElmRuntime.Display.NONE) {
81+ if (display !== ElmRuntime.Display.NONE) { initGraphics(elm, Module); }
81− module(elm);
82− return { send : send, recv : recv };
83− }
84
82
85− var Render = ElmRuntime.use(ElmRuntime.Render.Element);
83+ return { send:send, recv:recv,
84+ __private__ : { inputs:inputs, container:container } };
85+};
86
86
87− // evaluate the given module and extract its 'main' value.
87+function initGraphics(elm, Module) {
88+ if (!('main' in Module)) throw new Error("'main' is missing! What do I display?!");
88− signalGraph = module(elm).main;
89+ var signalGraph = Module.main;
89
90
90− // make sure the signal graph is actually a signal, extract the visual model,
91+ // make sure the signal graph is actually a signal & extract the visual model
91− // and filter out any unused inputs.
92+ if (!('recv' in signalGraph)) {
92− var Signal = Elm.Signal(elm);
93+ var Signal = Elm.Signal(elm);
93− if (!('recv' in signalGraph)) signalGraph = Signal.constant(signalGraph);
94+ signalGraph = Signal.constant(signalGraph);
95+ }
94− visualModel = signalGraph.value;
96+ var currentScene = signalGraph.value;
95− inputs = ElmRuntime.filterDeadInputs(inputs);
96
97
97− // Add the visualModel to the DOM
98+ // Add the currentScene to the DOM
99+ var Render = ElmRuntime.use(ElmRuntime.Render.Element);
98− container.appendChild(Render.render(visualModel));
100+ container.appendChild(Render.render(currentScene));
99 if (elm.Native.Window) elm.Native.Window.resizeIfNeeded();
101 if (elm.Native.Window) elm.Native.Window.resizeIfNeeded();
100
102
101 // set up updates so that the DOM is adjusted as necessary.
103 // set up updates so that the DOM is adjusted as necessary.
102− var update = Render.update;
103− function domUpdate(value) {
104+ function domUpdate(newScene) {
104 ElmRuntime.draw(function(_) {
105 ElmRuntime.draw(function(_) {
105− update(container.firstChild, visualModel, value);
106+ Render.update(container.firstChild, currentScene, newScene);
106− visualModel = value;
107+ currentScene = newScene;
107 if (elm.Native.Window) elm.Native.Window.resizeIfNeeded();
108 if (elm.Native.Window) elm.Native.Window.resizeIfNeeded();
108 });
109 });
109− return value;
110+ return newScene;
110 }
111 }
111−
112− signalGraph = A2(Signal.lift, domUpdate, signalGraph);
112+ A2(Signal.lift, domUpdate, signalGraph);
113−
113+}
114− return { send : send, recv : recv, node : container };
115−};
116
114
117}());
115}());