webengine/osswebengine/WebKitTools/Drosera/debugger.js
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /*
       
     2  * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
       
     3  * Copyright (C) 2006 David Smith (catfish.man@gmail.com)
       
     4  * Copyright (C) 2007 Vladimir Olexa (vladimir.olexa@gmail.com)
       
     5  *
       
     6  * Redistribution and use in source and binary forms, with or without
       
     7  * modification, are permitted provided that the following conditions
       
     8  * are met:
       
     9  *
       
    10  * 1.  Redistributions of source code must retain the above copyright
       
    11  *     notice, this list of conditions and the following disclaimer. 
       
    12  * 2.  Redistributions in binary form must reproduce the above copyright
       
    13  *     notice, this list of conditions and the following disclaimer in the
       
    14  *     documentation and/or other materials provided with the distribution. 
       
    15  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
       
    16  *     its contributors may be used to endorse or promote products derived
       
    17  *     from this software without specific prior written permission. 
       
    18  *
       
    19  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
       
    20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
       
    21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
       
    22  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
       
    23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
       
    24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
       
    25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
       
    26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
       
    28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    29  */
       
    30 
       
    31 var files = new Array();
       
    32 var filesLookup = new Object();
       
    33 var scripts = new Array();
       
    34 var currentFile = -1;
       
    35 var currentRow = null;
       
    36 var currentStack = null;
       
    37 var currentCallFrame = null;
       
    38 var lastStatement = null;
       
    39 var frameLineNumberStack = new Array();
       
    40 var previousFiles = new Array();
       
    41 var nextFiles = new Array();
       
    42 var isResizingColumn = false;
       
    43 var draggingBreakpoint = null;
       
    44 var steppingOut = false;
       
    45 var steppingOver = false;
       
    46 var steppingStack = 0;
       
    47 var pauseOnNextStatement = false;
       
    48 var pausedWhileLeavingFrame = false;
       
    49 var consoleWindow = null;
       
    50 var breakpointEditorHTML = DebuggerDocument.breakpointEditorHTML();
       
    51 var pendingAction = null;
       
    52 var isPaused = false;
       
    53 
       
    54 ScriptCallFrame = function (functionName, index, row)
       
    55 {
       
    56     this.functionName = functionName;
       
    57     this.index = index;
       
    58     this.row = row;
       
    59     this.localVariableNames = null;
       
    60 }
       
    61 
       
    62 ScriptCallFrame.prototype.valueForScopeVariable = function (name)
       
    63 {
       
    64     return DebuggerDocument.valueForScopeVariableNamed(name, this.index);
       
    65 }
       
    66 
       
    67 ScriptCallFrame.prototype.loadVariables = function ()
       
    68 {
       
    69     if (!this.localVariableNames)
       
    70         this.localVariableNames = DebuggerDocument.localScopeVariableNamesForCallFrame(this.index);
       
    71 
       
    72     var variablesTable = document.getElementById("variablesTable");
       
    73     variablesTable.innerHTML = "";
       
    74 
       
    75     if (!this.localVariableNames)
       
    76         return;
       
    77 
       
    78     for(var i = 0; i < this.localVariableNames.length; i++) {
       
    79         var tr = document.createElement("tr");
       
    80         var td = document.createElement("td");
       
    81         td.innerText = this.localVariableNames[i];
       
    82         td.className = "variable";
       
    83         tr.appendChild(td);
       
    84 
       
    85         td = document.createElement("td");
       
    86         td.innerText = this.valueForScopeVariable(this.localVariableNames[i]);
       
    87         tr.appendChild(td);
       
    88         tr.addEventListener("click", selectVariable, true);
       
    89 
       
    90         variablesTable.appendChild(tr);
       
    91     }
       
    92 }
       
    93 
       
    94 function sleep(numberMillis) 
       
    95 {
       
    96     var now = new Date();
       
    97     var exitTime = now.getTime() + numberMillis;
       
    98     while (true) {
       
    99         now = new Date();
       
   100         if (now.getTime() > exitTime)
       
   101             return;
       
   102     }
       
   103 }
       
   104 
       
   105 function headerMouseDown(element) 
       
   106 {
       
   107     if (!isResizingColumn) 
       
   108         element.style.background = "url(glossyHeaderPressed.png) repeat-x";
       
   109 }
       
   110 
       
   111 function headerMouseUp(element) 
       
   112 {
       
   113     element.style.background = "url(glossyHeader.png) repeat-x";
       
   114 }
       
   115 
       
   116 function headerMouseOut(element) 
       
   117 {
       
   118     element.style.background = "url(glossyHeader.png) repeat-x";
       
   119 }
       
   120 
       
   121 function filesDividerDragStart(event) 
       
   122 {
       
   123     dividerDragStart(document.getElementById("filesDivider"), filesDividerDrag, filesDividerDragEnd, event, "col-resize");
       
   124 }
       
   125 
       
   126 function filesDividerDragEnd(event) 
       
   127 {
       
   128     dividerDragEnd(document.getElementById("filesDivider"), filesDividerDrag, filesDividerDragEnd, event);
       
   129 }
       
   130 
       
   131 function filesDividerDrag(event) 
       
   132 {
       
   133     var element = document.getElementById("filesDivider");
       
   134     if (document.getElementById("filesDivider").dragging == true) {
       
   135         var masterMain = document.getElementById("masterMain");
       
   136         var main = document.getElementById("main");
       
   137         var fileBrowser = document.getElementById("fileBrowser");
       
   138         var x = event.clientX + window.scrollX;
       
   139         var delta = element.dragLastX - x;
       
   140         var newWidth = constrainedWidthFromElement(fileBrowser.clientWidth - delta, masterMain, 0.1, 0.9);
       
   141         if ((fileBrowser.clientWidth - delta) == newWidth) // the width wasn't constrained
       
   142             element.dragLastX = x;
       
   143         fileBrowser.style.width = newWidth + "px";
       
   144         main.style.left = newWidth + "px";
       
   145         event.preventDefault();
       
   146     }
       
   147 }
       
   148 
       
   149 function dividerDragStart(element, dividerDrag, dividerDragEnd, event, cursor) 
       
   150 {
       
   151     element.dragging = true;
       
   152     element.dragLastY = event.clientY + window.scrollY;
       
   153     element.dragLastX = event.clientX + window.scrollX;
       
   154     document.addEventListener("mousemove", dividerDrag, true);
       
   155     document.addEventListener("mouseup", dividerDragEnd, true);
       
   156     document.body.style.cursor = cursor;
       
   157     event.preventDefault();
       
   158 }
       
   159 
       
   160 function dividerDragEnd(element, dividerDrag, dividerDragEnd, event) 
       
   161 {
       
   162     element.dragging = false;
       
   163     document.removeEventListener("mousemove", dividerDrag, true);
       
   164     document.removeEventListener("mouseup", dividerDragEnd, true);
       
   165     document.body.style.removeProperty("cursor");
       
   166 }
       
   167 
       
   168 function dividerDrag(event) 
       
   169 {
       
   170     var element = document.getElementById("divider");
       
   171     if (document.getElementById("divider").dragging == true) {
       
   172         var main = document.getElementById("main");
       
   173         var top = document.getElementById("info");
       
   174         var bottom = document.getElementById("body");
       
   175         var y = event.clientY + window.scrollY;
       
   176         var delta = element.dragLastY - y;
       
   177         var newHeight = constrainedHeightFromElement(top.clientHeight - delta, main);
       
   178         if ((top.clientHeight - delta) == newHeight) // the height wasn't constrained
       
   179             element.dragLastY = y;
       
   180         top.style.height = newHeight + "px";
       
   181         bottom.style.top = newHeight + "px";
       
   182         event.preventDefault();
       
   183     }
       
   184 }
       
   185 
       
   186 function sourceDividerDragStart(event) 
       
   187 {
       
   188     dividerDragStart(document.getElementById("divider"), dividerDrag, sourceDividerDragEnd, event, "row-resize");
       
   189 }
       
   190 
       
   191 function sourceDividerDragEnd(event) 
       
   192 {
       
   193     dividerDragEnd(document.getElementById("divider"), dividerDrag, sourceDividerDragEnd, event);
       
   194 }
       
   195 
       
   196 function infoDividerDragStart(event) 
       
   197 {
       
   198     dividerDragStart(document.getElementById("infoDivider"), infoDividerDrag, infoDividerDragEnd, event, "col-resize");
       
   199 }
       
   200 
       
   201 function infoDividerDragEnd(event) 
       
   202 {
       
   203     dividerDragEnd(document.getElementById("infoDivider"), infoDividerDrag, infoDividerDragEnd, event);
       
   204 }
       
   205 
       
   206 function infoDividerDrag(event) 
       
   207 {
       
   208     var element = document.getElementById("infoDivider");
       
   209     if (document.getElementById("infoDivider").dragging == true) {
       
   210         var main = document.getElementById("main");
       
   211         var leftPane = document.getElementById("leftPane");
       
   212         var rightPane = document.getElementById("rightPane");
       
   213         var x = event.clientX + window.scrollX;
       
   214         var delta = element.dragLastX - x;
       
   215         var newWidth = constrainedWidthFromElement(leftPane.clientWidth - delta, main);
       
   216         if ((leftPane.clientWidth - delta) == newWidth) // the width wasn't constrained
       
   217             element.dragLastX = x;
       
   218         leftPane.style.width = newWidth + "px";
       
   219         rightPane.style.left = newWidth + "px";
       
   220         event.preventDefault();
       
   221     }
       
   222 }
       
   223 
       
   224 function columnResizerDragStart(event) 
       
   225 {
       
   226     isResizingColumn = true;
       
   227     dividerDragStart(document.getElementById("variableColumnResizer"), columnResizerDrag, columnResizerDragEnd, event, "col-resize");
       
   228 }
       
   229 
       
   230 function columnResizerDragEnd(event) 
       
   231 {
       
   232     isResizingColumn = false;
       
   233     dividerDragEnd(document.getElementById("variableColumnResizer"), columnResizerDrag, columnResizerDragEnd, event);
       
   234 }
       
   235 
       
   236 function columnResizerDrag(event) 
       
   237 {
       
   238     var element = document.getElementById("variableColumnResizer");
       
   239     if (element.dragging == true) {
       
   240         var main = document.getElementById("rightPane");
       
   241         var variableColumn = document.getElementById("variable");
       
   242         var rules = document.defaultView.getMatchedCSSRules(variableColumn, "");
       
   243         for (var i = 0; i < rules.length; i++) {
       
   244             if (rules[i].selectorText == ".variable") {
       
   245                 var columnRule = rules[i];
       
   246                 break;
       
   247             }
       
   248         }
       
   249 
       
   250         var x = event.clientX + window.scrollX;
       
   251         var delta = element.dragLastX - x;
       
   252         var newWidth = constrainedWidthFromElement(variableColumn.clientWidth - delta, main);
       
   253         if ((variableColumn.clientWidth - delta) == newWidth) // the width wasn't constrained
       
   254             element.dragLastX = x;
       
   255         columnRule.style.width = newWidth + "px";
       
   256         element.style.left = newWidth + "px";
       
   257         event.preventDefault();
       
   258     }
       
   259 }
       
   260 
       
   261 function constrainedWidthFromElement(width, element, constrainLeft, constrainRight) 
       
   262 {
       
   263     if (constrainLeft === undefined) constrainLeft = 0.25;
       
   264     if (constrainRight === undefined) constrainRight = 0.75;
       
   265     
       
   266     if (width < element.clientWidth * constrainLeft)
       
   267         width = element.clientWidth * constrainLeft;
       
   268     else if (width > element.clientWidth * constrainRight)
       
   269         width = element.clientWidth * constrainRight;
       
   270     return width;
       
   271 }
       
   272 
       
   273 function constrainedHeightFromElement(height, element) 
       
   274 {
       
   275     if (height < element.clientHeight * 0.25)
       
   276         height = element.clientHeight * 0.25;
       
   277     else if (height > element.clientHeight * 0.75)
       
   278         height = element.clientHeight * 0.75;
       
   279     return height;
       
   280 }
       
   281 
       
   282 function loaded() 
       
   283 {
       
   284     document.getElementById("divider").addEventListener("mousedown", sourceDividerDragStart, false);
       
   285     document.getElementById("infoDivider").addEventListener("mousedown", infoDividerDragStart, false);
       
   286     document.getElementById("filesDivider").addEventListener("mousedown", filesDividerDragStart, false);
       
   287     document.getElementById("variableColumnResizer").addEventListener("mousedown", columnResizerDragStart, false);
       
   288 }
       
   289 
       
   290 function pause() 
       
   291 {
       
   292     DebuggerDocument.pause();
       
   293     isPaused = true;
       
   294 }
       
   295 
       
   296 function resume()
       
   297 {
       
   298     if (currentRow) {
       
   299         currentRow.removeStyleClass("current");
       
   300         currentRow = null;
       
   301     }
       
   302 
       
   303     var stackframeTable = document.getElementById("stackframeTable");
       
   304     stackframeTable.innerHTML = ""; // clear the content
       
   305     var variablesTable = document.getElementById("variablesTable");
       
   306     variablesTable.innerHTML = ""; // clear the content
       
   307     currentStack = null;
       
   308     currentCallFrame = null;
       
   309 
       
   310     pauseOnNextStatement = false;
       
   311     pausedWhileLeavingFrame = false;
       
   312     steppingOut = false;
       
   313     steppingOver = false;
       
   314     steppingStack = 0;
       
   315 
       
   316     DebuggerDocument.resume();
       
   317     isPaused = false;
       
   318 }
       
   319 
       
   320 function stepInto()
       
   321 {
       
   322     pauseOnNextStatement = false;
       
   323     steppingOut = false;
       
   324     steppingOver = false;
       
   325     steppingStack = 0;
       
   326     DebuggerDocument.stepInto();
       
   327 }
       
   328 
       
   329 function stepOver()
       
   330 {
       
   331     pauseOnNextStatement = false;
       
   332     steppingOver = true;
       
   333     steppingStack = 0;
       
   334     DebuggerDocument.resume();
       
   335 }
       
   336 
       
   337 function stepOut()
       
   338 {
       
   339     pauseOnNextStatement = pausedWhileLeavingFrame;
       
   340     steppingOver = false;
       
   341     steppingStack = 0;
       
   342     steppingOut = true;
       
   343     DebuggerDocument.resume();
       
   344 }
       
   345 
       
   346 Element.prototype.removeStyleClass = function(className) 
       
   347 {
       
   348     if (this.hasStyleClass(className))
       
   349         this.className = this.className.replace(className, "");
       
   350 }
       
   351 
       
   352 Element.prototype.addStyleClass = function(className) 
       
   353 {
       
   354     if (!this.hasStyleClass(className))
       
   355         this.className += (this.className.length ? " " + className : className);
       
   356 }
       
   357 
       
   358 Element.prototype.hasStyleClass = function(className) 
       
   359 {
       
   360     return this.className.indexOf(className) != -1;
       
   361 }
       
   362 
       
   363 Element.prototype.firstParentWithClass = function(className) 
       
   364 {
       
   365     var node = this.parentNode;
       
   366     while(!node.hasStyleClass(className)) {
       
   367         if (node == document) 
       
   368             return null;
       
   369         node = node.parentNode;
       
   370     }
       
   371     return node;
       
   372 }
       
   373 
       
   374 Element.prototype.query = function(query) 
       
   375 {
       
   376     return document.evaluate(query, this, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
       
   377 }
       
   378 
       
   379 Element.prototype.removeChildren = function()
       
   380 {
       
   381     while (this.firstChild) 
       
   382         this.removeChild(this.firstChild);        
       
   383 }
       
   384 
       
   385 function breakpointAction(event)
       
   386 {
       
   387     var file = files[currentFile];
       
   388     var lineNum = event.target.title;
       
   389 
       
   390     if (!file.breakpoints[lineNum])
       
   391         file.breakpoints[lineNum] = new BreakPoint(event.target.parentNode, file, lineNum);
       
   392     else
       
   393         toggleBreakpointOnLine(lineNum);
       
   394 }
       
   395 
       
   396 BreakPoint = function(row, file, line) 
       
   397 {
       
   398     this.row = row;
       
   399     this.file = file;
       
   400     this.line = line;
       
   401     row.addStyleClass("breakpoint");
       
   402     row.removeStyleClass("disabled");
       
   403     this.value = "break";
       
   404     this.enabled = true;
       
   405     this.editor = null;
       
   406     this.type = 0;
       
   407     this.hitcount = 0;
       
   408 }
       
   409 
       
   410 function toggleBreakpointEditorOnLine(lineNum)
       
   411 {
       
   412     if (pendingAction) {
       
   413         clearTimeout(pendingAction);
       
   414         pendingAction = null;
       
   415     }
       
   416     var file = files[currentFile];
       
   417     bp = file.breakpoints[lineNum];
       
   418     if (bp) {
       
   419         var editor = bp.editor;
       
   420         if (!editor) {
       
   421             var sourcesDocument = document.getElementById("sources").contentDocument;
       
   422             editor = sourcesDocument.createElement("div");
       
   423             editor.className = "editor";
       
   424             editor.id = lineNum;
       
   425             editor.innerHTML = breakpointEditorHTML;
       
   426             
       
   427             bp.row.childNodes[1].appendChild(editor);
       
   428 
       
   429             bp.editor = editor;
       
   430             file.breakpoints[lineNum] = bp;
       
   431 
       
   432             editor.query('.//input[@class="enable"]').checked = bp.enabled;
       
   433 
       
   434             editor.query('.//select[@class="editorDropdown"]').selectedIndex = bp.type;
       
   435             updateBreakpointTypeOnLine(lineNum);
       
   436 
       
   437             editor.query('.//span[@class="hitCounter"]').innerText = bp.hitcount;
       
   438                 
       
   439             setConditionFieldText(bp);
       
   440         } else {
       
   441             saveBreakpointOnLine(lineNum);
       
   442             bp.row.childNodes[1].removeChild(editor);
       
   443             bp.editor = null;
       
   444         }
       
   445     }
       
   446 }
       
   447 
       
   448 function updateBreakpointTypeOnLine(line)
       
   449 {
       
   450     var breakpoint = files[currentFile].breakpoints[line];
       
   451     var editor = breakpoint.editor;
       
   452     var label = editor.query('.//label[@class="conditionLabel"]');
       
   453     var dropdown = editor.query('.//select[@class="editorDropdown"]');
       
   454     breakpoint.type = dropdown.selectedIndex;
       
   455     switch(breakpoint.type) {
       
   456         case 0:
       
   457             label.innerText = "Condition:";
       
   458             break;
       
   459         case 1:
       
   460             label.innerText = "Expression:";
       
   461             break;
       
   462     }
       
   463 }
       
   464 
       
   465 function setConditionFieldText(breakpoint)
       
   466 {
       
   467     var conditionField = breakpoint.editor.query('.//div[@class="condition"]');
       
   468 
       
   469     var functionBody = breakpoint.value;
       
   470     if (!functionBody || functionBody == "break")
       
   471         functionBody = "";
       
   472     else {
       
   473         var startIndex = functionBody.indexOf("return((") + 8;
       
   474         var endIndex = null;
       
   475         if (startIndex != 7) //-1 + 8, yes, that's lame
       
   476             endIndex = functionBody.lastIndexOf("))");
       
   477         else {
       
   478             startIndex = functionBody.indexOf("{") + 1;
       
   479             endIndex = functionBody.lastIndexOf("}"); 
       
   480         }
       
   481         functionBody = functionBody.substring(startIndex, endIndex);
       
   482     }
       
   483     conditionField.innerText = functionBody;
       
   484     conditionField.addEventListener("keyup", new Function("saveBreakpointOnLine(" + breakpoint.line + ");"), false);
       
   485     conditionField.focus();
       
   486 }
       
   487 
       
   488 function saveBreakpointOnLine(lineNum)
       
   489 {
       
   490     var file = files[currentFile];
       
   491     var breakpoint = file.breakpoints[lineNum];
       
   492     row = file.element.firstChild.childNodes.item(lineNum - 1);
       
   493     var editor = breakpoint.editor;
       
   494     var body = editor.query('.//div[@class="condition"]').innerText;
       
   495     var actionIndex = editor.query('.//select[@class="editorDropdown"]').selectedIndex;
       
   496     if (body.length == 0)
       
   497         breakpoint.value = "break";
       
   498     else if (body.indexOf("return") != -1)
       
   499         breakpoint.value = "__drosera_breakpoint_conditional_func = function() {" + body + "}; __drosera_breakpoint_conditional_func();";
       
   500     else
       
   501         breakpoint.value = "__drosera_breakpoint_conditional_func = function() { return((" + body + ")); }; __drosera_breakpoint_conditional_func();";
       
   502 }
       
   503 
       
   504 function toggleBreakpointOnLine(lineNum)
       
   505 {
       
   506     var breakpoint = files[currentFile].breakpoints[lineNum];
       
   507     pendingAction = null;
       
   508     if (breakpoint.enabled)
       
   509         breakpoint.row.addStyleClass("disabled");
       
   510     else
       
   511         breakpoint.row.removeStyleClass("disabled");
       
   512     
       
   513     var hack = breakpoint.row.offsetTop; // force a relayout if needed.
       
   514     
       
   515     breakpoint.enabled = !breakpoint.enabled;
       
   516     var editor = breakpoint.editor;
       
   517     if (editor) {
       
   518         editor.query('.//input[@class="enable"]').checked = breakpoint.enabled;
       
   519         setConditionFieldText(breakpoint, lineNum);
       
   520     }
       
   521 }
       
   522 
       
   523 function moveBreakPoint(event)
       
   524 {
       
   525     if (event.target.parentNode.hasStyleClass("breakpoint")) {
       
   526         draggingBreakpoint = event.target;
       
   527         draggingBreakpoint.started = false;
       
   528         draggingBreakpoint.dragLastY = event.clientY + window.scrollY;
       
   529         draggingBreakpoint.dragLastX = event.clientX + window.scrollX;
       
   530         var sourcesDocument = document.getElementById("sources").contentDocument;
       
   531         sourcesDocument.addEventListener("mousemove", breakpointDrag, true);
       
   532         sourcesDocument.addEventListener("mouseup", breakpointDragEnd, true);
       
   533         sourcesDocument.body.style.cursor = "default";
       
   534     }
       
   535 }
       
   536 
       
   537 function breakpointDrag(event)
       
   538 {
       
   539     var sourcesDocument = document.getElementById("sources").contentDocument;
       
   540     if (!draggingBreakpoint) {
       
   541         sourcesDocument.removeEventListener("mousemove", breakpointDrag, true);
       
   542         sourcesDocument.removeEventListener("mouseup", breakpointDragEnd, true);
       
   543         sourcesDocument.body.style.removeProperty("cursor");
       
   544         return;
       
   545     }
       
   546 
       
   547     var x = event.clientX + window.scrollX;
       
   548     var y = event.clientY + window.scrollY;
       
   549     var deltaX = draggingBreakpoint.dragLastX - x;
       
   550     var deltaY = draggingBreakpoint.dragLastY - y;
       
   551     if (draggingBreakpoint.started || deltaX > 4 || deltaY > 4 || deltaX < -4 || deltaY < -4) {
       
   552     
       
   553         if (!draggingBreakpoint.started) {
       
   554             var lineNum = draggingBreakpoint.title;
       
   555             var file = files[currentFile];
       
   556             var breakpoint = file.breakpoints[lineNum];
       
   557             draggingBreakpoint.breakpoint = breakpoint;
       
   558             breakpoint.row.removeStyleClass("breakpoint");
       
   559             breakpoint.row.removeStyleClass("disabled");
       
   560 
       
   561             var editor = breakpoint.editor;
       
   562             if (editor)
       
   563                 toggleBreakpointEditorOnLine(lineNum);
       
   564             
       
   565             draggingBreakpoint.started = true;
       
   566                         
       
   567             file.breakpoints[lineNum] = null;
       
   568 
       
   569             var dragImage = sourcesDocument.createElement("img");
       
   570             if (draggingBreakpoint.breakpoint.enabled)
       
   571                 dragImage.src = "breakPoint.tif";
       
   572             else
       
   573                 dragImage.src = "breakPointDisabled.tif";
       
   574 
       
   575             dragImage.id = "breakpointDrag";
       
   576             dragImage.style.top = y - 8 + "px";
       
   577             dragImage.style.left = x - 12 + "px";
       
   578             sourcesDocument.body.appendChild(dragImage);
       
   579         } else {
       
   580             var dragImage = sourcesDocument.getElementById("breakpointDrag");
       
   581             if (!dragImage) {
       
   582                 sourcesDocument.removeEventListener("mousemove", breakpointDrag, true);
       
   583                 sourcesDocument.removeEventListener("mouseup", breakpointDragEnd, true);
       
   584                 sourcesDocument.body.style.removeProperty("cursor");
       
   585                 return;
       
   586             }
       
   587 
       
   588             dragImage.style.top = y - 8 + "px";
       
   589             dragImage.style.left = x - 12 + "px";
       
   590             if (x > 40)
       
   591                 dragImage.style.visibility = "hidden";
       
   592             else
       
   593                 dragImage.style.removeProperty("visibility");
       
   594         }
       
   595 
       
   596         draggingBreakpoint.dragLastX = x;
       
   597         draggingBreakpoint.dragLastY = y;
       
   598     }
       
   599 }
       
   600 
       
   601 function breakpointDragEnd(event)
       
   602 {
       
   603     var sourcesDocument = document.getElementById("sources").contentDocument;
       
   604     sourcesDocument.removeEventListener("mousemove", breakpointDrag, true);
       
   605     sourcesDocument.removeEventListener("mouseup", breakpointDragEnd, true);
       
   606     sourcesDocument.body.style.removeProperty("cursor");
       
   607 
       
   608     var dragImage = sourcesDocument.getElementById("breakpointDrag");
       
   609     if (!dragImage)
       
   610         return;
       
   611         
       
   612     dragImage.parentNode.removeChild(dragImage);
       
   613 
       
   614     var x = event.clientX + window.scrollX;
       
   615     if (x > 40 || !draggingBreakpoint)
       
   616         return;
       
   617 
       
   618     var y = event.clientY + window.scrollY;
       
   619     var rowHeight = draggingBreakpoint.parentNode.offsetHeight;
       
   620     var row = Math.ceil(y / rowHeight);
       
   621     if (row <= 0)
       
   622         row = 1;
       
   623 
       
   624     var file = files[currentFile];
       
   625     var table = file.element.firstChild;
       
   626     if (row > table.childNodes.length)
       
   627         return;
       
   628 
       
   629     var tr = table.childNodes.item(row - 1);
       
   630     if (!tr)
       
   631         return;
       
   632         
       
   633     var breakpoint = draggingBreakpoint.breakpoint;
       
   634     breakpoint.row = tr;
       
   635     
       
   636     // leave the editor there if it exists... we'll want to update it to the new values
       
   637     breakpoint.editor = file.breakpoints[row].editor;
       
   638     
       
   639     file.breakpoints[row] = breakpoint;
       
   640     
       
   641     if (breakpoint.editor) {
       
   642         breakpoint.editor.id = row;
       
   643         updateBreakpointTypeOnLine(row);
       
   644         setConditionFieldText(breakpoint);
       
   645     }
       
   646     
       
   647     if (!breakpoint.enabled)
       
   648         tr.addStyleClass("disabled");
       
   649 
       
   650     tr.addStyleClass("breakpoint");
       
   651     
       
   652     draggingBreakpoint = null;
       
   653 }
       
   654 
       
   655 function totalOffsetTop(element, stop)
       
   656 {
       
   657     var currentTop = 0;
       
   658     while (element.offsetParent) {
       
   659         currentTop += element.offsetTop
       
   660         element = element.offsetParent;
       
   661         if (element == stop)
       
   662             break;
       
   663     }
       
   664     return currentTop;
       
   665 }
       
   666 
       
   667 function switchFile(fileIndex)
       
   668 {
       
   669     var filesSelect = document.getElementById("files");
       
   670     
       
   671     if (fileIndex === undefined) 
       
   672         fileIndex = filesSelect.selectedIndex;
       
   673     
       
   674     fileClicked(filesSelect.options[fileIndex].value, false);
       
   675     loadFile(filesSelect.options[fileIndex].value, true);    
       
   676 }
       
   677 
       
   678 function syntaxHighlight(code, file)
       
   679 {
       
   680     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 };
       
   681 
       
   682     function echoChar(c) {
       
   683         if (c == '<')
       
   684             result += '&lt;';
       
   685         else if (c == '>')
       
   686             result += '&gt;';
       
   687         else if (c == '&')
       
   688             result += '&amp;';
       
   689         else if (c == '\t')
       
   690             result += '    ';
       
   691         else
       
   692             result += c;
       
   693     }
       
   694 
       
   695     function isDigit(number) {
       
   696         var string = "1234567890";
       
   697         if (string.indexOf(number) != -1)
       
   698             return true;
       
   699         return false;
       
   700     }
       
   701 
       
   702     function isHex(hex) {
       
   703         var string = "1234567890abcdefABCDEF";
       
   704         if (string.indexOf(hex) != -1)
       
   705             return true;
       
   706         return false;
       
   707     }
       
   708 
       
   709     function isLetter(letter) {
       
   710         var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
       
   711         if (string.indexOf(letter) != -1)
       
   712             return true;
       
   713         return false;
       
   714     }
       
   715 
       
   716     var result = "";
       
   717     var cPrev = "";
       
   718     var c = "";
       
   719     var cNext = "";
       
   720     for (var i = 0; i < code.length; i++) {
       
   721         cPrev = c;
       
   722         c = code.charAt(i);
       
   723         cNext = code.charAt(i + 1);
       
   724 
       
   725         if (c == "/" && cNext == "*") {
       
   726             result += "<span class=\"comment\">";
       
   727             echoChar(c);
       
   728             echoChar(cNext);
       
   729             for (i += 2; i < code.length; i++) {
       
   730                 c = code.charAt(i);
       
   731                 if (c == "\n")
       
   732                     result += "</span>";
       
   733                 echoChar(c);
       
   734                 if (c == "\n")
       
   735                     result += "<span class=\"comment\">";
       
   736                 if (cPrev == "*" && c == "/")
       
   737                     break;
       
   738                 cPrev = c;
       
   739             }
       
   740             result += "</span>";
       
   741             continue;
       
   742         } else if (c == "/" && cNext == "/") {
       
   743             result += "<span class=\"comment\">";
       
   744             echoChar(c);
       
   745             echoChar(cNext);
       
   746             for (i += 2; i < code.length; i++) {
       
   747                 c = code.charAt(i);
       
   748                 if (c == "\n")
       
   749                     break;
       
   750                 echoChar(c);
       
   751             }
       
   752             result += "</span>";
       
   753             echoChar(c);
       
   754             continue;
       
   755         } else if (c == "\"" || c == "'") {
       
   756             var instringtype = c;
       
   757             var stringstart = i;
       
   758             result += "<span class=\"string\">";
       
   759             echoChar(c);
       
   760             for (i += 1; i < code.length; i++) {
       
   761                 c = code.charAt(i);
       
   762                 if (stringstart < (i - 1) && cPrev == instringtype && code.charAt(i - 2) != "\\")
       
   763                     break;
       
   764                 echoChar(c);
       
   765                 cPrev = c;
       
   766             }
       
   767             result += "</span>";
       
   768             echoChar(c);
       
   769             continue;
       
   770         } else if (c == "0" && cNext == "x" && (i == 0 || (!isLetter(cPrev) && !isDigit(cPrev)))) {
       
   771             result += "<span class=\"number\">";
       
   772             echoChar(c);
       
   773             echoChar(cNext);
       
   774             for (i += 2; i < code.length; i++) {
       
   775                 c = code.charAt(i);
       
   776                 if (!isHex(c))
       
   777                     break;
       
   778                 echoChar(c);
       
   779             }
       
   780             result += "</span>";
       
   781             echoChar(c);
       
   782             continue;
       
   783         } else if ((isDigit(c) || ((c == "-" || c == ".") && isDigit(cNext))) && (i == 0 || (!isLetter(cPrev) && !isDigit(cPrev)))) {
       
   784             result += "<span class=\"number\">";
       
   785             echoChar(c);
       
   786             for (i += 1; i < code.length; i++) {
       
   787                 c = code.charAt(i);
       
   788                 if (!isDigit(c) && c != ".")
       
   789                     break;
       
   790                 echoChar(c);
       
   791             }
       
   792             result += "</span>";
       
   793             echoChar(c);
       
   794             continue;
       
   795         } else if (isLetter(c) && (i == 0 || !isLetter(cPrev))) {
       
   796             var keyword = c;
       
   797             var cj = "";
       
   798             for (var j = i + 1; j < i + 12 && j < code.length; j++) {
       
   799                 cj = code.charAt(j);
       
   800                 if (!isLetter(cj))
       
   801                     break;
       
   802                 keyword += cj;
       
   803             }
       
   804 
       
   805             if (keywords[keyword]) {
       
   806                 var functionName = "";
       
   807                 var functionIsAnonymous = false;
       
   808                 if (keyword == "function") {
       
   809                     var functionKeywordOffset = 8;
       
   810                     for (var j = i + functionKeywordOffset; j < code.length; j++) {
       
   811                         cj = code.charAt(j);
       
   812                         if (cj == " ")
       
   813                             continue;
       
   814                         if (cj == "(")
       
   815                             break;
       
   816                         functionName += cj;
       
   817                     }
       
   818 
       
   819                     if (!functionName.length) {
       
   820                         functionIsAnonymous = true;
       
   821                         var functionAssignmentFound = false;
       
   822                         var functionNameStart = -1;
       
   823                         var functionNameEnd = -1;
       
   824 
       
   825                         for (var j = i - 1; j >= 0; j--) {
       
   826                             cj = code.charAt(j);
       
   827                             if (cj == ":" || cj == "=") {
       
   828                                 functionAssignmentFound = true;
       
   829                                 continue;
       
   830                             }
       
   831 
       
   832                             var curCharIsSpace = (cj == " " || cj == "\t" || cj == "\n");
       
   833                             if (functionAssignmentFound && functionNameEnd == -1 && !curCharIsSpace) {
       
   834                                 functionNameEnd = j + 1;
       
   835                             } else if (!functionAssignmentFound && !curCharIsSpace) {
       
   836                                 break;
       
   837                             } else if (functionNameEnd != -1 && curCharIsSpace) {
       
   838                                 functionNameStart = j;
       
   839                                 break;
       
   840                             }
       
   841                         }
       
   842 
       
   843                         if (functionNameStart != -1 && functionNameEnd != -1)
       
   844                             functionName = code.substring(functionNameStart, functionNameEnd);
       
   845                     }
       
   846 
       
   847                     if (!functionName.length)
       
   848                         functionName = "function";
       
   849 
       
   850                     file.functionNames.push(functionName);
       
   851                 }
       
   852 
       
   853                 var fileIndex = filesLookup[file.url];
       
   854 
       
   855                 if (keyword == "function" && functionIsAnonymous)
       
   856                     result += "<span class=\"keyword\"><a name=\"function-" + fileIndex + "-" + file.functionNames.length + "\" id=\"" + fileIndex + "-" + file.functionNames.length + "\">" + keyword + "</a></span>";
       
   857                 else
       
   858                     result += "<span class=\"keyword\">" + keyword + "</span>";
       
   859 
       
   860                 if (functionName.length && !functionIsAnonymous) {
       
   861                     result += " <a name=\"function-" + fileIndex + "-" + file.functionNames.length + "\" id=\"" + fileIndex + "-" + file.functionNames.length + "\">" + functionName + "</a>";
       
   862                     i += keyword.length + functionName.length;
       
   863                 } else
       
   864                     i += keyword.length - 1;
       
   865 
       
   866                 continue;
       
   867             }
       
   868         }
       
   869 
       
   870         echoChar(c);
       
   871     }
       
   872         
       
   873     return result;
       
   874 }
       
   875 
       
   876 function navFilePrevious(element)
       
   877 {
       
   878     if (element.disabled)
       
   879         return;
       
   880     var lastFile = previousFiles.pop();
       
   881     if (currentFile != -1)
       
   882         nextFiles.unshift(currentFile);
       
   883     loadFile(lastFile, false);
       
   884 }
       
   885 
       
   886 function navFileNext(element)
       
   887 {
       
   888     if (element.disabled)
       
   889         return;
       
   890     var lastFile = nextFiles.shift();
       
   891     if (currentFile != -1)
       
   892         previousFiles.push(currentFile);
       
   893     loadFile(lastFile, false);
       
   894 }
       
   895 
       
   896 function updateFunctionStack()
       
   897 {
       
   898     var stackframeTable = document.getElementById("stackframeTable");
       
   899     stackframeTable.innerHTML = ""; // clear the content
       
   900 
       
   901     currentStack = new Array();
       
   902     var stack = DebuggerDocument.currentFunctionStack();
       
   903     for(var i = 0; i < stack.length; i++) {
       
   904         var tr = document.createElement("tr");
       
   905         var td = document.createElement("td");
       
   906         td.className = "stackNumber";
       
   907         td.innerText = i;
       
   908         tr.appendChild(td);
       
   909 
       
   910         td = document.createElement("td");
       
   911         td.innerText = stack[i];
       
   912         tr.appendChild(td);
       
   913         tr.addEventListener("click", selectStackFrame, true);
       
   914 
       
   915         stackframeTable.appendChild(tr);
       
   916 
       
   917         var frame = new ScriptCallFrame(stack[i], i, tr);
       
   918         tr.callFrame = frame;
       
   919         currentStack.push(frame);
       
   920 
       
   921         if (i == 0) {
       
   922             tr.addStyleClass("current");
       
   923             frame.loadVariables();
       
   924             currentCallFrame = frame;
       
   925         }
       
   926     }
       
   927 }
       
   928 
       
   929 function selectStackFrame(event)
       
   930 {
       
   931     var stackframeTable = document.getElementById("stackframeTable");
       
   932     var rows = stackframeTable.childNodes;
       
   933     for (var i = 0; i < rows.length; i++)
       
   934         rows[i].removeStyleClass("current");
       
   935     this.addStyleClass("current");
       
   936     this.callFrame.loadVariables();
       
   937     currentCallFrame = this.callFrame;
       
   938 
       
   939     if (frameLineNumberInfo = frameLineNumberStack[this.callFrame.index - 1])
       
   940         jumpToLine(frameLineNumberInfo[0], frameLineNumberInfo[1]);
       
   941     else if (this.callFrame.index == 0)
       
   942         jumpToLine(lastStatement[0], lastStatement[1]);
       
   943 }
       
   944 
       
   945 function selectVariable(event)
       
   946 {
       
   947     var variablesTable = document.getElementById("variablesTable");
       
   948     var rows = variablesTable.childNodes;
       
   949     for (var i = 0; i < rows.length; i++)
       
   950         rows[i].removeStyleClass("current");
       
   951     this.addStyleClass("current");
       
   952 }
       
   953 
       
   954 function switchFunction(index, shouldResetPopup)
       
   955 {
       
   956     if (shouldResetPopup === undefined) shouldResetPopup = false;
       
   957     var sourcesFrame = window.frames['sourcesFrame'];
       
   958     
       
   959     if (shouldResetPopup || index == 0) {
       
   960         document.getElementById("functionPopupButtonContent").innerHTML = '<span class="placeholder">&lt;No selected symbol&gt;</span>';
       
   961         return;
       
   962     }
       
   963 
       
   964     var functionSelect = document.getElementById("functions");
       
   965     var selectedFunction = functionSelect.childNodes[index];
       
   966     var selection = sourcesFrame.getSelection();
       
   967     var currentFunction = selectedFunction.value;     
       
   968     var currentFunctionElement = sourcesFrame.contentDocument.getElementById(currentFunction);
       
   969     
       
   970     functionSelect.blur();
       
   971     sourcesFrame.focus();
       
   972     selection.setBaseAndExtent(currentFunctionElement, 0, currentFunctionElement, 1);
       
   973     sourcesFrame.location.hash = "#function-" + selectedFunction.value;
       
   974     document.getElementById("functionPopupButtonContent").innerText = selectedFunction.innerText;
       
   975 }
       
   976 
       
   977 function loadFile(fileIndex, manageNavLists)
       
   978 {
       
   979     var file = files[fileIndex];
       
   980     if (!file)
       
   981         return;
       
   982 
       
   983     if (currentFile != -1 && files[currentFile] && files[currentFile].element)
       
   984         files[currentFile].element.style.display = "none";
       
   985 
       
   986     if (!file.loaded) {
       
   987         var sourcesDocument = document.getElementById("sources").contentDocument;
       
   988         var sourcesDiv = sourcesDocument.body;
       
   989         var sourceDiv = sourcesDocument.createElement("div");
       
   990         sourceDiv.id = "file" + fileIndex;
       
   991         sourcesDiv.appendChild(sourceDiv);
       
   992         file.element = sourceDiv;
       
   993 
       
   994         var table = sourcesDocument.createElement("table");
       
   995         sourceDiv.appendChild(table);
       
   996 
       
   997         var normalizedSource = file.source.replace(/\r\n|\r/g, "\n"); // normalize line endings
       
   998         var lines = syntaxHighlight(normalizedSource, file).split("\n");
       
   999         for( var i = 0; i < lines.length; i++ ) {
       
  1000             var tr = sourcesDocument.createElement("tr");
       
  1001             var td = sourcesDocument.createElement("td");
       
  1002             td.className = "gutter";
       
  1003             td.title = (i + 1);
       
  1004             td.addEventListener("click", breakpointAction, true);
       
  1005             td.addEventListener("dblclick", function(event) { toggleBreakpointEditorOnLine(event.target.title); }, true);
       
  1006             td.addEventListener("mousedown", moveBreakPoint, true);
       
  1007             tr.appendChild(td);
       
  1008 
       
  1009             td = sourcesDocument.createElement("td");
       
  1010             td.className = "source";
       
  1011             td.innerHTML = (lines[i].length ? lines[i] : "&nbsp;");
       
  1012             tr.appendChild(td);
       
  1013             table.appendChild(tr);
       
  1014         }
       
  1015         
       
  1016         file.loaded = true;
       
  1017     }
       
  1018 
       
  1019     file.element.style.removeProperty("display");
       
  1020 
       
  1021     document.getElementById("filesPopupButtonContent").innerText = (file.url ? file.url : "(unknown script)");
       
  1022     
       
  1023     var filesSelect = document.getElementById("files");
       
  1024     for (var i = 0; i < filesSelect.childNodes.length; i++) {
       
  1025         if (filesSelect.childNodes[i].value == fileIndex) {
       
  1026             filesSelect.selectedIndex = i;
       
  1027             break;
       
  1028         }
       
  1029     }
       
  1030 
       
  1031     // Populate the function names pop-up
       
  1032     if (file.functionNames.length > 0) {
       
  1033         var functionSelect = document.getElementById("functions");
       
  1034         var functionOption = document.createElement("option");
       
  1035         
       
  1036         document.getElementById("functionNamesPopup").style.display = "inline";
       
  1037         switchFunction(0, true);
       
  1038         
       
  1039         functionSelect.removeChildren();
       
  1040         functionOption.value = null;
       
  1041         functionOption.text = "<No selected symbol>";
       
  1042         functionSelect.appendChild(functionOption);
       
  1043         
       
  1044         for (var i = 0; i < file.functionNames.length; i++) {
       
  1045             functionOption = document.createElement("option");
       
  1046             functionOption.value = fileIndex + "-" + (i+1);
       
  1047             functionOption.text = file.functionNames[i] + "()";
       
  1048             functionSelect.appendChild(functionOption);
       
  1049         }
       
  1050     } else
       
  1051         document.getElementById("functionNamesPopup").style.display = "none";
       
  1052     
       
  1053     if (manageNavLists) {
       
  1054         nextFiles = new Array();
       
  1055         if (currentFile != -1)
       
  1056             previousFiles.push(currentFile);
       
  1057     }
       
  1058 
       
  1059     document.getElementById("navFileLeftButton").disabled = (previousFiles.length == 0);
       
  1060     document.getElementById("navFileRightButton").disabled = (nextFiles.length == 0);
       
  1061 
       
  1062     //Remember and recall scroll position for current file and file we just loaded
       
  1063     var frameBody = document.getElementById("sources").contentDocument.body;
       
  1064     if (currentFile != -1) 
       
  1065         files[currentFile].scrollPosition = frameBody.scrollTop;
       
  1066     frameBody.scrollTop = file.scrollPosition;
       
  1067     frameBody.scrollLeft = 0;
       
  1068     
       
  1069     currentFile = fileIndex;
       
  1070 }
       
  1071 
       
  1072 function updateFileSource(source, url, force)
       
  1073 {
       
  1074     var fileIndex = filesLookup[url];
       
  1075     if (!fileIndex || !source.length)
       
  1076         return;
       
  1077 
       
  1078     var file = files[fileIndex];
       
  1079     if (force || file.source.length != source.length || file.source != source) {
       
  1080         file.source = source;
       
  1081         file.loaded = false;
       
  1082 
       
  1083         if (file.element) {
       
  1084             file.element.parentNode.removeChild(file.element);
       
  1085             file.element = null;
       
  1086         }
       
  1087 
       
  1088         if (currentFile == fileIndex)
       
  1089             loadFile(fileIndex, false);
       
  1090     }
       
  1091 }
       
  1092 
       
  1093 /**
       
  1094 * ParsedURL - this object chops up full URL into two parts: 
       
  1095  * 1) The domain: everything from http:// to the end of the domain name
       
  1096  * 2) The relative path: everything after the domain
       
  1097  *
       
  1098  * @param string url URL to be processed
       
  1099  */
       
  1100 function ParsedURL(url)
       
  1101 {
       
  1102     // Since we're getting the URL from the browser, we're safe to assume the URL is already well formatted
       
  1103     // and so there is no need for more sophisticated regular expression here
       
  1104     var url_parts = ((url.substring(0,4)).toLowerCase() == "file") ? url.match(/(file:[\/]{2,3}(\w|\.|-|_|\/)+)\/(.*)/) : url.match(/(http[s]?:\/\/(www)?\.?(\w|\.|-)+\w(:\d{1,5})?)\/?(.*)/);    
       
  1105     // the domain here is considered the whole http://www.example.org:8000 or file:///Users/user/folder/file.htm string for display purposes
       
  1106     this.domain = url_parts[1];
       
  1107     // the relative path is everything following the domain
       
  1108     this.relativePath = (url_parts[5] === undefined) ? "/" + url_parts[3] : "/" + url_parts[5];
       
  1109 }
       
  1110 
       
  1111 /**
       
  1112 * SiteBrowser - modifies the file tree via DOM as new files are being open
       
  1113  *
       
  1114  */
       
  1115 function SiteBrowser()
       
  1116 {
       
  1117     var fileBrowser = document.getElementById("filesBrowserSites");
       
  1118     
       
  1119     this.addURL = function add(url, fileIndex)
       
  1120     {
       
  1121         var parsedURL = new ParsedURL(url);
       
  1122         var divs = fileBrowser.getElementsByTagName("div");
       
  1123         
       
  1124         if (divs.length == 0) { 
       
  1125             addNewDomain(parsedURL, fileIndex);
       
  1126         } else {
       
  1127             var isNew = true;
       
  1128             for (var i = 0; i < divs.length; i++) {
       
  1129                 if (divs[i].id == parsedURL.domain) {
       
  1130                     var uls = divs[i].getElementsByTagName("ul");
       
  1131                     var ul = (uls.length > 0) ? uls[0] : document.createElement("ul");
       
  1132                     var li = document.createElement("li");
       
  1133                     
       
  1134                     li.id = fileIndex;
       
  1135                     li.addEventListener("click", fileBrowserMouseEvents, false);
       
  1136                     li.title = li.innerText = parsedURL.relativePath ? parsedURL.relativePath : "/";
       
  1137                     ul.appendChild(li);
       
  1138                     isNew = false;
       
  1139                     break;
       
  1140                 }
       
  1141             }
       
  1142             if (isNew) {
       
  1143                 addNewDomain(parsedURL, fileIndex);
       
  1144             }
       
  1145         }
       
  1146     }
       
  1147     
       
  1148     this.selectInitialFile = function sf()
       
  1149     {
       
  1150         if (currentFile == -1)
       
  1151             document.getElementById("1").className = "active";
       
  1152     }
       
  1153     
       
  1154     function addNewDomain(parsedURL, fileIndex)
       
  1155     {
       
  1156         var div = document.createElement("div");
       
  1157         var ul = document.createElement("ul");
       
  1158         var li = document.createElement("li");
       
  1159         
       
  1160         div.id = div.innerText = div.title = parsedURL.domain;
       
  1161         div.addEventListener("click", fileBrowserMouseEvents, false);
       
  1162         // Maybe we can add some roll-overs here...
       
  1163         //div.addEventListener("mouseover", fileBrowserMouseEvents, false);
       
  1164         //div.addEventListener("mouseout", fileBrowserMouseEvents, false);
       
  1165         li.id = fileIndex;
       
  1166         li.addEventListener("click", fileBrowserMouseEvents, false);
       
  1167         li.title = li.innerText = parsedURL.relativePath ? parsedURL.relativePath : "/";
       
  1168         ul.appendChild(li);
       
  1169         div.appendChild(ul);
       
  1170         fileBrowser.appendChild(div);        
       
  1171     }
       
  1172     
       
  1173     function removeFile(fileIndex)
       
  1174     {
       
  1175         var theFile = document.getElementById(fileIndex);
       
  1176         // If we are removing the last file from its site, go ahead and remove the whole site
       
  1177         if (theFile.parentNode.childNodes.length < 2) { 
       
  1178             var theSite = theFile.parentNode.parentNode;
       
  1179             theSite.removeChildren();
       
  1180             theSite.parentNode.removeChild(theSite);
       
  1181         }
       
  1182         else
       
  1183             theFile.parentNode.removeChild(theFile);
       
  1184     }
       
  1185 }
       
  1186 
       
  1187 function fileBrowserMouseEvents(event)
       
  1188 {
       
  1189     switch (event.type)
       
  1190     {
       
  1191         case "click":
       
  1192             // If we clicked on a site, collapse/expand it, if on a file, display it. Since we're only capturing this
       
  1193             // event from either a DIV or LI element, we don't have to worry about any ambiguity
       
  1194             (event.target.nodeName.toUpperCase() == "DIV") ? toggleCollapseSite(event) : fileClicked(event.target.id);
       
  1195             break;
       
  1196     }
       
  1197 }
       
  1198 
       
  1199 function fileClicked(fileId, shouldLoadFile)
       
  1200 {
       
  1201     if (shouldLoadFile === undefined) 
       
  1202         shouldLoadFile = true;
       
  1203     if (currentFile != -1) 
       
  1204         document.getElementById(currentFile).className = "passive";
       
  1205     document.getElementById(fileId).className = "active";
       
  1206     if (shouldLoadFile) 
       
  1207         loadFile(fileId, false);
       
  1208 }
       
  1209 
       
  1210 function toggleCollapseSite(event)
       
  1211 {
       
  1212     var thisSite = document.getElementById(event.target.id);
       
  1213     var siteFiles = thisSite.getElementsByTagName("ul");
       
  1214     
       
  1215     if (siteFiles[0].style.display == "block" || !siteFiles[0].style.display) {
       
  1216         siteFiles[0].style.display = "none";
       
  1217         thisSite.className = "collapsed"; 
       
  1218     } else {
       
  1219         siteFiles[0].style.display = "block";
       
  1220         thisSite.className = "expanded";
       
  1221     }
       
  1222 }
       
  1223 
       
  1224 function didParseScript(source, fileSource, url, sourceId, baseLineNumber)
       
  1225 {
       
  1226     var fileIndex = filesLookup[url];
       
  1227     var file = files[fileIndex];
       
  1228     var firstLoad = false;
       
  1229 
       
  1230     if (!fileIndex || !file) {
       
  1231         fileIndex = files.length + 1;
       
  1232         if (url.length)
       
  1233             filesLookup[url] = fileIndex;
       
  1234 
       
  1235         file = new Object();
       
  1236         file.scripts = new Array();
       
  1237         file.breakpoints = new Array();
       
  1238         file.functionNames = new Array();
       
  1239         file.source = (fileSource.length ? fileSource : source);
       
  1240         file.url = (url.length ? url : null);
       
  1241         file.loaded = false;
       
  1242 
       
  1243         files[fileIndex] = file;
       
  1244 
       
  1245         var filesSelect = document.getElementById("files");
       
  1246         var option = document.createElement("option");
       
  1247         files[fileIndex].menuOption = option;
       
  1248         option.value = fileIndex;
       
  1249         option.text = (file.url ? file.url : "(unknown script)");
       
  1250         filesSelect.appendChild(option);
       
  1251 
       
  1252         var siteBrowser = new SiteBrowser();
       
  1253         siteBrowser.addURL(file.url, fileIndex);
       
  1254         siteBrowser.selectInitialFile();        
       
  1255         
       
  1256         firstLoad = true;
       
  1257     }
       
  1258 
       
  1259     var sourceObj = new Object();
       
  1260     sourceObj.file = fileIndex;
       
  1261     sourceObj.baseLineNumber = baseLineNumber;
       
  1262     file.scripts.push(sourceId);
       
  1263     scripts[sourceId] = sourceObj;
       
  1264 
       
  1265     if (!firstLoad)
       
  1266         updateFileSource((fileSource.length ? fileSource : source), url, false);
       
  1267 
       
  1268     if (currentFile == -1)
       
  1269         loadFile(fileIndex, false);
       
  1270 }
       
  1271 
       
  1272 function jumpToLine(sourceId, line)
       
  1273 {
       
  1274     var script = scripts[sourceId];
       
  1275     if (line <= 0 || !script)
       
  1276         return;
       
  1277 
       
  1278     var file = files[script.file];
       
  1279     if (!file)
       
  1280         return;
       
  1281 
       
  1282     if (currentFile != script.file)
       
  1283         loadFile(script.file, true);
       
  1284     if (currentRow)
       
  1285         currentRow.removeStyleClass("current");
       
  1286     if (!file.element)
       
  1287         return;
       
  1288     if (line > file.element.firstChild.childNodes.length)
       
  1289         return;
       
  1290 
       
  1291     currentRow = file.element.firstChild.childNodes.item(line - 1);
       
  1292     if (!currentRow)
       
  1293         return;
       
  1294 
       
  1295     currentRow.addStyleClass("current");
       
  1296 
       
  1297     var sourcesDiv = document.getElementById("sources");
       
  1298     var sourcesDocument = document.getElementById("sources").contentDocument;
       
  1299     var parent = sourcesDocument.body;
       
  1300     var offset = totalOffsetTop(currentRow, parent);
       
  1301     if (offset < (parent.scrollTop + 20) || offset > (parent.scrollTop + sourcesDiv.clientHeight - 20))
       
  1302         parent.scrollTop = totalOffsetTop(currentRow, parent) - (sourcesDiv.clientHeight / 2) + 10;
       
  1303 }
       
  1304 
       
  1305 function willExecuteStatement(sourceId, line, fromLeavingFrame)
       
  1306 {
       
  1307     var script = scripts[sourceId];
       
  1308     if (line <= 0 || !script)
       
  1309         return;
       
  1310 
       
  1311     var file = files[script.file];
       
  1312     if (!file)
       
  1313         return;
       
  1314 
       
  1315     lastStatement = [sourceId, line];
       
  1316 
       
  1317     var breakpoint = file.breakpoints[line];
       
  1318 
       
  1319     var shouldBreak = false;
       
  1320 
       
  1321     if (breakpoint && breakpoint.enabled) {
       
  1322         switch(breakpoint.type) {
       
  1323             case 0:
       
  1324                 shouldBreak = (breakpoint.value == "break" || DebuggerDocument.evaluateScript(breakpoint.value, 0) == 1);
       
  1325                 if (shouldBreak)
       
  1326                     breakpoint.hitcount++;
       
  1327                 break;
       
  1328             case 1:
       
  1329                 var message = "Hit breakpoint on line " + line;
       
  1330                 if (breakpoint.value != "break")
       
  1331                     message = DebuggerDocument.evaluateScript(breakpoint.value, 0);
       
  1332                 if (consoleWindow)
       
  1333                     consoleWindow.appendMessage("", message);
       
  1334                 breakpoint.hitcount++;
       
  1335                 break;
       
  1336         }
       
  1337         var editor = breakpoint.editor;
       
  1338         var counter = null;
       
  1339         if (editor)
       
  1340             counter = breakpoint.editor.query('.//span[@class="hitCounter"]');
       
  1341         if (counter)
       
  1342             counter.innerText = breakpoint.hitcount;
       
  1343     }
       
  1344 
       
  1345     if (pauseOnNextStatement || shouldBreak || (steppingOver && !steppingStack)) {
       
  1346         pause();
       
  1347         pauseOnNextStatement = false;
       
  1348         pausedWhileLeavingFrame = fromLeavingFrame || false;
       
  1349     }
       
  1350 
       
  1351     if (isPaused) {
       
  1352         updateFunctionStack();
       
  1353         jumpToLine(sourceId, line);
       
  1354     }
       
  1355 }
       
  1356 
       
  1357 function didEnterCallFrame(sourceId, line)
       
  1358 {
       
  1359     if (steppingOver || steppingOut)
       
  1360         steppingStack++;
       
  1361 
       
  1362     if (lastStatement)
       
  1363         frameLineNumberStack.unshift(lastStatement);
       
  1364     willExecuteStatement(sourceId, line);
       
  1365 }
       
  1366 
       
  1367 function willLeaveCallFrame(sourceId, line)
       
  1368 {
       
  1369     if (line <= 0)
       
  1370         resume();
       
  1371     willExecuteStatement(sourceId, line, true);
       
  1372     frameLineNumberStack.shift();
       
  1373     if (!steppingStack)
       
  1374         steppingOver = false;
       
  1375     if (steppingOut && !steppingStack) {
       
  1376         steppingOut = false;
       
  1377         pauseOnNextStatement = true;
       
  1378     }
       
  1379     if ((steppingOver || steppingOut) && steppingStack >= 1)
       
  1380         steppingStack--;
       
  1381 }
       
  1382 
       
  1383 function exceptionWasRaised(sourceId, line)
       
  1384 {
       
  1385     pause();
       
  1386     updateFunctionStack();
       
  1387     jumpToLine(sourceId, line);
       
  1388 }
       
  1389 
       
  1390 function showConsoleWindow()
       
  1391 {
       
  1392     if (!consoleWindow)
       
  1393         consoleWindow = window.open("console.html", "console", "top=200, left=200, width=500, height=300, toolbar=yes, resizable=yes");
       
  1394     else
       
  1395         consoleWindow.focus();
       
  1396 }
       
  1397 
       
  1398 function closeFile(fileIndex)
       
  1399 {
       
  1400     if (fileIndex != -1) {
       
  1401         currentFile = -1;
       
  1402         var file = files[fileIndex];
       
  1403         var sourcesDocument = document.getElementById("sources").contentDocument;
       
  1404         // Clean up our file's content 
       
  1405         sourcesDocument.getElementById("file" + fileIndex).removeChildren();
       
  1406         // Remove the file from the open files pool
       
  1407         delete sourcesDocument.getElementById("file" + fileIndex);
       
  1408 
       
  1409         var filesSelect = document.getElementById("files");
       
  1410         // Select the next loaded file. If we're at the end of the list, loop back to beginning. 
       
  1411         var nextSelectedFile = (filesSelect.selectedIndex + 1 >= filesSelect.options.length) ? 0 : filesSelect.selectedIndex;
       
  1412         // Remove the file from file lists
       
  1413         filesSelect.options[filesSelect.selectedIndex] = null;        
       
  1414         SiteBrowser.removeFile(fileIndex);
       
  1415         delete files[fileIndex];
       
  1416         
       
  1417         // Clean up the function list
       
  1418         document.getElementById("functions").removeChildren();
       
  1419 
       
  1420         // Display appropriate place-holders, if we closed all files
       
  1421         if (filesSelect.options.length < 1) {
       
  1422             document.getElementById("filesPopupButtonContent").innerHTML = "<span class='placeholder'>&lt;No files loaded&gt;</span>";
       
  1423             document.getElementById("functionNamesPopup").style.display = "none";
       
  1424         }
       
  1425         else 
       
  1426             switchFile(nextSelectedFile);
       
  1427     }    
       
  1428 }
       
  1429 
       
  1430 function closeCurrentFile()
       
  1431 {
       
  1432     closeFile(currentFile);
       
  1433 }