| /* |
| * 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 += "<span class=\"comment\">"; |
| echoChar(c); |
| echoChar(cNext); |
| for (i += 2; i < code.length; i++) { |
| c = code.charAt(i); |
| if (c == "\n") |
| result += "</span>"; |
| echoChar(c); |
| if (c == "\n") |
| result += "<span class=\"comment\">"; |
| if (cPrev == "*" && c == "/") |
| break; |
| cPrev = c; |
| } |
| result += "</span>"; |
| continue; |
| } else if (c == "/" && cNext == "/") { |
| result += "<span class=\"comment\">"; |
| echoChar(c); |
| echoChar(cNext); |
| for (i += 2; i < code.length; i++) { |
| c = code.charAt(i); |
| if (c == "\n") |
| break; |
| echoChar(c); |
| } |
| result += "</span>"; |
| echoChar(c); |
| continue; |
| } else if (c == "\"" || c == "'") { |
| var instringtype = c; |
| var stringstart = i; |
| result += "<span class=\"string\">"; |
| 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 += "</span>"; |
| echoChar(c); |
| continue; |
| } else if (c == "0" && cNext == "x" && (i == 0 || (!isLetter(cPrev) && !isDigit(cPrev)))) { |
| result += "<span class=\"number\">"; |
| echoChar(c); |
| echoChar(cNext); |
| for (i += 2; i < code.length; i++) { |
| c = code.charAt(i); |
| if (!isHex(c)) |
| break; |
| echoChar(c); |
| } |
| result += "</span>"; |
| echoChar(c); |
| continue; |
| } else if ((isDigit(c) || ((c == "-" || c == ".") && isDigit(cNext))) && (i == 0 || (!isLetter(cPrev) && !isDigit(cPrev)))) { |
| result += "<span class=\"number\">"; |
| echoChar(c); |
| for (i += 1; i < code.length; i++) { |
| c = code.charAt(i); |
| if (!isDigit(c) && c != ".") |
| break; |
| echoChar(c); |
| } |
| result += "</span>"; |
| 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 += "<span class=\"keyword\"><a name=\"function-" + fileIndex + "-" + file.functionNames.length + "\" id=\"" + fileIndex + "-" + file.functionNames.length + "\">" + keyword + "</a></span>"; |
| else |
| result += "<span class=\"keyword\">" + keyword + "</span>"; |
| |
| if (functionName.length && !functionIsAnonymous) { |
| result += " <a name=\"function-" + fileIndex + "-" + file.functionNames.length + "\" id=\"" + fileIndex + "-" + file.functionNames.length + "\">" + functionName + "</a>"; |
| 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 = '<span class="placeholder"><No selected symbol></span>'; |
| return; |
| } |
| |
| var functionSelect = document.getElementById("functions"); |
| var selectedFunction = functionSelect.childNodes[index]; |
| var selection = sourcesFrame.getSelection(); |
| var currentFunction = selectedFunction.value; |
| var currentFunctionElement = sourcesFrame.document.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 = "<No selected symbol>"; |
| 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 || force) |
| 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 = "<span class='placeholder'><No files loaded></span>"; |
| document.getElementById("functionNamesPopup").style.display = "none"; |
| } |
| else |
| switchFile(nextSelectedFile); |
| } |
| } |
| |
| function closeCurrentFile() |
| { |
| closeFile(currentFile); |
| } |