6Elm.debuggerAttach = function(module, hotSwapState /* =undefined */) {
8 make: function(runtime) {
9 var wrappedModule = debugModule(module, runtime);
10 Elm.Debugger = debuggerInit(wrappedModule, runtime, hotSwapState);
11 dispatchElmDebuggerInit();
12 return wrappedModule.moduleInstance;
17function dispatchElmDebuggerInit() {
19 var dispatch = function(_) {
20 parent.window.postMessage("elmDebuggerInit", window.location.origin);
22 if (parent.window.document.readyState === "complete") {
26 parent.window.addEventListener("load", dispatch);
31function debugModule(module, runtime) {
32 var programPaused = false;
33 var recordedEvents = [];
34 var asyncCallbacks = [];
36 function wrapNotify(id, v) {
37 var timestep = Date.now();
40 // ignore async events generated while playing back
41 // or user events while program is paused
44 runtime.notify(id, v, timestep);
45 recordEvent(id, v, timestep);
47 parent.window.postMessage("elmNotify", window.location.origin);
52 function wrapRunDelayed(func, delayMs) {
53 var cbObj = { func:func, delayMs:delayMs, timerId:0, executed:false };
54 var timerId = setTimeout(function() {
55 cbObj.executed = true;
58 cbObj.timerId = timerId;
59 asyncCallbacks.push(cbObj);
63 function recordEvent(id, v, timestep) {
64 recordedEvents.push({ id:id, value:v, timestep:timestep });
67 function clearAsyncCallbacks() {
68 asyncCallbacks.forEach(function(timer) {
69 if (!timer.executed) {
70 clearTimeout(timer.timerId);
75 function clearRecordedEvents() {
79 function getRecordedEventsLength() {
80 return recordedEvents.length;
83 function getRecordedEventAt(i) {
84 return recordedEvents[i];
87 function copyRecordedEvents() {
88 return recordedEvents.slice();
91 function loadRecordedEvents(events) {
92 recordedEvents = events.slice();
95 function setPaused(v) {
98 clearAsyncCallbacks();
99 tracePath.stopRecording();
102 // executeCallbacks should only be called when continuing, not when restarting.
103 //executeCallbacks(asyncCallbacks, false);
104 tracePath.startRecording();
108 function getPaused() {
109 return programPaused;
112 var wrappedRuntime = {
114 runDelayed: wrapRunDelayed,
116 display: runtime.display,
118 addListener: runtime.addListener,
119 inputs: runtime.inputs
122 var moduleInstance = module.make(wrappedRuntime);
123 var moduleNodes = flattenNodes(wrappedRuntime.inputs);
124 var tracePath = tracePathInit(runtime);
127 moduleInstance: moduleInstance,
128 moduleNodes: moduleNodes,
129 initialNodeValues: saveNodeValues(moduleNodes),
130 initialAsyncCallbacks: asyncCallbacks.slice(),
132 clearAsyncCallbacks: clearAsyncCallbacks,
133 clearRecordedEvents: clearRecordedEvents,
134 getRecordedEventsLength: getRecordedEventsLength,
135 getRecordedEventAt: getRecordedEventAt,
136 copyRecordedEvents: copyRecordedEvents,
137 loadRecordedEvents: loadRecordedEvents,
138 getPaused: getPaused,
139 setPaused: setPaused,
144function debuggerInit(debugModule, runtime, hotSwapState /* =undefined */) {
145 var Signal = Elm.Signal.make(runtime);
147 var tracePathNode = A2(Signal.lift, debugModule.tracePath.graphicsUpdate, debugModule.moduleInstance.main);
148 runtime.node.parentNode.appendChild(debugModule.tracePath.canvas);
150 var eventCounter = 0;
152 function resetProgram() {
153 debugModule.clearAsyncCallbacks();
154 restoreNodeValues(debugModule.moduleNodes, debugModule.initialNodeValues);
158 function restartProgram() {
160 debugModule.tracePath.clearTraces();
161 debugModule.clearRecordedEvents();
162 debugModule.setPaused(false);
163 executeCallbacks(debugModule.initialAsyncCallbacks, true);
166 function pauseProgram() {
167 debugModule.setPaused(true);
168 eventCounter = debugModule.getRecordedEventsLength();
171 function continueProgram() {
172 if (debugModule.getPaused())
174 debugModule.setPaused(false);
178 function stepTo(index) {
179 if (!debugModule.getPaused()) {
180 debugModule.setPaused(true);
184 if (index < 0 || index > getMaxSteps()) {
185 throw "Index out of bounds: " + index;
188 if (index < eventCounter) {
193 assert(index >= eventCounter, "index must be bad");
194 while (eventCounter < index) {
195 var nextEvent = debugModule.getRecordedEventAt(eventCounter);
196 runtime.notify(nextEvent.id, nextEvent.value, nextEvent.timestep);
200 assert(eventCounter == index, "while loop didn't work");
203 function getMaxSteps() {
204 return debugModule.getRecordedEventsLength();
207 function doPlayback(eventList) {
208 var x = eventList.shift();
210 runtime.notify(x[0], x[1], x[2]);
212 if (eventList.length > 0) {
213 var delta = eventList[0][2] - time;
214 setTimeout(function() { doPlayback(eventList); }, delta);
218 function redrawGraphics() {
219 var graphicsNode = debugModule.moduleInstance.main.kids[0];
220 graphicsNode.recv(Date.now(), true, debugModule.moduleInstance.main.id);
223 function getHotSwapState() {
224 if (debugModule.getPaused()) {
226 recordedEvents: debugModule.copyRecordedEvents(),
227 eventCounter: eventCounter
236 var parentNode = runtime.node.parentNode;
237 parentNode.removeChild(debugModule.tracePath.canvas);
238 parentNode.removeChild(runtime.node);
242 debugModule.setPaused(true);
243 debugModule.loadRecordedEvents(hotSwapState.recordedEvents);
245 // draw new trace path
246 debugModule.tracePath.startRecording();
247 stepTo(getMaxSteps());
248 debugModule.tracePath.stopRecording();
250 stepTo(hotSwapState.eventCounter);
254 restart: restartProgram,
256 kontinue: continueProgram,
257 getMaxSteps: getMaxSteps,
259 getPaused: debugModule.getPaused,
260 getHotSwapState: getHotSwapState,
267function Point(x, y) {
271 this.translate = function(x, y) {
272 return new Point(this.x + x, this.y + y);
275 this.equals = function(p) {
276 return this.x == p.x && this.y == p.y;
280function tracePathInit(runtime) {
281 var List = Elm.List.make(runtime);
282 var tracePathCanvas = createCanvas();
283 var tracePositions = {};
284 var recordingTraces = true;
286 function findPositions(currentScene) {
288 function processElement(elem, offset) {
289 if (elem.element.ctor == "Custom" && elem.element.type == "Collage")
291 List.map(F2(processForm)(offset))(elem.element.model.forms);
293 else if (elem.element.ctor == "Image" && elem.props.debugTracePathId)
295 positions[elem.props.debugTracePathId] = new Point(offset.x, offset.y);
299 function processForm(offset, form) {
300 if (form.form.ctor == "FElement")
302 processElement(form.form._0, offset.translate(form.x, -form.y));
306 processElement(currentScene, new Point(0, 0));
310 function appendPositions(positions) {
311 for (var id in positions) {
312 var pos = positions[id];
313 if (tracePositions.hasOwnProperty(id)) {
314 var points = tracePositions[id];
315 if (!pos.equals(points[points.length-1])) {
320 tracePositions[id] = [pos];
325 function graphicsUpdate(currentScene) {
326 if (!recordingTraces) {
330 var ctx = tracePathCanvas.getContext('2d');
331 ctx.clearRect(0, 0, tracePathCanvas.width, tracePathCanvas.height);
334 ctx.translate(ctx.canvas.width/2, ctx.canvas.height/2);
335 appendPositions(findPositions(currentScene));
336 for (var id in tracePositions)
339 var points = tracePositions[id];
340 for (var i=0; i < points.length; i++)
344 ctx.moveTo(p.x, p.y);
347 ctx.lineTo(p.x, p.y);
352 ctx.strokeStyle = "black";
358 function clearTraces() {
362 function stopRecording() {
363 recordingTraces = false;
366 function startRecording() {
367 recordingTraces = true;
371 graphicsUpdate: graphicsUpdate,
372 canvas: tracePathCanvas,
373 clearTraces: clearTraces,
374 stopRecording: stopRecording,
375 startRecording: startRecording
380function executeCallbacks(callbacks, reexecute) {
381 callbacks.forEach(function(timer) {
382 if (reexecute || !timer.executed) {
383 var func = timer.func;
389function createCanvas() {
390 var c = document.createElement('canvas');
391 c.width = window.innerWidth;
392 c.height = window.innerHeight;
393 c.style.position = "absolute";
399function assert(bool, msg) {
401 throw "Assertion error: " + msg;
405function saveNodeValues(allNodes) {
408 allNodes.forEach(function(node) {
409 nodeValues.push({ value: node.value, id: node.id });
416function restoreNodeValues(allNodes, nodeStates) {
417 assert(allNodes.length == nodeStates.length, "saved program state has wrong length");
418 for (var i=0; i < allNodes.length; i++) {
419 var node = allNodes[i];
420 var state = nodeStates[i];
421 assert(node.id == state.id, "the nodes moved position");
423 node.value = state.value;
427function flattenNodes(nodes) {
428 var treeReduce = function(prev, cur, i, arr) {
429 var flatKids = cur.kids.reduce(treeReduce, []);
431 return prev.concat(flatKids);
434 return nodes.reduce(treeReduce, []);
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list