@@ -136,7 +136,7 @@ function init(display, container, module, ports, moduleToReplace) {
136136 var graphicsNode = initGraphics(elm, Module);
137137 }
138138 if (typeof moduleToReplace !== 'undefined') {
139− ElmRuntime.swap(moduleToReplace, elm);
139+ hotSwap(moduleToReplace, elm);
140140
141141 // rerender scene if graphics are enabled.
142142 if (typeof graphicsNode !== 'undefined') {
@@ -330,4 +330,49 @@ if (win.requestAnimationFrame && win.cancelAnimationFrame) {
330330 };
331331}
332332
333+
334+//// HOT SWAPPING ////
335+
336+// Returns boolean indicating if the swap was successful.
337+// Requires that the two signal graphs have exactly the same
338+// structure.
339+function hotSwap(from, to) {
340+ function similar(nodeOld,nodeNew) {
341+ var idOkay = nodeOld.id === nodeNew.id;
342+ var lengthOkay = nodeOld.kids.length === nodeNew.kids.length;
343+ return idOkay && lengthOkay;
344+ }
345+ function swap(nodeOld,nodeNew) {
346+ nodeNew.value = nodeOld.value;
347+ return true;
348+ }
349+ var canSwap = depthFirstTraversals(similar, from.inputs, to.inputs);
350+ if (canSwap) {
351+ depthFirstTraversals(swap, from.inputs, to.inputs);
352+ }
353+ from.node.parentNode.replaceChild(to.node, from.node);
354+
355+ return canSwap;
356+}
357+
358+// Returns false if the node operation f ever fails.
359+function depthFirstTraversals(f, queueOld, queueNew) {
360+ if (queueOld.length !== queueNew.length) return false;
361+ queueOld = queueOld.slice(0);
362+ queueNew = queueNew.slice(0);
363+
364+ var seen = [];
365+ while (queueOld.length > 0 && queueNew.length > 0) {
366+ var nodeOld = queueOld.pop();
367+ var nodeNew = queueNew.pop();
368+ if (seen.indexOf(nodeOld.id) < 0) {
369+ if (!f(nodeOld, nodeNew)) return false;
370+ queueOld = queueOld.concat(nodeOld.kids);
371+ queueNew = queueNew.concat(nodeNew.kids);
372+ seen.push(nodeOld.id);
373+ }
374+ }
375+ return true;
376+}
377+
333378}());