2 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 var sourceFiles = new Array();
30 var currentSourceId = -1;
31 var currentRow = null;
32 var currentStack = null;
33 var previousFiles = new Array();
34 var nextFiles = new Array();
35 var isResizingColumn = false;
37 function sleep(numberMillis) {
39 var exitTime = now.getTime() + numberMillis;
42 if (now.getTime() > exitTime)
47 function columnResizerMouseOver(element) {
48 element.style.cursor = "move";
51 function columnResizerMouseOut(element) {
53 element.style.cursor = "arrow";
56 function headerMouseDown(element) {
57 if (!isResizingColumn)
58 element.style.background = "url(glossyHeaderPressed.png) repeat-x";
61 function headerMouseUp(element) {
62 element.style.background = "url(glossyHeader.png) repeat-x";
65 function headerMouseOut(element) {
66 element.style.background = "url(glossyHeader.png) repeat-x";
69 function dividerDragStart(element, dividerDrag, dividerDragEnd, event) {
70 element.dragging = true;
71 element.dragLastY = event.clientY + window.scrollY;
72 element.dragLastX = event.clientX + window.scrollX;
73 document.addEventListener("mousemove", dividerDrag, true);
74 document.addEventListener("mouseup", dividerDragEnd, true);
75 event.preventDefault();
78 function sourceDividerDragStart(event) {
79 dividerDragStart(document.getElementById("divider"), dividerDrag, sourceDividerDragEnd, event);
82 function infoDividerDragStart(event) {
83 dividerDragStart(document.getElementById("infoDivider"), infoDividerDrag, infoDividerDragEnd, event);
86 function columnResizerDragStart(event) {
87 isResizingColumn = true;
88 dividerDragStart(document.getElementById("variableColumnResizer"), columnResizerDrag, columnResizerDragEnd, event);
91 function columnResizerDragEnd(event) {
92 isResizingColumn = false;
93 dividerDragEnd(document.getElementById("variableColumnResizer"), columnResizerDrag, columnResizerDragEnd, event);
96 function infoDividerDragEnd(event) {
97 dividerDragEnd(document.getElementById("infoDivider"), infoDividerDrag, infoDividerDragEnd);
100 function sourceDividerDragEnd(event) {
101 dividerDragEnd(document.getElementById("divider"), dividerDrag, sourceDividerDragEnd);
104 function dividerDragEnd(element, dividerDrag, dividerDragEnd, event) {
105 element.dragging = false;
106 document.removeEventListener("mousemove", dividerDrag, true);
107 document.removeEventListener("mouseup", dividerDragEnd, true);
110 function columnResizerDrag(event) {
111 var element = document.getElementById("variableColumnResizer");
112 if (element.dragging == true) {
113 var main = document.getElementById("rightPane");
114 var variableColumn = document.getElementById("variable");
115 var x = event.clientX + window.scrollX;
116 var delta = element.dragLastX - x;
117 var newWidth = constrainedWidthFromElement(variableColumn.clientWidth - delta, main);
118 variableColumn.style.width = newWidth + "px";
119 element.style.left = newWidth + "px";
120 element.dragLastX = x;
121 event.preventDefault();
125 function constrainedWidthFromElement(width, element) {
126 if (width < element.clientWidth * 0.25)
127 width = element.clientWidth * 0.25;
128 else if (width > element.clientWidth * 0.75)
129 width = element.clientWidth * 0.75;
134 function constrainedHeightFromElement(height, element) {
135 if (height < element.clientHeight * 0.25)
136 height = element.clientHeight * 0.25;
137 else if (height > element.clientHeight * 0.75)
138 height = element.clientHeight * 0.75;
143 function infoDividerDrag(event) {
144 var element = document.getElementById("infoDivider");
145 if (document.getElementById("infoDivider").dragging == true) {
146 var main = document.getElementById("main");
147 var leftPane = document.getElementById("leftPane");
148 var rightPane = document.getElementById("rightPane");
149 var x = event.clientX + window.scrollX;
150 var delta = element.dragLastX - x;
151 var newWidth = constrainedWidthFromElement(leftPane.clientWidth - delta, main);
152 leftPane.style.width = newWidth + "px";
153 rightPane.style.left = newWidth + "px";
154 element.dragLastX = x;
155 event.preventDefault();
159 function dividerDrag(event) {
160 var element = document.getElementById("divider");
161 if (document.getElementById("divider").dragging == true) {
162 var main = document.getElementById("main");
163 var top = document.getElementById("info");
164 var bottom = document.getElementById("body");
165 var y = event.clientY + window.scrollY;
166 var delta = element.dragLastY - y;
167 var newHeight = constrainedHeightFromElement(top.clientHeight - delta, main);
168 top.style.height = newHeight + "px";
169 bottom.style.top = newHeight + "px";
170 element.dragLastY = y;
171 event.preventDefault();
176 document.getElementById("divider").addEventListener("mousedown", sourceDividerDragStart, false);
177 document.getElementById("infoDivider").addEventListener("mousedown", infoDividerDragStart, false);
178 document.getElementById("variableColumnResizer").addEventListener("mousedown", columnResizerDragStart, false);
181 function isPaused() {
182 return DebuggerDocument.isPaused();
186 DebuggerDocument.pause();
192 removeStyleClass(currentRow, "current");
197 var stackframeTable = document.getElementById("stackframeTable");
198 stackframeTable.innerHTML = ""; // clear the content
202 DebuggerDocument.resume();
207 DebuggerDocument.stepInto();
210 function hasStyleClass(element,className)
212 return ( element.className.indexOf(className) != -1 );
215 function addStyleClass(element,className)
217 if (!hasStyleClass(element,className))
218 element.className += ( element.className.length ? " " + className : className );
221 function removeStyleClass(element,className)
223 if (hasStyleClass(element,className))
224 element.className = element.className.replace(className, "");
227 function addBreakPoint(event)
229 var row = event.target.parentNode;
230 if (hasStyleClass(row, "breakpoint")) {
231 if (hasStyleClass(row, "disabled")) {
232 removeStyleClass(row, "disabled");
233 sourceFiles[currentSourceId].breakpoints[parseInt(event.target.title)] = 1;
235 addStyleClass(row, "disabled");
236 sourceFiles[currentSourceId].breakpoints[parseInt(event.target.title)] = -1;
239 addStyleClass(row, "breakpoint");
240 removeStyleClass(row, "disabled");
241 sourceFiles[currentSourceId].breakpoints[parseInt(event.target.title)] = 1;
245 function totalOffsetTop(element,stop)
248 if (element.offsetParent) {
249 while (element.offsetParent) {
250 currentTop += element.offsetTop
251 element = element.offsetParent;
259 function switchFile()
261 var files = document.getElementById("files");
262 loadSource(files.options[files.selectedIndex].value,true);
265 function syntaxHighlight(code)
267 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 };
269 function echoChar(c) {
282 function isDigit(number) {
283 var string = "1234567890";
284 if (string.indexOf(number) != -1)
289 function isHex(hex) {
290 var string = "1234567890abcdefABCDEF";
291 if (string.indexOf(hex) != -1)
296 function isLetter(letter) {
297 var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
298 if (string.indexOf(letter) != -1)
307 for (var i = 0; i < code.length; i++) {
310 cNext = code.charAt(i + 1);
312 if (c == "/" && cNext == "*") {
313 result += "<span class=\"comment\">";
316 for (i += 2; i < code.length; i++) {
322 result += "<span class=\"comment\">";
323 if (cPrev == "*" && c == "/")
329 } else if (c == "/" && cNext == "/") {
330 result += "<span class=\"comment\">";
333 for (i += 2; i < code.length; i++) {
342 } else if (c == "\"" || c == "'") {
343 var instringtype = c;
345 result += "<span class=\"string\">";
347 for (i += 1; i < code.length; i++) {
349 if (stringstart < (i - 1) && cPrev == instringtype && code.charAt(i - 2) != "\\")
357 } else if (c == "0" && cNext == "x" && (i == 0 || (!isLetter(cPrev) && !isDigit(cPrev)))) {
358 result += "<span class=\"number\">";
361 for (i += 2; i < code.length; i++) {
370 } else if ((isDigit(c) || ((c == "-" || c == ".") && isDigit(cNext))) && (i == 0 || (!isLetter(cPrev) && !isDigit(cPrev)))) {
371 result += "<span class=\"number\">";
373 for (i += 1; i < code.length; i++) {
375 if (!isDigit(c) && c != ".")
382 } else if(isLetter(c) && (i == 0 || !isLetter(cPrev))) {
385 for (var j = i + 1; j < i + 12 && j < code.length; j++) {
392 if (keywords[keyword]) {
393 result += "<span class=\"keyword\">" + keyword + "</span>";
394 i += keyword.length - 1;
405 function navFilePrevious(element)
407 if (element.disabled)
409 var lastSource = previousFiles.pop();
410 if (currentSourceId != -1)
411 nextFiles.unshift(currentSourceId);
412 loadSource(lastSource, false);
415 function navFileNext(element)
417 if (element.disabled)
419 var lastSource = nextFiles.shift();
420 if (currentSourceId != -1)
421 previousFiles.push(currentSourceId);
422 loadSource(lastSource, false);
425 function updateFunctionStack()
427 var stackframeTable = document.getElementById("stackframeTable");
428 stackframeTable.innerHTML = ""; // clear the content
430 currentStack = DebuggerDocument.currentFunctionStack();
431 for(var i = 0; i < currentStack.length; i++) {
432 var tr = document.createElement("tr");
433 var td = document.createElement("td");
434 td.className = "stackNumber";
438 td = document.createElement("td");
439 td.innerText = currentStack[i];
442 stackframeTable.appendChild(tr);
445 var tr = document.createElement("tr");
446 var td = document.createElement("td");
447 td.className = "stackNumber";
451 td = document.createElement("td");
452 td.innerText = "Global";
455 stackframeTable.appendChild(tr);
458 function loadSource(sourceId,manageNavLists)
460 if (!sourceFiles[sourceId])
463 if (currentSourceId != -1 && sourceFiles[currentSourceId] && sourceFiles[currentSourceId].element)
464 sourceFiles[currentSourceId].element.style.display = "none";
466 if (!sourceFiles[sourceId].loaded) {
467 sourceFiles[sourceId].lines = sourceFiles[sourceId].source.split("\n");
469 var sourcesDocument = document.getElementById("sources").contentDocument;
470 var sourcesDiv = sourcesDocument.body;
471 var sourceDiv = sourcesDocument.createElement("div");
472 sourceDiv.id = "source" + sourceId;
473 sourcesDiv.appendChild(sourceDiv);
474 sourceFiles[sourceId].element = sourceDiv;
476 var table = sourcesDocument.createElement("table");
477 sourceDiv.appendChild(table);
479 var lines = syntaxHighlight(sourceFiles[sourceId].source).split("\n");
480 for( var i = 0; i < lines.length; i++ ) {
481 var tr = sourcesDocument.createElement("tr");
482 var td = sourcesDocument.createElement("td");
483 td.className = "gutter";
485 td.addEventListener("click", addBreakPoint, true);
488 td = sourcesDocument.createElement("td");
489 td.className = "source";
490 td.innerHTML = lines[i];
492 table.appendChild(tr);
495 sourceFiles[sourceId].loaded = true;
498 sourceFiles[sourceId].element.style.display = null;
500 document.getElementById("filesPopupButtonContent").innerText = sourceFiles[sourceId].url;
502 var files = document.getElementById("files");
503 for (var i = 0; i < files.childNodes.length; i++) {
504 if (files.childNodes[i].value == sourceId) {
505 files.selectedIndex = i;
510 if (manageNavLists) {
511 nextFiles = new Array();
512 if (currentSourceId != -1)
513 previousFiles.push(currentSourceId);
516 document.getElementById("navFileLeftButton").disabled = (previousFiles.length == 0);
517 document.getElementById("navFileRightButton").disabled = (nextFiles.length == 0);
519 currentSourceId = sourceId;
522 function didParseScript(source,url,sourceId)
524 sourceFiles[sourceId] = new Object();
525 sourceFiles[sourceId].source = source;
526 sourceFiles[sourceId].url = url;
527 sourceFiles[sourceId].loaded = false;
528 sourceFiles[sourceId].breakpoints = new Array();
530 var files = document.getElementById("files");
531 var option = document.createElement("option");
532 sourceFiles[sourceId].menuOption = option;
533 option.value = sourceId;
535 files.appendChild(option);
537 if (currentSourceId == -1)
538 loadSource(sourceId,true);
542 function willExecuteStatement(sourceId,line)
544 if (line <= 0 || !sourceFiles[sourceId])
547 if (sourceFiles[sourceId].breakpoints[line] == 1)
551 if (currentSourceId != sourceId)
552 loadSource(sourceId,true);
554 removeStyleClass(currentRow, "current");
555 if (!sourceFiles[sourceId].element)
557 if (sourceFiles[sourceId].element.firstChild.childNodes.length < line)
560 updateFunctionStack();
562 currentRow = sourceFiles[sourceId].element.firstChild.childNodes.item(line - 1);
563 addStyleClass(currentRow, "current");
565 var sourcesDiv = document.getElementById("sources");
566 var sourcesDocument = document.getElementById("sources").contentDocument;
567 var parent = sourcesDocument.body;
568 var offset = totalOffsetTop(currentRow, parent);
569 if (offset < (parent.scrollTop + 20) || offset > (parent.scrollTop + sourcesDiv.clientHeight - 20))
570 parent.scrollTop = totalOffsetTop(currentRow, parent) - (sourcesDiv.clientHeight / 2) + 10;
574 function didEnterCallFrame(sourceId,line)
576 willExecuteStatement(sourceId,line);
579 function willLeaveCallFrame(sourceId,line)
581 willExecuteStatement(sourceId,line);