@@ -136,7 +136,7 @@ function init(display, container, module, ports, moduleToReplace) {
136 var graphicsNode = initGraphics(elm, Module);
136 var graphicsNode = initGraphics(elm, Module);
138 if (typeof moduleToReplace !== 'undefined') {
138 if (typeof moduleToReplace !== 'undefined') {
139− ElmRuntime.swap(moduleToReplace, elm);
139+ hotSwap(moduleToReplace, elm);
141 // rerender scene if graphics are enabled.
141 // rerender scene if graphics are enabled.
142 if (typeof graphicsNode !== 'undefined') {
142 if (typeof graphicsNode !== 'undefined') {
@@ -330,4 +330,49 @@ if (win.requestAnimationFrame && win.cancelAnimationFrame) {
334+//// HOT SWAPPING ////
336+// Returns boolean indicating if the swap was successful.
337+// Requires that the two signal graphs have exactly the same
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;
345+ function swap(nodeOld,nodeNew) {
346+ nodeNew.value = nodeOld.value;
349+ var canSwap = depthFirstTraversals(similar, from.inputs, to.inputs);
351+ depthFirstTraversals(swap, from.inputs, to.inputs);
353+ from.node.parentNode.replaceChild(to.node, from.node);
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);
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);