diff -r 000000000000 -r dd21522fd290 webengine/osswebengine/WebKitTools/Drosera/debugger.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/webengine/osswebengine/WebKitTools/Drosera/debugger.js Mon Mar 30 12:54:55 2009 +0300 @@ -0,0 +1,1433 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 David Smith (catfish.man@gmail.com) + * Copyright (C) 2007 Vladimir Olexa (vladimir.olexa@gmail.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +var files = new Array(); +var filesLookup = new Object(); +var scripts = new Array(); +var currentFile = -1; +var currentRow = null; +var currentStack = null; +var currentCallFrame = null; +var lastStatement = null; +var frameLineNumberStack = new Array(); +var previousFiles = new Array(); +var nextFiles = new Array(); +var isResizingColumn = false; +var draggingBreakpoint = null; +var steppingOut = false; +var steppingOver = false; +var steppingStack = 0; +var pauseOnNextStatement = false; +var pausedWhileLeavingFrame = false; +var consoleWindow = null; +var breakpointEditorHTML = DebuggerDocument.breakpointEditorHTML(); +var pendingAction = null; +var isPaused = false; + +ScriptCallFrame = function (functionName, index, row) +{ + this.functionName = functionName; + this.index = index; + this.row = row; + this.localVariableNames = null; +} + +ScriptCallFrame.prototype.valueForScopeVariable = function (name) +{ + return DebuggerDocument.valueForScopeVariableNamed(name, this.index); +} + +ScriptCallFrame.prototype.loadVariables = function () +{ + if (!this.localVariableNames) + this.localVariableNames = DebuggerDocument.localScopeVariableNamesForCallFrame(this.index); + + var variablesTable = document.getElementById("variablesTable"); + variablesTable.innerHTML = ""; + + if (!this.localVariableNames) + return; + + for(var i = 0; i < this.localVariableNames.length; i++) { + var tr = document.createElement("tr"); + var td = document.createElement("td"); + td.innerText = this.localVariableNames[i]; + td.className = "variable"; + tr.appendChild(td); + + td = document.createElement("td"); + td.innerText = this.valueForScopeVariable(this.localVariableNames[i]); + tr.appendChild(td); + tr.addEventListener("click", selectVariable, true); + + variablesTable.appendChild(tr); + } +} + +function sleep(numberMillis) +{ + var now = new Date(); + var exitTime = now.getTime() + numberMillis; + while (true) { + now = new Date(); + if (now.getTime() > exitTime) + return; + } +} + +function headerMouseDown(element) +{ + if (!isResizingColumn) + element.style.background = "url(glossyHeaderPressed.png) repeat-x"; +} + +function headerMouseUp(element) +{ + element.style.background = "url(glossyHeader.png) repeat-x"; +} + +function headerMouseOut(element) +{ + element.style.background = "url(glossyHeader.png) repeat-x"; +} + +function filesDividerDragStart(event) +{ + dividerDragStart(document.getElementById("filesDivider"), filesDividerDrag, filesDividerDragEnd, event, "col-resize"); +} + +function filesDividerDragEnd(event) +{ + dividerDragEnd(document.getElementById("filesDivider"), filesDividerDrag, filesDividerDragEnd, event); +} + +function filesDividerDrag(event) +{ + var element = document.getElementById("filesDivider"); + if (document.getElementById("filesDivider").dragging == true) { + var masterMain = document.getElementById("masterMain"); + var main = document.getElementById("main"); + var fileBrowser = document.getElementById("fileBrowser"); + var x = event.clientX + window.scrollX; + var delta = element.dragLastX - x; + var newWidth = constrainedWidthFromElement(fileBrowser.clientWidth - delta, masterMain, 0.1, 0.9); + if ((fileBrowser.clientWidth - delta) == newWidth) // the width wasn't constrained + element.dragLastX = x; + fileBrowser.style.width = newWidth + "px"; + main.style.left = newWidth + "px"; + event.preventDefault(); + } +} + +function dividerDragStart(element, dividerDrag, dividerDragEnd, event, cursor) +{ + element.dragging = true; + element.dragLastY = event.clientY + window.scrollY; + element.dragLastX = event.clientX + window.scrollX; + document.addEventListener("mousemove", dividerDrag, true); + document.addEventListener("mouseup", dividerDragEnd, true); + document.body.style.cursor = cursor; + event.preventDefault(); +} + +function dividerDragEnd(element, dividerDrag, dividerDragEnd, event) +{ + element.dragging = false; + document.removeEventListener("mousemove", dividerDrag, true); + document.removeEventListener("mouseup", dividerDragEnd, true); + document.body.style.removeProperty("cursor"); +} + +function dividerDrag(event) +{ + var element = document.getElementById("divider"); + if (document.getElementById("divider").dragging == true) { + var main = document.getElementById("main"); + var top = document.getElementById("info"); + var bottom = document.getElementById("body"); + var y = event.clientY + window.scrollY; + var delta = element.dragLastY - y; + var newHeight = constrainedHeightFromElement(top.clientHeight - delta, main); + if ((top.clientHeight - delta) == newHeight) // the height wasn't constrained + element.dragLastY = y; + top.style.height = newHeight + "px"; + bottom.style.top = newHeight + "px"; + event.preventDefault(); + } +} + +function sourceDividerDragStart(event) +{ + dividerDragStart(document.getElementById("divider"), dividerDrag, sourceDividerDragEnd, event, "row-resize"); +} + +function sourceDividerDragEnd(event) +{ + dividerDragEnd(document.getElementById("divider"), dividerDrag, sourceDividerDragEnd, event); +} + +function infoDividerDragStart(event) +{ + dividerDragStart(document.getElementById("infoDivider"), infoDividerDrag, infoDividerDragEnd, event, "col-resize"); +} + +function infoDividerDragEnd(event) +{ + dividerDragEnd(document.getElementById("infoDivider"), infoDividerDrag, infoDividerDragEnd, event); +} + +function infoDividerDrag(event) +{ + var element = document.getElementById("infoDivider"); + if (document.getElementById("infoDivider").dragging == true) { + var main = document.getElementById("main"); + var leftPane = document.getElementById("leftPane"); + var rightPane = document.getElementById("rightPane"); + var x = event.clientX + window.scrollX; + var delta = element.dragLastX - x; + var newWidth = constrainedWidthFromElement(leftPane.clientWidth - delta, main); + if ((leftPane.clientWidth - delta) == newWidth) // the width wasn't constrained + element.dragLastX = x; + leftPane.style.width = newWidth + "px"; + rightPane.style.left = newWidth + "px"; + event.preventDefault(); + } +} + +function columnResizerDragStart(event) +{ + isResizingColumn = true; + dividerDragStart(document.getElementById("variableColumnResizer"), columnResizerDrag, columnResizerDragEnd, event, "col-resize"); +} + +function columnResizerDragEnd(event) +{ + isResizingColumn = false; + dividerDragEnd(document.getElementById("variableColumnResizer"), columnResizerDrag, columnResizerDragEnd, event); +} + +function columnResizerDrag(event) +{ + var element = document.getElementById("variableColumnResizer"); + if (element.dragging == true) { + var main = document.getElementById("rightPane"); + var variableColumn = document.getElementById("variable"); + var rules = document.defaultView.getMatchedCSSRules(variableColumn, ""); + for (var i = 0; i < rules.length; i++) { + if (rules[i].selectorText == ".variable") { + var columnRule = rules[i]; + break; + } + } + + var x = event.clientX + window.scrollX; + var delta = element.dragLastX - x; + var newWidth = constrainedWidthFromElement(variableColumn.clientWidth - delta, main); + if ((variableColumn.clientWidth - delta) == newWidth) // the width wasn't constrained + element.dragLastX = x; + columnRule.style.width = newWidth + "px"; + element.style.left = newWidth + "px"; + event.preventDefault(); + } +} + +function constrainedWidthFromElement(width, element, constrainLeft, constrainRight) +{ + if (constrainLeft === undefined) constrainLeft = 0.25; + if (constrainRight === undefined) constrainRight = 0.75; + + if (width < element.clientWidth * constrainLeft) + width = element.clientWidth * constrainLeft; + else if (width > element.clientWidth * constrainRight) + width = element.clientWidth * constrainRight; + return width; +} + +function constrainedHeightFromElement(height, element) +{ + if (height < element.clientHeight * 0.25) + height = element.clientHeight * 0.25; + else if (height > element.clientHeight * 0.75) + height = element.clientHeight * 0.75; + return height; +} + +function loaded() +{ + document.getElementById("divider").addEventListener("mousedown", sourceDividerDragStart, false); + document.getElementById("infoDivider").addEventListener("mousedown", infoDividerDragStart, false); + document.getElementById("filesDivider").addEventListener("mousedown", filesDividerDragStart, false); + document.getElementById("variableColumnResizer").addEventListener("mousedown", columnResizerDragStart, false); +} + +function pause() +{ + DebuggerDocument.pause(); + isPaused = true; +} + +function resume() +{ + if (currentRow) { + currentRow.removeStyleClass("current"); + currentRow = null; + } + + var stackframeTable = document.getElementById("stackframeTable"); + stackframeTable.innerHTML = ""; // clear the content + var variablesTable = document.getElementById("variablesTable"); + variablesTable.innerHTML = ""; // clear the content + currentStack = null; + currentCallFrame = null; + + pauseOnNextStatement = false; + pausedWhileLeavingFrame = false; + steppingOut = false; + steppingOver = false; + steppingStack = 0; + + DebuggerDocument.resume(); + isPaused = false; +} + +function stepInto() +{ + pauseOnNextStatement = false; + steppingOut = false; + steppingOver = false; + steppingStack = 0; + DebuggerDocument.stepInto(); +} + +function stepOver() +{ + pauseOnNextStatement = false; + steppingOver = true; + steppingStack = 0; + DebuggerDocument.resume(); +} + +function stepOut() +{ + pauseOnNextStatement = pausedWhileLeavingFrame; + steppingOver = false; + steppingStack = 0; + steppingOut = true; + DebuggerDocument.resume(); +} + +Element.prototype.removeStyleClass = function(className) +{ + if (this.hasStyleClass(className)) + this.className = this.className.replace(className, ""); +} + +Element.prototype.addStyleClass = function(className) +{ + if (!this.hasStyleClass(className)) + this.className += (this.className.length ? " " + className : className); +} + +Element.prototype.hasStyleClass = function(className) +{ + return this.className.indexOf(className) != -1; +} + +Element.prototype.firstParentWithClass = function(className) +{ + var node = this.parentNode; + while(!node.hasStyleClass(className)) { + if (node == document) + return null; + node = node.parentNode; + } + return node; +} + +Element.prototype.query = function(query) +{ + return document.evaluate(query, this, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; +} + +Element.prototype.removeChildren = function() +{ + while (this.firstChild) + this.removeChild(this.firstChild); +} + +function breakpointAction(event) +{ + var file = files[currentFile]; + var lineNum = event.target.title; + + if (!file.breakpoints[lineNum]) + file.breakpoints[lineNum] = new BreakPoint(event.target.parentNode, file, lineNum); + else + toggleBreakpointOnLine(lineNum); +} + +BreakPoint = function(row, file, line) +{ + this.row = row; + this.file = file; + this.line = line; + row.addStyleClass("breakpoint"); + row.removeStyleClass("disabled"); + this.value = "break"; + this.enabled = true; + this.editor = null; + this.type = 0; + this.hitcount = 0; +} + +function toggleBreakpointEditorOnLine(lineNum) +{ + if (pendingAction) { + clearTimeout(pendingAction); + pendingAction = null; + } + var file = files[currentFile]; + bp = file.breakpoints[lineNum]; + if (bp) { + var editor = bp.editor; + if (!editor) { + var sourcesDocument = document.getElementById("sources").contentDocument; + editor = sourcesDocument.createElement("div"); + editor.className = "editor"; + editor.id = lineNum; + editor.innerHTML = breakpointEditorHTML; + + bp.row.childNodes[1].appendChild(editor); + + bp.editor = editor; + file.breakpoints[lineNum] = bp; + + editor.query('.//input[@class="enable"]').checked = bp.enabled; + + editor.query('.//select[@class="editorDropdown"]').selectedIndex = bp.type; + updateBreakpointTypeOnLine(lineNum); + + editor.query('.//span[@class="hitCounter"]').innerText = bp.hitcount; + + setConditionFieldText(bp); + } else { + saveBreakpointOnLine(lineNum); + bp.row.childNodes[1].removeChild(editor); + bp.editor = null; + } + } +} + +function updateBreakpointTypeOnLine(line) +{ + var breakpoint = files[currentFile].breakpoints[line]; + var editor = breakpoint.editor; + var label = editor.query('.//label[@class="conditionLabel"]'); + var dropdown = editor.query('.//select[@class="editorDropdown"]'); + breakpoint.type = dropdown.selectedIndex; + switch(breakpoint.type) { + case 0: + label.innerText = "Condition:"; + break; + case 1: + label.innerText = "Expression:"; + break; + } +} + +function setConditionFieldText(breakpoint) +{ + var conditionField = breakpoint.editor.query('.//div[@class="condition"]'); + + var functionBody = breakpoint.value; + if (!functionBody || functionBody == "break") + functionBody = ""; + else { + var startIndex = functionBody.indexOf("return((") + 8; + var endIndex = null; + if (startIndex != 7) //-1 + 8, yes, that's lame + endIndex = functionBody.lastIndexOf("))"); + else { + startIndex = functionBody.indexOf("{") + 1; + endIndex = functionBody.lastIndexOf("}"); + } + functionBody = functionBody.substring(startIndex, endIndex); + } + conditionField.innerText = functionBody; + conditionField.addEventListener("keyup", new Function("saveBreakpointOnLine(" + breakpoint.line + ");"), false); + conditionField.focus(); +} + +function saveBreakpointOnLine(lineNum) +{ + var file = files[currentFile]; + var breakpoint = file.breakpoints[lineNum]; + row = file.element.firstChild.childNodes.item(lineNum - 1); + var editor = breakpoint.editor; + var body = editor.query('.//div[@class="condition"]').innerText; + var actionIndex = editor.query('.//select[@class="editorDropdown"]').selectedIndex; + if (body.length == 0) + breakpoint.value = "break"; + else if (body.indexOf("return") != -1) + breakpoint.value = "__drosera_breakpoint_conditional_func = function() {" + body + "}; __drosera_breakpoint_conditional_func();"; + else + breakpoint.value = "__drosera_breakpoint_conditional_func = function() { return((" + body + ")); }; __drosera_breakpoint_conditional_func();"; +} + +function toggleBreakpointOnLine(lineNum) +{ + var breakpoint = files[currentFile].breakpoints[lineNum]; + pendingAction = null; + if (breakpoint.enabled) + breakpoint.row.addStyleClass("disabled"); + else + breakpoint.row.removeStyleClass("disabled"); + + var hack = breakpoint.row.offsetTop; // force a relayout if needed. + + breakpoint.enabled = !breakpoint.enabled; + var editor = breakpoint.editor; + if (editor) { + editor.query('.//input[@class="enable"]').checked = breakpoint.enabled; + setConditionFieldText(breakpoint, lineNum); + } +} + +function moveBreakPoint(event) +{ + if (event.target.parentNode.hasStyleClass("breakpoint")) { + draggingBreakpoint = event.target; + draggingBreakpoint.started = false; + draggingBreakpoint.dragLastY = event.clientY + window.scrollY; + draggingBreakpoint.dragLastX = event.clientX + window.scrollX; + var sourcesDocument = document.getElementById("sources").contentDocument; + sourcesDocument.addEventListener("mousemove", breakpointDrag, true); + sourcesDocument.addEventListener("mouseup", breakpointDragEnd, true); + sourcesDocument.body.style.cursor = "default"; + } +} + +function breakpointDrag(event) +{ + var sourcesDocument = document.getElementById("sources").contentDocument; + if (!draggingBreakpoint) { + sourcesDocument.removeEventListener("mousemove", breakpointDrag, true); + sourcesDocument.removeEventListener("mouseup", breakpointDragEnd, true); + sourcesDocument.body.style.removeProperty("cursor"); + return; + } + + var x = event.clientX + window.scrollX; + var y = event.clientY + window.scrollY; + var deltaX = draggingBreakpoint.dragLastX - x; + var deltaY = draggingBreakpoint.dragLastY - y; + if (draggingBreakpoint.started || deltaX > 4 || deltaY > 4 || deltaX < -4 || deltaY < -4) { + + if (!draggingBreakpoint.started) { + var lineNum = draggingBreakpoint.title; + var file = files[currentFile]; + var breakpoint = file.breakpoints[lineNum]; + draggingBreakpoint.breakpoint = breakpoint; + breakpoint.row.removeStyleClass("breakpoint"); + breakpoint.row.removeStyleClass("disabled"); + + var editor = breakpoint.editor; + if (editor) + toggleBreakpointEditorOnLine(lineNum); + + draggingBreakpoint.started = true; + + file.breakpoints[lineNum] = null; + + var dragImage = sourcesDocument.createElement("img"); + if (draggingBreakpoint.breakpoint.enabled) + dragImage.src = "breakPoint.tif"; + else + dragImage.src = "breakPointDisabled.tif"; + + dragImage.id = "breakpointDrag"; + dragImage.style.top = y - 8 + "px"; + dragImage.style.left = x - 12 + "px"; + sourcesDocument.body.appendChild(dragImage); + } else { + var dragImage = sourcesDocument.getElementById("breakpointDrag"); + if (!dragImage) { + sourcesDocument.removeEventListener("mousemove", breakpointDrag, true); + sourcesDocument.removeEventListener("mouseup", breakpointDragEnd, true); + sourcesDocument.body.style.removeProperty("cursor"); + return; + } + + dragImage.style.top = y - 8 + "px"; + dragImage.style.left = x - 12 + "px"; + if (x > 40) + dragImage.style.visibility = "hidden"; + else + dragImage.style.removeProperty("visibility"); + } + + draggingBreakpoint.dragLastX = x; + draggingBreakpoint.dragLastY = y; + } +} + +function breakpointDragEnd(event) +{ + var sourcesDocument = document.getElementById("sources").contentDocument; + sourcesDocument.removeEventListener("mousemove", breakpointDrag, true); + sourcesDocument.removeEventListener("mouseup", breakpointDragEnd, true); + sourcesDocument.body.style.removeProperty("cursor"); + + var dragImage = sourcesDocument.getElementById("breakpointDrag"); + if (!dragImage) + return; + + dragImage.parentNode.removeChild(dragImage); + + var x = event.clientX + window.scrollX; + if (x > 40 || !draggingBreakpoint) + return; + + var y = event.clientY + window.scrollY; + var rowHeight = draggingBreakpoint.parentNode.offsetHeight; + var row = Math.ceil(y / rowHeight); + if (row <= 0) + row = 1; + + var file = files[currentFile]; + var table = file.element.firstChild; + if (row > table.childNodes.length) + return; + + var tr = table.childNodes.item(row - 1); + if (!tr) + return; + + var breakpoint = draggingBreakpoint.breakpoint; + breakpoint.row = tr; + + // leave the editor there if it exists... we'll want to update it to the new values + breakpoint.editor = file.breakpoints[row].editor; + + file.breakpoints[row] = breakpoint; + + if (breakpoint.editor) { + breakpoint.editor.id = row; + updateBreakpointTypeOnLine(row); + setConditionFieldText(breakpoint); + } + + if (!breakpoint.enabled) + tr.addStyleClass("disabled"); + + tr.addStyleClass("breakpoint"); + + draggingBreakpoint = null; +} + +function totalOffsetTop(element, stop) +{ + var currentTop = 0; + while (element.offsetParent) { + currentTop += element.offsetTop + element = element.offsetParent; + if (element == stop) + break; + } + return currentTop; +} + +function switchFile(fileIndex) +{ + var filesSelect = document.getElementById("files"); + + if (fileIndex === undefined) + fileIndex = filesSelect.selectedIndex; + + fileClicked(filesSelect.options[fileIndex].value, false); + loadFile(filesSelect.options[fileIndex].value, true); +} + +function syntaxHighlight(code, file) +{ + 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 }; + + function echoChar(c) { + if (c == '<') + result += '<'; + else if (c == '>') + result += '>'; + else if (c == '&') + result += '&'; + else if (c == '\t') + result += ' '; + else + result += c; + } + + function isDigit(number) { + var string = "1234567890"; + if (string.indexOf(number) != -1) + return true; + return false; + } + + function isHex(hex) { + var string = "1234567890abcdefABCDEF"; + if (string.indexOf(hex) != -1) + return true; + return false; + } + + function isLetter(letter) { + var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if (string.indexOf(letter) != -1) + return true; + return false; + } + + var result = ""; + var cPrev = ""; + var c = ""; + var cNext = ""; + for (var i = 0; i < code.length; i++) { + cPrev = c; + c = code.charAt(i); + cNext = code.charAt(i + 1); + + if (c == "/" && cNext == "*") { + result += ""; + echoChar(c); + echoChar(cNext); + for (i += 2; i < code.length; i++) { + c = code.charAt(i); + if (c == "\n") + result += ""; + echoChar(c); + if (c == "\n") + result += ""; + if (cPrev == "*" && c == "/") + break; + cPrev = c; + } + result += ""; + continue; + } else if (c == "/" && cNext == "/") { + result += ""; + echoChar(c); + echoChar(cNext); + for (i += 2; i < code.length; i++) { + c = code.charAt(i); + if (c == "\n") + break; + echoChar(c); + } + result += ""; + echoChar(c); + continue; + } else if (c == "\"" || c == "'") { + var instringtype = c; + var stringstart = i; + result += ""; + echoChar(c); + for (i += 1; i < code.length; i++) { + c = code.charAt(i); + if (stringstart < (i - 1) && cPrev == instringtype && code.charAt(i - 2) != "\\") + break; + echoChar(c); + cPrev = c; + } + result += ""; + echoChar(c); + continue; + } else if (c == "0" && cNext == "x" && (i == 0 || (!isLetter(cPrev) && !isDigit(cPrev)))) { + result += ""; + echoChar(c); + echoChar(cNext); + for (i += 2; i < code.length; i++) { + c = code.charAt(i); + if (!isHex(c)) + break; + echoChar(c); + } + result += ""; + echoChar(c); + continue; + } else if ((isDigit(c) || ((c == "-" || c == ".") && isDigit(cNext))) && (i == 0 || (!isLetter(cPrev) && !isDigit(cPrev)))) { + result += ""; + echoChar(c); + for (i += 1; i < code.length; i++) { + c = code.charAt(i); + if (!isDigit(c) && c != ".") + break; + echoChar(c); + } + result += ""; + echoChar(c); + continue; + } else if (isLetter(c) && (i == 0 || !isLetter(cPrev))) { + var keyword = c; + var cj = ""; + for (var j = i + 1; j < i + 12 && j < code.length; j++) { + cj = code.charAt(j); + if (!isLetter(cj)) + break; + keyword += cj; + } + + if (keywords[keyword]) { + var functionName = ""; + var functionIsAnonymous = false; + if (keyword == "function") { + var functionKeywordOffset = 8; + for (var j = i + functionKeywordOffset; j < code.length; j++) { + cj = code.charAt(j); + if (cj == " ") + continue; + if (cj == "(") + break; + functionName += cj; + } + + if (!functionName.length) { + functionIsAnonymous = true; + var functionAssignmentFound = false; + var functionNameStart = -1; + var functionNameEnd = -1; + + for (var j = i - 1; j >= 0; j--) { + cj = code.charAt(j); + if (cj == ":" || cj == "=") { + functionAssignmentFound = true; + continue; + } + + var curCharIsSpace = (cj == " " || cj == "\t" || cj == "\n"); + if (functionAssignmentFound && functionNameEnd == -1 && !curCharIsSpace) { + functionNameEnd = j + 1; + } else if (!functionAssignmentFound && !curCharIsSpace) { + break; + } else if (functionNameEnd != -1 && curCharIsSpace) { + functionNameStart = j; + break; + } + } + + if (functionNameStart != -1 && functionNameEnd != -1) + functionName = code.substring(functionNameStart, functionNameEnd); + } + + if (!functionName.length) + functionName = "function"; + + file.functionNames.push(functionName); + } + + var fileIndex = filesLookup[file.url]; + + if (keyword == "function" && functionIsAnonymous) + result += "" + keyword + ""; + else + result += "" + keyword + ""; + + if (functionName.length && !functionIsAnonymous) { + result += " " + functionName + ""; + i += keyword.length + functionName.length; + } else + i += keyword.length - 1; + + continue; + } + } + + echoChar(c); + } + + return result; +} + +function navFilePrevious(element) +{ + if (element.disabled) + return; + var lastFile = previousFiles.pop(); + if (currentFile != -1) + nextFiles.unshift(currentFile); + loadFile(lastFile, false); +} + +function navFileNext(element) +{ + if (element.disabled) + return; + var lastFile = nextFiles.shift(); + if (currentFile != -1) + previousFiles.push(currentFile); + loadFile(lastFile, false); +} + +function updateFunctionStack() +{ + var stackframeTable = document.getElementById("stackframeTable"); + stackframeTable.innerHTML = ""; // clear the content + + currentStack = new Array(); + var stack = DebuggerDocument.currentFunctionStack(); + for(var i = 0; i < stack.length; i++) { + var tr = document.createElement("tr"); + var td = document.createElement("td"); + td.className = "stackNumber"; + td.innerText = i; + tr.appendChild(td); + + td = document.createElement("td"); + td.innerText = stack[i]; + tr.appendChild(td); + tr.addEventListener("click", selectStackFrame, true); + + stackframeTable.appendChild(tr); + + var frame = new ScriptCallFrame(stack[i], i, tr); + tr.callFrame = frame; + currentStack.push(frame); + + if (i == 0) { + tr.addStyleClass("current"); + frame.loadVariables(); + currentCallFrame = frame; + } + } +} + +function selectStackFrame(event) +{ + var stackframeTable = document.getElementById("stackframeTable"); + var rows = stackframeTable.childNodes; + for (var i = 0; i < rows.length; i++) + rows[i].removeStyleClass("current"); + this.addStyleClass("current"); + this.callFrame.loadVariables(); + currentCallFrame = this.callFrame; + + if (frameLineNumberInfo = frameLineNumberStack[this.callFrame.index - 1]) + jumpToLine(frameLineNumberInfo[0], frameLineNumberInfo[1]); + else if (this.callFrame.index == 0) + jumpToLine(lastStatement[0], lastStatement[1]); +} + +function selectVariable(event) +{ + var variablesTable = document.getElementById("variablesTable"); + var rows = variablesTable.childNodes; + for (var i = 0; i < rows.length; i++) + rows[i].removeStyleClass("current"); + this.addStyleClass("current"); +} + +function switchFunction(index, shouldResetPopup) +{ + if (shouldResetPopup === undefined) shouldResetPopup = false; + var sourcesFrame = window.frames['sourcesFrame']; + + if (shouldResetPopup || index == 0) { + document.getElementById("functionPopupButtonContent").innerHTML = '<No selected symbol>'; + return; + } + + var functionSelect = document.getElementById("functions"); + var selectedFunction = functionSelect.childNodes[index]; + var selection = sourcesFrame.getSelection(); + var currentFunction = selectedFunction.value; + var currentFunctionElement = sourcesFrame.contentDocument.getElementById(currentFunction); + + functionSelect.blur(); + sourcesFrame.focus(); + selection.setBaseAndExtent(currentFunctionElement, 0, currentFunctionElement, 1); + sourcesFrame.location.hash = "#function-" + selectedFunction.value; + document.getElementById("functionPopupButtonContent").innerText = selectedFunction.innerText; +} + +function loadFile(fileIndex, manageNavLists) +{ + var file = files[fileIndex]; + if (!file) + return; + + if (currentFile != -1 && files[currentFile] && files[currentFile].element) + files[currentFile].element.style.display = "none"; + + if (!file.loaded) { + var sourcesDocument = document.getElementById("sources").contentDocument; + var sourcesDiv = sourcesDocument.body; + var sourceDiv = sourcesDocument.createElement("div"); + sourceDiv.id = "file" + fileIndex; + sourcesDiv.appendChild(sourceDiv); + file.element = sourceDiv; + + var table = sourcesDocument.createElement("table"); + sourceDiv.appendChild(table); + + var normalizedSource = file.source.replace(/\r\n|\r/g, "\n"); // normalize line endings + var lines = syntaxHighlight(normalizedSource, file).split("\n"); + for( var i = 0; i < lines.length; i++ ) { + var tr = sourcesDocument.createElement("tr"); + var td = sourcesDocument.createElement("td"); + td.className = "gutter"; + td.title = (i + 1); + td.addEventListener("click", breakpointAction, true); + td.addEventListener("dblclick", function(event) { toggleBreakpointEditorOnLine(event.target.title); }, true); + td.addEventListener("mousedown", moveBreakPoint, true); + tr.appendChild(td); + + td = sourcesDocument.createElement("td"); + td.className = "source"; + td.innerHTML = (lines[i].length ? lines[i] : " "); + tr.appendChild(td); + table.appendChild(tr); + } + + file.loaded = true; + } + + file.element.style.removeProperty("display"); + + document.getElementById("filesPopupButtonContent").innerText = (file.url ? file.url : "(unknown script)"); + + var filesSelect = document.getElementById("files"); + for (var i = 0; i < filesSelect.childNodes.length; i++) { + if (filesSelect.childNodes[i].value == fileIndex) { + filesSelect.selectedIndex = i; + break; + } + } + + // Populate the function names pop-up + if (file.functionNames.length > 0) { + var functionSelect = document.getElementById("functions"); + var functionOption = document.createElement("option"); + + document.getElementById("functionNamesPopup").style.display = "inline"; + switchFunction(0, true); + + functionSelect.removeChildren(); + functionOption.value = null; + functionOption.text = ""; + functionSelect.appendChild(functionOption); + + for (var i = 0; i < file.functionNames.length; i++) { + functionOption = document.createElement("option"); + functionOption.value = fileIndex + "-" + (i+1); + functionOption.text = file.functionNames[i] + "()"; + functionSelect.appendChild(functionOption); + } + } else + document.getElementById("functionNamesPopup").style.display = "none"; + + if (manageNavLists) { + nextFiles = new Array(); + if (currentFile != -1) + previousFiles.push(currentFile); + } + + document.getElementById("navFileLeftButton").disabled = (previousFiles.length == 0); + document.getElementById("navFileRightButton").disabled = (nextFiles.length == 0); + + //Remember and recall scroll position for current file and file we just loaded + var frameBody = document.getElementById("sources").contentDocument.body; + if (currentFile != -1) + files[currentFile].scrollPosition = frameBody.scrollTop; + frameBody.scrollTop = file.scrollPosition; + frameBody.scrollLeft = 0; + + currentFile = fileIndex; +} + +function updateFileSource(source, url, force) +{ + var fileIndex = filesLookup[url]; + if (!fileIndex || !source.length) + return; + + var file = files[fileIndex]; + if (force || file.source.length != source.length || file.source != source) { + file.source = source; + file.loaded = false; + + if (file.element) { + file.element.parentNode.removeChild(file.element); + file.element = null; + } + + if (currentFile == fileIndex) + loadFile(fileIndex, false); + } +} + +/** +* ParsedURL - this object chops up full URL into two parts: + * 1) The domain: everything from http:// to the end of the domain name + * 2) The relative path: everything after the domain + * + * @param string url URL to be processed + */ +function ParsedURL(url) +{ + // Since we're getting the URL from the browser, we're safe to assume the URL is already well formatted + // and so there is no need for more sophisticated regular expression here + 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})?)\/?(.*)/); + // the domain here is considered the whole http://www.example.org:8000 or file:///Users/user/folder/file.htm string for display purposes + this.domain = url_parts[1]; + // the relative path is everything following the domain + this.relativePath = (url_parts[5] === undefined) ? "/" + url_parts[3] : "/" + url_parts[5]; +} + +/** +* SiteBrowser - modifies the file tree via DOM as new files are being open + * + */ +function SiteBrowser() +{ + var fileBrowser = document.getElementById("filesBrowserSites"); + + this.addURL = function add(url, fileIndex) + { + var parsedURL = new ParsedURL(url); + var divs = fileBrowser.getElementsByTagName("div"); + + if (divs.length == 0) { + addNewDomain(parsedURL, fileIndex); + } else { + var isNew = true; + for (var i = 0; i < divs.length; i++) { + if (divs[i].id == parsedURL.domain) { + var uls = divs[i].getElementsByTagName("ul"); + var ul = (uls.length > 0) ? uls[0] : document.createElement("ul"); + var li = document.createElement("li"); + + li.id = fileIndex; + li.addEventListener("click", fileBrowserMouseEvents, false); + li.title = li.innerText = parsedURL.relativePath ? parsedURL.relativePath : "/"; + ul.appendChild(li); + isNew = false; + break; + } + } + if (isNew) { + addNewDomain(parsedURL, fileIndex); + } + } + } + + this.selectInitialFile = function sf() + { + if (currentFile == -1) + document.getElementById("1").className = "active"; + } + + function addNewDomain(parsedURL, fileIndex) + { + var div = document.createElement("div"); + var ul = document.createElement("ul"); + var li = document.createElement("li"); + + div.id = div.innerText = div.title = parsedURL.domain; + div.addEventListener("click", fileBrowserMouseEvents, false); + // Maybe we can add some roll-overs here... + //div.addEventListener("mouseover", fileBrowserMouseEvents, false); + //div.addEventListener("mouseout", fileBrowserMouseEvents, false); + li.id = fileIndex; + li.addEventListener("click", fileBrowserMouseEvents, false); + li.title = li.innerText = parsedURL.relativePath ? parsedURL.relativePath : "/"; + ul.appendChild(li); + div.appendChild(ul); + fileBrowser.appendChild(div); + } + + function removeFile(fileIndex) + { + var theFile = document.getElementById(fileIndex); + // If we are removing the last file from its site, go ahead and remove the whole site + if (theFile.parentNode.childNodes.length < 2) { + var theSite = theFile.parentNode.parentNode; + theSite.removeChildren(); + theSite.parentNode.removeChild(theSite); + } + else + theFile.parentNode.removeChild(theFile); + } +} + +function fileBrowserMouseEvents(event) +{ + switch (event.type) + { + case "click": + // If we clicked on a site, collapse/expand it, if on a file, display it. Since we're only capturing this + // event from either a DIV or LI element, we don't have to worry about any ambiguity + (event.target.nodeName.toUpperCase() == "DIV") ? toggleCollapseSite(event) : fileClicked(event.target.id); + break; + } +} + +function fileClicked(fileId, shouldLoadFile) +{ + if (shouldLoadFile === undefined) + shouldLoadFile = true; + if (currentFile != -1) + document.getElementById(currentFile).className = "passive"; + document.getElementById(fileId).className = "active"; + if (shouldLoadFile) + loadFile(fileId, false); +} + +function toggleCollapseSite(event) +{ + var thisSite = document.getElementById(event.target.id); + var siteFiles = thisSite.getElementsByTagName("ul"); + + if (siteFiles[0].style.display == "block" || !siteFiles[0].style.display) { + siteFiles[0].style.display = "none"; + thisSite.className = "collapsed"; + } else { + siteFiles[0].style.display = "block"; + thisSite.className = "expanded"; + } +} + +function didParseScript(source, fileSource, url, sourceId, baseLineNumber) +{ + var fileIndex = filesLookup[url]; + var file = files[fileIndex]; + var firstLoad = false; + + if (!fileIndex || !file) { + fileIndex = files.length + 1; + if (url.length) + filesLookup[url] = fileIndex; + + file = new Object(); + file.scripts = new Array(); + file.breakpoints = new Array(); + file.functionNames = new Array(); + file.source = (fileSource.length ? fileSource : source); + file.url = (url.length ? url : null); + file.loaded = false; + + files[fileIndex] = file; + + var filesSelect = document.getElementById("files"); + var option = document.createElement("option"); + files[fileIndex].menuOption = option; + option.value = fileIndex; + option.text = (file.url ? file.url : "(unknown script)"); + filesSelect.appendChild(option); + + var siteBrowser = new SiteBrowser(); + siteBrowser.addURL(file.url, fileIndex); + siteBrowser.selectInitialFile(); + + firstLoad = true; + } + + var sourceObj = new Object(); + sourceObj.file = fileIndex; + sourceObj.baseLineNumber = baseLineNumber; + file.scripts.push(sourceId); + scripts[sourceId] = sourceObj; + + if (!firstLoad) + updateFileSource((fileSource.length ? fileSource : source), url, false); + + if (currentFile == -1) + loadFile(fileIndex, false); +} + +function jumpToLine(sourceId, line) +{ + var script = scripts[sourceId]; + if (line <= 0 || !script) + return; + + var file = files[script.file]; + if (!file) + return; + + if (currentFile != script.file) + loadFile(script.file, true); + if (currentRow) + currentRow.removeStyleClass("current"); + if (!file.element) + return; + if (line > file.element.firstChild.childNodes.length) + return; + + currentRow = file.element.firstChild.childNodes.item(line - 1); + if (!currentRow) + return; + + currentRow.addStyleClass("current"); + + var sourcesDiv = document.getElementById("sources"); + var sourcesDocument = document.getElementById("sources").contentDocument; + var parent = sourcesDocument.body; + var offset = totalOffsetTop(currentRow, parent); + if (offset < (parent.scrollTop + 20) || offset > (parent.scrollTop + sourcesDiv.clientHeight - 20)) + parent.scrollTop = totalOffsetTop(currentRow, parent) - (sourcesDiv.clientHeight / 2) + 10; +} + +function willExecuteStatement(sourceId, line, fromLeavingFrame) +{ + var script = scripts[sourceId]; + if (line <= 0 || !script) + return; + + var file = files[script.file]; + if (!file) + return; + + lastStatement = [sourceId, line]; + + var breakpoint = file.breakpoints[line]; + + var shouldBreak = false; + + if (breakpoint && breakpoint.enabled) { + switch(breakpoint.type) { + case 0: + shouldBreak = (breakpoint.value == "break" || DebuggerDocument.evaluateScript(breakpoint.value, 0) == 1); + if (shouldBreak) + breakpoint.hitcount++; + break; + case 1: + var message = "Hit breakpoint on line " + line; + if (breakpoint.value != "break") + message = DebuggerDocument.evaluateScript(breakpoint.value, 0); + if (consoleWindow) + consoleWindow.appendMessage("", message); + breakpoint.hitcount++; + break; + } + var editor = breakpoint.editor; + var counter = null; + if (editor) + counter = breakpoint.editor.query('.//span[@class="hitCounter"]'); + if (counter) + counter.innerText = breakpoint.hitcount; + } + + if (pauseOnNextStatement || shouldBreak || (steppingOver && !steppingStack)) { + pause(); + pauseOnNextStatement = false; + pausedWhileLeavingFrame = fromLeavingFrame || false; + } + + if (isPaused) { + updateFunctionStack(); + jumpToLine(sourceId, line); + } +} + +function didEnterCallFrame(sourceId, line) +{ + if (steppingOver || steppingOut) + steppingStack++; + + if (lastStatement) + frameLineNumberStack.unshift(lastStatement); + willExecuteStatement(sourceId, line); +} + +function willLeaveCallFrame(sourceId, line) +{ + if (line <= 0) + resume(); + willExecuteStatement(sourceId, line, true); + frameLineNumberStack.shift(); + if (!steppingStack) + steppingOver = false; + if (steppingOut && !steppingStack) { + steppingOut = false; + pauseOnNextStatement = true; + } + if ((steppingOver || steppingOut) && steppingStack >= 1) + steppingStack--; +} + +function exceptionWasRaised(sourceId, line) +{ + pause(); + updateFunctionStack(); + jumpToLine(sourceId, line); +} + +function showConsoleWindow() +{ + if (!consoleWindow) + consoleWindow = window.open("console.html", "console", "top=200, left=200, width=500, height=300, toolbar=yes, resizable=yes"); + else + consoleWindow.focus(); +} + +function closeFile(fileIndex) +{ + if (fileIndex != -1) { + currentFile = -1; + var file = files[fileIndex]; + var sourcesDocument = document.getElementById("sources").contentDocument; + // Clean up our file's content + sourcesDocument.getElementById("file" + fileIndex).removeChildren(); + // Remove the file from the open files pool + delete sourcesDocument.getElementById("file" + fileIndex); + + var filesSelect = document.getElementById("files"); + // Select the next loaded file. If we're at the end of the list, loop back to beginning. + var nextSelectedFile = (filesSelect.selectedIndex + 1 >= filesSelect.options.length) ? 0 : filesSelect.selectedIndex; + // Remove the file from file lists + filesSelect.options[filesSelect.selectedIndex] = null; + SiteBrowser.removeFile(fileIndex); + delete files[fileIndex]; + + // Clean up the function list + document.getElementById("functions").removeChildren(); + + // Display appropriate place-holders, if we closed all files + if (filesSelect.options.length < 1) { + document.getElementById("filesPopupButtonContent").innerHTML = "<No files loaded>"; + document.getElementById("functionNamesPopup").style.display = "none"; + } + else + switchFile(nextSelectedFile); + } +} + +function closeCurrentFile() +{ + closeFile(currentFile); +}