88ab43652d8ae84c12bffe3d35a14b2c685ffa56
[WebKit-https.git] / WebKitTools / Drosera / debugger.js
1 /*
2  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 var files = new Array();
30 var filesLookup = new Object();
31 var scripts = new Array();
32 var currentFile = -1;
33 var currentRow = null;
34 var currentStack = null;
35 var currentCallFrame = null;
36 var previousFiles = new Array();
37 var nextFiles = new Array();
38 var isResizingColumn = false;
39 var draggingBreakpoint = null;
40 var steppingOut = false;
41 var steppingOver = false;
42 var steppingStack = 0;
43 var pauseOnNextStatement = false;
44 var consoleWindow = null;
45
46 ScriptCallFrame = function (functionName, index, row)
47 {
48     this.functionName = functionName;
49     this.index = index;
50     this.row = row;
51     this.localVariableNames = null;
52 }
53
54 ScriptCallFrame.prototype.valueForScopeVariable = function (name)
55 {
56     return DebuggerDocument.valueForScopeVariableNamed_inCallFrame_(name, this.index);
57 }
58
59 ScriptCallFrame.prototype.loadVariables = function ()
60 {
61     if (!this.localVariableNames)
62         this.localVariableNames = DebuggerDocument.localScopeVariableNamesForCallFrame_(this.index);
63
64     var variablesTable = document.getElementById("variablesTable");
65     variablesTable.innerHTML = "";
66
67     for(var i = 0; i < this.localVariableNames.length; i++) {
68         var tr = document.createElement("tr");
69         var td = document.createElement("td");
70         td.innerText = this.localVariableNames[i];
71         td.className = "variable";
72         tr.appendChild(td);
73
74         td = document.createElement("td");
75         td.innerText = this.valueForScopeVariable(this.localVariableNames[i]);
76         tr.appendChild(td);
77         tr.addEventListener("click", selectVariable, true);
78
79         variablesTable.appendChild(tr);
80     }
81 }
82
83 function sleep(numberMillis) {
84     var now = new Date();
85     var exitTime = now.getTime() + numberMillis;
86     while (true) {
87         now = new Date();
88         if (now.getTime() > exitTime)
89             return;
90     }
91 }
92
93 function headerMouseDown(element) {
94     if (!isResizingColumn) 
95         element.style.background = "url(glossyHeaderPressed.png) repeat-x";
96 }
97
98 function headerMouseUp(element) {
99     element.style.background = "url(glossyHeader.png) repeat-x";
100 }
101
102 function headerMouseOut(element) {
103     element.style.background = "url(glossyHeader.png) repeat-x";
104 }
105
106 function dividerDragStart(element, dividerDrag, dividerDragEnd, event, cursor) {
107     element.dragging = true;
108     element.dragLastY = event.clientY + window.scrollY;
109     element.dragLastX = event.clientX + window.scrollX;
110     document.addEventListener("mousemove", dividerDrag, true);
111     document.addEventListener("mouseup", dividerDragEnd, true);
112     document.body.style.cursor = cursor;
113     event.preventDefault();
114 }
115
116 function sourceDividerDragStart(event) {
117     dividerDragStart(document.getElementById("divider"), dividerDrag, sourceDividerDragEnd, event, "row-resize");
118 }
119
120 function infoDividerDragStart(event) {
121     dividerDragStart(document.getElementById("infoDivider"), infoDividerDrag, infoDividerDragEnd, event, "col-resize");
122 }
123
124 function columnResizerDragStart(event) {
125     isResizingColumn = true;
126     dividerDragStart(document.getElementById("variableColumnResizer"), columnResizerDrag, columnResizerDragEnd, event, "col-resize");
127 }
128
129 function columnResizerDragEnd(event) {
130     isResizingColumn = false;
131     dividerDragEnd(document.getElementById("variableColumnResizer"), columnResizerDrag, columnResizerDragEnd, event);
132 }
133
134 function infoDividerDragEnd(event) {
135     dividerDragEnd(document.getElementById("infoDivider"), infoDividerDrag, infoDividerDragEnd, event);
136 }
137
138 function sourceDividerDragEnd(event) {
139     dividerDragEnd(document.getElementById("divider"), dividerDrag, sourceDividerDragEnd, event);
140 }
141
142 function dividerDragEnd(element, dividerDrag, dividerDragEnd, event) {
143     element.dragging = false;
144     document.removeEventListener("mousemove", dividerDrag, true);
145     document.removeEventListener("mouseup", dividerDragEnd, true);
146     document.body.style.cursor = null;
147 }
148
149 function columnResizerDrag(event) {
150     var element = document.getElementById("variableColumnResizer");
151     if (element.dragging == true) {
152         var main = document.getElementById("rightPane");
153         var variableColumn = document.getElementById("variable");
154         var rules = document.defaultView.getMatchedCSSRules(variableColumn, "");
155         for (var i = 0; i < rules.length; i++) {
156             if (rules[i].selectorText == ".variable") {
157                 var columnRule = rules[i];
158                 break;
159             }
160         }
161
162         var x = event.clientX + window.scrollX;
163         var delta = element.dragLastX - x;
164         var newWidth = constrainedWidthFromElement(variableColumn.clientWidth - delta, main);
165         if ((variableColumn.clientWidth - delta) == newWidth) // the width wasn't constrained
166             element.dragLastX = x;
167         columnRule.style.width = newWidth + "px";
168         element.style.left = newWidth + "px";
169         event.preventDefault();
170     }
171 }
172
173 function constrainedWidthFromElement(width, element) {
174     if (width < element.clientWidth * 0.25)
175         width = element.clientWidth * 0.25;
176     else if (width > element.clientWidth * 0.75)
177         width = element.clientWidth * 0.75;
178     return width;
179 }
180
181 function constrainedHeightFromElement(height, element) {
182     if (height < element.clientHeight * 0.25)
183         height = element.clientHeight * 0.25;
184     else if (height > element.clientHeight * 0.75)
185         height = element.clientHeight * 0.75;
186     return height;
187 }
188
189 function infoDividerDrag(event) {
190     var element = document.getElementById("infoDivider");
191     if (document.getElementById("infoDivider").dragging == true) {
192         var main = document.getElementById("main");
193         var leftPane = document.getElementById("leftPane");
194         var rightPane = document.getElementById("rightPane");
195         var x = event.clientX + window.scrollX;
196         var delta = element.dragLastX - x;
197         var newWidth = constrainedWidthFromElement(leftPane.clientWidth - delta, main);
198         if ((leftPane.clientWidth - delta) == newWidth) // the width wasn't constrained
199             element.dragLastX = x;
200         leftPane.style.width = newWidth + "px";
201         rightPane.style.left = newWidth + "px";
202         event.preventDefault();
203     }
204 }
205
206 function dividerDrag(event) {
207     var element = document.getElementById("divider");
208     if (document.getElementById("divider").dragging == true) {
209         var main = document.getElementById("main");
210         var top = document.getElementById("info");
211         var bottom = document.getElementById("body");
212         var y = event.clientY + window.scrollY;
213         var delta = element.dragLastY - y;
214         var newHeight = constrainedHeightFromElement(top.clientHeight - delta, main);
215         if ((top.clientHeight - delta) == newHeight) // the height wasn't constrained
216             element.dragLastY = y;
217         top.style.height = newHeight + "px";
218         bottom.style.top = newHeight + "px";
219         event.preventDefault();
220     }
221 }
222
223 function loaded() {
224     document.getElementById("divider").addEventListener("mousedown", sourceDividerDragStart, false);
225     document.getElementById("infoDivider").addEventListener("mousedown", infoDividerDragStart, false);
226     document.getElementById("variableColumnResizer").addEventListener("mousedown", columnResizerDragStart, false);
227 }
228
229 function isPaused() {
230     return DebuggerDocument.isPaused();
231 }
232
233 function pause() {
234     DebuggerDocument.pause();
235 }
236
237 function resume()
238 {
239     if (currentRow) {
240         removeStyleClass(currentRow, "current");
241         currentRow = null;
242     }
243
244     var stackframeTable = document.getElementById("stackframeTable");
245     stackframeTable.innerHTML = ""; // clear the content
246     var variablesTable = document.getElementById("variablesTable");
247     variablesTable.innerHTML = ""; // clear the content
248     currentStack = null;
249     currentCallFrame = null;
250
251     pauseOnNextStatement = false;
252     steppingOut = false;
253     steppingOver = false;
254     steppingStack = 0;
255
256     DebuggerDocument.resume();
257 }
258
259 function stepInto()
260 {
261     pauseOnNextStatement = false;
262     steppingOut = false;
263     steppingOver = false;
264     steppingStack = 0;
265     DebuggerDocument.stepInto();
266 }
267
268 function stepOver()
269 {
270     pauseOnNextStatement = false;
271     steppingOver = true;
272     steppingStack = 0;
273     DebuggerDocument.resume();
274 }
275
276 function stepOut()
277 {
278     pauseOnNextStatement = false;
279     steppingOver = false;
280     steppingStack = 0;
281     steppingOut = true;
282     DebuggerDocument.resume();
283 }
284
285 function hasStyleClass(element, className)
286 {
287     return ( element.className.indexOf(className) != -1 );
288 }
289
290 function addStyleClass(element, className)
291 {
292     if (!hasStyleClass(element, className))
293         element.className += (element.className.length ? " " + className : className);
294 }
295
296 function removeStyleClass(element, className)
297 {
298     if (hasStyleClass(element, className))
299         element.className = element.className.replace(className, "");
300 }
301
302 function addBreakPoint(event)
303 {
304     var row = event.target.parentNode;
305     if (hasStyleClass(row, "breakpoint")) {
306         if (hasStyleClass(row, "disabled")) {
307             removeStyleClass(row, "disabled");
308             files[currentFile].breakpoints[parseInt(event.target.title)] = 1;
309         } else {
310             addStyleClass(row, "disabled");
311             files[currentFile].breakpoints[parseInt(event.target.title)] = -1;
312         }
313     } else {
314         addStyleClass(row, "breakpoint");
315         removeStyleClass(row, "disabled");
316         files[currentFile].breakpoints[parseInt(event.target.title)] = 1;
317     }
318 }
319
320 function moveBreakPoint(event)
321 {
322     if (hasStyleClass(event.target.parentNode, "breakpoint")) {
323         files[currentFile].breakpoints[parseInt(event.target.title)] = 0;
324         draggingBreakpoint = event.target;
325         draggingBreakpoint.started = false;
326         draggingBreakpoint.dragLastY = event.clientY + window.scrollY;
327         draggingBreakpoint.dragLastX = event.clientX + window.scrollX;
328         var sourcesDocument = document.getElementById("sources").contentDocument;
329         sourcesDocument.addEventListener("mousemove", breakpointDrag, true);
330         sourcesDocument.addEventListener("mouseup", breakpointDragEnd, true);
331         sourcesDocument.body.style.cursor = "default";
332     }
333 }
334
335 function breakpointDrag(event)
336 {
337     var sourcesDocument = document.getElementById("sources").contentDocument;
338     if (!draggingBreakpoint) {
339         sourcesDocument.removeEventListener("mousemove", breakpointDrag, true);
340         sourcesDocument.removeEventListener("mouseup", breakpointDragEnd, true);
341         sourcesDocument.body.style.cursor = null;
342         return;
343     }
344
345     var x = event.clientX + window.scrollX;
346     var y = event.clientY + window.scrollY;
347     var deltaX = draggingBreakpoint.dragLastX - x;
348     var deltaY = draggingBreakpoint.dragLastY - y;
349     if (draggingBreakpoint.started || deltaX > 4 || deltaY > 4 || deltaX < -4 || deltaY < -4) {
350         if (!draggingBreakpoint.started) {
351             draggingBreakpoint.isDisabled = hasStyleClass(draggingBreakpoint.parentNode, "disabled");
352             removeStyleClass(draggingBreakpoint.parentNode, "breakpoint");
353             removeStyleClass(draggingBreakpoint.parentNode, "disabled");
354             draggingBreakpoint.started = true;
355
356             var dragImage = sourcesDocument.createElement("img");
357             if (draggingBreakpoint.isDisabled)
358                 dragImage.src = "breakPointDisabled.tif";
359             else
360                 dragImage.src = "breakPoint.tif";
361             dragImage.id = "breakpointDrag";
362             dragImage.style.top = y - 8 + "px";
363             dragImage.style.left = x - 12 + "px";
364             sourcesDocument.body.appendChild(dragImage);
365         } else {
366             var dragImage = sourcesDocument.getElementById("breakpointDrag");
367             if (!dragImage) {
368                 sourcesDocument.removeEventListener("mousemove", breakpointDrag, true);
369                 sourcesDocument.removeEventListener("mouseup", breakpointDragEnd, true);
370                 sourcesDocument.body.style.cursor = null;
371                 return;
372             }
373
374             dragImage.style.top = y - 8 + "px";
375             dragImage.style.left = x - 12 + "px";
376             if (x > 40)
377                 dragImage.style.visibility = "hidden";
378             else
379                 dragImage.style.visibility = null;
380         }
381
382         draggingBreakpoint.dragLastX = x;
383         draggingBreakpoint.dragLastY = y;
384     }
385 }
386
387 function breakpointDragEnd(event)
388 {
389     var sourcesDocument = document.getElementById("sources").contentDocument;
390     sourcesDocument.removeEventListener("mousemove", breakpointDrag, true);
391     sourcesDocument.removeEventListener("mouseup", breakpointDragEnd, true);
392     sourcesDocument.body.style.cursor = null;
393
394     var dragImage = sourcesDocument.getElementById("breakpointDrag");
395     if (!dragImage)
396         return;
397
398     dragImage.parentNode.removeChild(dragImage);
399
400     var x = event.clientX + window.scrollX;
401     if (x > 40 || !draggingBreakpoint)
402         return;
403
404     var y = event.clientY + window.scrollY;
405     var rowHeight = draggingBreakpoint.parentNode.offsetHeight;
406     var row = Math.ceil(y / rowHeight);
407     if (row <= 0)
408         row = 1;
409
410     var file = files[currentFile];
411     var table = file.element.firstChild;
412     if (row > table.childNodes.length)
413         return;
414
415     var tr = table.childNodes.item(row - 1);
416     if (!tr)
417         return;
418
419     if (draggingBreakpoint.isDisabled)
420         addStyleClass(tr, "disabled");
421     addStyleClass(tr, "breakpoint");
422     file.breakpoints[row] = (draggingBreakpoint.isDisabled ? -1 : 1);
423
424     draggingBreakpoint = null;
425 }
426
427 function totalOffsetTop(element, stop)
428 {
429     var currentTop = 0;
430     while (element.offsetParent) {
431         currentTop += element.offsetTop
432         element = element.offsetParent;
433         if (element == stop)
434             break;
435     }
436     return currentTop;
437 }
438
439 function switchFile()
440 {
441     var filesSelect = document.getElementById("files");
442     loadFile(filesSelect.options[filesSelect.selectedIndex].value, true);
443 }
444
445 function syntaxHighlight(code)
446 {
447     var keywords = { 'abstract': 1, 'boolean': 1, 'break': 1, 'byte': 1, 'case': 1, 'catch': 1, 'char': 1, 'class': 1, 'const': 1, 'continue': 1, 'debugger': 1, 'default': 1, 'delete': 1, 'do': 1, 'double': 1, 'else': 1, 'enum': 1, 'export': 1, 'extends': 1, 'false': 1, 'final': 1, 'finally': 1, 'float': 1, 'for': 1, 'function': 1, 'goto': 1, 'if': 1, 'implements': 1, 'import': 1, 'in': 1, 'instanceof': 1, 'int': 1, 'interface': 1, 'long': 1, 'native': 1, 'new': 1, 'null': 1, 'package': 1, 'private': 1, 'protected': 1, 'public': 1, 'return': 1, 'short': 1, 'static': 1, 'super': 1, 'switch': 1, 'synchronized': 1, 'this': 1, 'throw': 1, 'throws': 1, 'transient': 1, 'true': 1, 'try': 1, 'typeof': 1, 'var': 1, 'void': 1, 'volatile': 1, 'while': 1, 'with': 1 };
448
449     function echoChar(c) {
450         if (c == '<')
451             result += '&lt;';
452         else if (c == '>')
453             result += '&gt;';
454         else if (c == '&')
455             result += '&amp;';
456         else if (c == '\t')
457             result += '    ';
458         else
459             result += c;
460     }
461
462     function isDigit(number) {
463         var string = "1234567890";
464         if (string.indexOf(number) != -1)
465             return true;
466         return false;
467     }
468
469     function isHex(hex) {
470         var string = "1234567890abcdefABCDEF";
471         if (string.indexOf(hex) != -1)
472             return true;
473         return false;
474     }
475
476     function isLetter(letter) {
477         var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
478         if (string.indexOf(letter) != -1)
479             return true;
480         return false;
481     }
482
483     var result = "";
484     var cPrev = "";
485     var c = "";
486     var cNext = "";
487     for (var i = 0; i < code.length; i++) {
488         cPrev = c;
489         c = code.charAt(i);
490         cNext = code.charAt(i + 1);
491
492         if (c == "/" && cNext == "*") {
493             result += "<span class=\"comment\">";
494             echoChar(c);
495             echoChar(cNext);
496             for (i += 2; i < code.length; i++) {
497                 c = code.charAt(i);
498                 if (c == "\n")
499                     result += "</span>";
500                 echoChar(c);
501                 if (c == "\n")
502                     result += "<span class=\"comment\">";
503                 if (cPrev == "*" && c == "/")
504                     break;
505                 cPrev = c;
506             }
507             result += "</span>";
508             continue;
509         } else if (c == "/" && cNext == "/") {
510             result += "<span class=\"comment\">";
511             echoChar(c);
512             echoChar(cNext);
513             for (i += 2; i < code.length; i++) {
514                 c = code.charAt(i);
515                 if (c == "\n")
516                     break;
517                 echoChar(c);
518             }
519             result += "</span>";
520             echoChar(c);
521             continue;
522         } else if (c == "\"" || c == "'") {
523             var instringtype = c;
524             var stringstart = i;
525             result += "<span class=\"string\">";
526             echoChar(c);
527             for (i += 1; i < code.length; i++) {
528                 c = code.charAt(i);
529                 if (stringstart < (i - 1) && cPrev == instringtype && code.charAt(i - 2) != "\\")
530                     break;
531                 echoChar(c);
532                 cPrev = c;
533             }
534             result += "</span>";
535             echoChar(c);
536             continue;
537         } else if (c == "0" && cNext == "x" && (i == 0 || (!isLetter(cPrev) && !isDigit(cPrev)))) {
538             result += "<span class=\"number\">";
539             echoChar(c);
540             echoChar(cNext);
541             for (i += 2; i < code.length; i++) {
542                 c = code.charAt(i);
543                 if (!isHex(c))
544                     break;
545                 echoChar(c);
546             }
547             result += "</span>";
548             echoChar(c);
549             continue;
550         } else if ((isDigit(c) || ((c == "-" || c == ".") && isDigit(cNext))) && (i == 0 || (!isLetter(cPrev) && !isDigit(cPrev)))) {
551             result += "<span class=\"number\">";
552             echoChar(c);
553             for (i += 1; i < code.length; i++) {
554                 c = code.charAt(i);
555                 if (!isDigit(c) && c != ".")
556                     break;
557                 echoChar(c);
558             }
559             result += "</span>";
560             echoChar(c);
561             continue;
562         } else if(isLetter(c) && (i == 0 || !isLetter(cPrev))) {
563             var keyword = c;
564             var cj = "";
565             for (var j = i + 1; j < i + 12 && j < code.length; j++) {
566                 cj = code.charAt(j);
567                 if (!isLetter(cj))
568                     break;
569                 keyword += cj;
570             }
571
572             if (keywords[keyword]) {
573                 result += "<span class=\"keyword\">" + keyword + "</span>";
574                 i += keyword.length - 1;
575                 continue;
576             }
577         }
578
579         echoChar(c);
580     }
581
582     return result;
583 }
584
585 function navFilePrevious(element)
586 {
587     if (element.disabled)
588         return;
589     var lastFile = previousFiles.pop();
590     if (currentFile != -1)
591         nextFiles.unshift(currentFile);
592     loadFile(lastFile, false);
593 }
594
595 function navFileNext(element)
596 {
597     if (element.disabled)
598         return;
599     var lastFile = nextFiles.shift();
600     if (currentFile != -1)
601         previousFiles.push(currentFile);
602     loadFile(lastFile, false);
603 }
604
605 function updateFunctionStack()
606 {
607     var stackframeTable = document.getElementById("stackframeTable");
608     stackframeTable.innerHTML = ""; // clear the content
609
610     currentStack = new Array();
611     var stack = DebuggerDocument.currentFunctionStack();
612     for(var i = 0; i < stack.length; i++) {
613         var tr = document.createElement("tr");
614         var td = document.createElement("td");
615         td.className = "stackNumber";
616         td.innerText = i;
617         tr.appendChild(td);
618
619         td = document.createElement("td");
620         td.innerText = stack[i];
621         tr.appendChild(td);
622         tr.addEventListener("click", selectStackFrame, true);
623
624         stackframeTable.appendChild(tr);
625
626         var frame = new ScriptCallFrame(stack[i], i, tr);
627         tr.callFrame = frame;
628         currentStack.push(frame);
629
630         if (i == 0) {
631             addStyleClass(tr, "current");
632             frame.loadVariables();
633             currentCallFrame = frame;
634         }
635     }
636 }
637
638 function selectStackFrame(event)
639 {
640     var stackframeTable = document.getElementById("stackframeTable");
641     var rows = stackframeTable.childNodes;
642     for (var i = 0; i < rows.length; i++)
643         removeStyleClass(rows[i], "current");
644     addStyleClass(this, "current");
645     this.callFrame.loadVariables();
646     currentCallFrame = this.callFrame;
647 }
648
649 function selectVariable(event)
650 {
651     var variablesTable = document.getElementById("variablesTable");
652     var rows = variablesTable.childNodes;
653     for (var i = 0; i < rows.length; i++)
654         removeStyleClass(rows[i], "current");
655     addStyleClass(this, "current");
656 }
657
658 function loadFile(fileIndex, manageNavLists)
659 {
660     var file = files[fileIndex];
661     if (!file)
662         return;
663
664     if (currentFile != -1 && files[currentFile] && files[currentFile].element)
665         files[currentFile].element.style.display = "none";
666
667     if (!file.loaded) {
668         file.source = file.source.replace(/\r\n|\r/, "\n"); // normalize line endings
669
670         var sourcesDocument = document.getElementById("sources").contentDocument;
671         var sourcesDiv = sourcesDocument.body;
672         var sourceDiv = sourcesDocument.createElement("div");
673         sourceDiv.id = "file" + fileIndex;
674         sourcesDiv.appendChild(sourceDiv);
675         file.element = sourceDiv;
676
677         var table = sourcesDocument.createElement("table");
678         sourceDiv.appendChild(table);
679
680         var lines = syntaxHighlight(file.source).split("\n");
681         for( var i = 0; i < lines.length; i++ ) {
682             var tr = sourcesDocument.createElement("tr");
683             var td = sourcesDocument.createElement("td");
684             td.className = "gutter";
685             td.title = (i + 1);
686             td.addEventListener("click", addBreakPoint, true);
687             td.addEventListener("mousedown", moveBreakPoint, true);
688             tr.appendChild(td);
689
690             td = sourcesDocument.createElement("td");
691             td.className = "source";
692             td.innerHTML = (lines[i].length ? lines[i] : "&nbsp;");
693             tr.appendChild(td);
694             table.appendChild(tr);
695         }
696
697         file.loaded = true;
698     }
699
700     file.element.style.display = null;
701
702     document.getElementById("filesPopupButtonContent").innerText = (file.url ? file.url : "(unknown script)");
703     
704     var filesSelect = document.getElementById("files");
705     for (var i = 0; i < filesSelect.childNodes.length; i++) {
706         if (filesSelect.childNodes[i].value == fileIndex) {
707             filesSelect.selectedIndex = i;
708             break;
709         }
710     }
711
712     if (manageNavLists) {
713         nextFiles = new Array();
714         if (currentFile != -1)
715             previousFiles.push(currentFile);
716     }
717
718     document.getElementById("navFileLeftButton").disabled = (previousFiles.length == 0);
719     document.getElementById("navFileRightButton").disabled = (nextFiles.length == 0);
720
721     currentFile = fileIndex;
722 }
723
724 function updateFileSource(source, url, force)
725 {
726     var fileIndex = filesLookup[url];
727     if (!fileIndex || !source.length)
728         return;
729
730     var file = files[fileIndex];
731     if (force || file.source.length != source.length || file.source != source) {
732         file.source = source;
733         file.loaded = false;
734
735         if (file.element) {
736             file.element.parentNode.removeChild(file.element);
737             file.element = null;
738         }
739
740         if (currentFile == fileIndex)
741             loadFile(fileIndex, false);
742     }
743 }
744
745 function didParseScript(source, fileSource, url, sourceId, baseLineNumber)
746 {
747     var fileIndex = filesLookup[url];
748     var file = files[fileIndex];
749     var firstLoad = false;
750     if (!fileIndex || !file) {
751         fileIndex = files.length + 1;
752         if (url.length)
753             filesLookup[url] = fileIndex;
754
755         file = new Object();
756         file.scripts = new Array();
757         file.breakpoints = new Array();
758         file.source = (fileSource.length ? fileSource : source);
759         file.url = (url.length ? url : null);
760         file.loaded = false;
761
762         files[fileIndex] = file;
763
764         var filesSelect = document.getElementById("files");
765         var option = document.createElement("option");
766         files[fileIndex].menuOption = option;
767         option.value = fileIndex;
768         option.text = (file.url ? file.url : "(unknown script)");
769         filesSelect.appendChild(option);
770         firstLoad = true;
771     }
772
773     var sourceObj = new Object();
774     sourceObj.file = fileIndex;
775     sourceObj.baseLineNumber = baseLineNumber;
776     file.scripts.push(sourceId);
777     scripts[sourceId] = sourceObj;
778
779     if (!firstLoad)
780         updateFileSource((fileSource.length ? fileSource : source), url, false);
781
782     if (currentFile == -1)
783         loadFile(fileIndex, true);
784 }
785
786 function willExecuteStatement(sourceId, line)
787 {
788     var script = scripts[sourceId];
789     if (line <= 0 || !script)
790         return;
791
792     var file = files[script.file];
793     if (!file)
794         return;
795
796     if (pauseOnNextStatement || file.breakpoints[line] == 1 || (steppingOver && !steppingStack)) {
797         pause();
798         pauseOnNextStatement = false;
799     }
800
801     if (isPaused()) {
802         if (currentFile != script.file)
803             loadFile(script.file, true);
804         if (currentRow)
805             removeStyleClass(currentRow, "current");
806         if (!file.element)
807             return;
808         if (line > file.element.firstChild.childNodes.length)
809             return;
810
811         updateFunctionStack();
812
813         currentRow = file.element.firstChild.childNodes.item(line - 1);
814         if (!currentRow)
815             return;
816
817         addStyleClass(currentRow, "current");
818
819         var sourcesDiv = document.getElementById("sources");
820         var sourcesDocument = document.getElementById("sources").contentDocument;
821         var parent = sourcesDocument.body;
822         var offset = totalOffsetTop(currentRow, parent);
823         if (offset < (parent.scrollTop + 20) || offset > (parent.scrollTop + sourcesDiv.clientHeight - 20))
824             parent.scrollTop = totalOffsetTop(currentRow, parent) - (sourcesDiv.clientHeight / 2) + 10;
825     }
826 }
827
828 function didEnterCallFrame(sourceId, line)
829 {
830     if (steppingOver || steppingOut)
831         steppingStack++;
832     willExecuteStatement(sourceId, line);
833 }
834
835 function willLeaveCallFrame(sourceId, line)
836 {
837     if (line <= 0)
838         resume();
839     willExecuteStatement(sourceId, line);
840     if (!steppingStack)
841         steppingOver = false;
842     if (steppingOut && !steppingStack) {
843         steppingOut = false;
844         pauseOnNextStatement = true;
845     }
846     if ((steppingOver || steppingOut) && steppingStack >= 1)
847         steppingStack--;
848 }
849
850 function showConsoleWindow()
851 {
852     if (!consoleWindow)
853         consoleWindow = window.open("console.html", "console", "top=200, left=200, width=500, height=300, toolbar=yes, resizable=yes");
854 }