Skip to content

Instantly share code, notes, and snippets.

@jonmagic
Created October 6, 2008 22:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonmagic/15169 to your computer and use it in GitHub Desktop.
Save jonmagic/15169 to your computer and use it in GitHub Desktop.
/* utilities.js */
/*
* Copyright (C) 2007 Apple Inc. All rights reserved.
*
* 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.
*/
Object.type = function(obj, win)
{
if (obj === null)
return "null";
var type = typeof obj;
if (type !== "object" && type !== "function")
return type;
win = win || window;
if (obj instanceof win.String)
return "string";
if (obj instanceof win.Array)
return "array";
if (obj instanceof win.Boolean)
return "boolean";
if (obj instanceof win.Number)
return "number";
if (obj instanceof win.Date)
return "date";
if (obj instanceof win.RegExp)
return "regexp";
if (obj instanceof win.Error)
return "error";
return type;
}
Object.hasProperties = function(obj)
{
if (typeof obj === "undefined" || typeof obj === "null")
return false;
for (var name in obj)
return true;
return false;
}
Object.describe = function(obj, abbreviated)
{
var type1 = Object.type(obj);
var type2 = Object.prototype.toString.call(obj).replace(/^\[object (.*)\]$/i, "$1");
switch (type1) {
case "object":
return type2;
case "array":
return "[" + obj.toString() + "]";
case "string":
if (obj.length > 100)
return "\"" + obj.substring(0, 100) + "\u2026\"";
return "\"" + obj + "\"";
case "function":
var objectText = String(obj);
if (!/^function /.test(objectText))
objectText = (type2 == "object") ? type1 : type2;
else if (abbreviated)
objectText = /.*/.exec(obj)[0].replace(/ +$/g, "");
return objectText;
case "regexp":
return String(obj).replace(/([\\\/])/g, "\\$1").replace(/\\(\/[gim]*)$/, "$1").substring(1);
default:
return String(obj);
}
}
Object.sortedProperties = function(obj)
{
var properties = [];
for (var prop in obj)
properties.push(prop);
properties.sort();
return properties;
}
Function.prototype.bind = function(thisObject)
{
var func = this;
var args = Array.prototype.slice.call(arguments, 1);
return function() { return func.apply(thisObject, args.concat(Array.prototype.slice.call(arguments, 0))) };
}
Node.prototype.rangeOfWord = function(offset, stopCharacters, stayWithinNode, direction)
{
var startNode;
var startOffset = 0;
var endNode;
var endOffset = 0;
if (!stayWithinNode)
stayWithinNode = this;
if (!direction || direction === "backward" || direction === "both") {
var node = this;
while (node) {
if (node === stayWithinNode) {
if (!startNode)
startNode = stayWithinNode;
break;
}
if (node.nodeType === Node.TEXT_NODE) {
var start = (node === this ? (offset - 1) : (node.nodeValue.length - 1));
for (var i = start; i >= 0; --i) {
if (stopCharacters.indexOf(node.nodeValue[i]) !== -1) {
startNode = node;
startOffset = i + 1;
break;
}
}
}
if (startNode)
break;
node = node.traversePreviousNode(false, stayWithinNode);
}
if (!startNode) {
startNode = stayWithinNode;
startOffset = 0;
}
} else {
startNode = this;
startOffset = offset;
}
if (!direction || direction === "forward" || direction === "both") {
node = this;
while (node) {
if (node === stayWithinNode) {
if (!endNode)
endNode = stayWithinNode;
break;
}
if (node.nodeType === Node.TEXT_NODE) {
var start = (node === this ? offset : 0);
for (var i = start; i < node.nodeValue.length; ++i) {
if (stopCharacters.indexOf(node.nodeValue[i]) !== -1) {
endNode = node;
endOffset = i;
break;
}
}
}
if (endNode)
break;
node = node.traverseNextNode(false, stayWithinNode);
}
if (!endNode) {
endNode = stayWithinNode;
endOffset = stayWithinNode.nodeType === Node.TEXT_NODE ? stayWithinNode.nodeValue.length : stayWithinNode.childNodes.length;
}
} else {
endNode = this;
endOffset = offset;
}
var result = this.ownerDocument.createRange();
result.setStart(startNode, startOffset);
result.setEnd(endNode, endOffset);
return result;
}
Element.prototype.removeStyleClass = function(className)
{
// Test for the simple case before using a RegExp.
if (this.className === className) {
this.className = "";
return;
}
this.removeMatchingStyleClasses(className.escapeForRegExp());
}
Element.prototype.removeMatchingStyleClasses = function(classNameRegex)
{
var regex = new RegExp("(^|\\s+)" + classNameRegex + "($|\\s+)");
if (regex.test(this.className))
this.className = this.className.replace(regex, " ");
}
Element.prototype.addStyleClass = function(className)
{
if (className && !this.hasStyleClass(className))
this.className += (this.className.length ? " " + className : className);
}
Element.prototype.hasStyleClass = function(className)
{
if (!className)
return false;
// Test for the simple case before using a RegExp.
if (this.className === className)
return true;
var regex = new RegExp("(^|\\s)" + className.escapeForRegExp() + "($|\\s)");
return regex.test(this.className);
}
Node.prototype.enclosingNodeOrSelfWithNodeNameInArray = function(nameArray)
{
for (var node = this; node && !objectsAreSame(node, this.ownerDocument); node = node.parentNode)
for (var i = 0; i < nameArray.length; ++i)
if (node.nodeName.toLowerCase() === nameArray[i].toLowerCase())
return node;
return null;
}
Node.prototype.enclosingNodeOrSelfWithNodeName = function(nodeName)
{
return this.enclosingNodeOrSelfWithNodeNameInArray([nodeName]);
}
Node.prototype.enclosingNodeOrSelfWithClass = function(className)
{
for (var node = this; node && !objectsAreSame(node, this.ownerDocument); node = node.parentNode)
if (node.nodeType === Node.ELEMENT_NODE && node.hasStyleClass(className))
return node;
return null;
}
Node.prototype.enclosingNodeWithClass = function(className)
{
if (!this.parentNode)
return null;
return this.parentNode.enclosingNodeOrSelfWithClass(className);
}
Element.prototype.query = function(query)
{
return this.ownerDocument.evaluate(query, this, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}
Element.prototype.removeChildren = function()
{
while (this.firstChild)
this.removeChild(this.firstChild);
}
Element.prototype.isInsertionCaretInside = function()
{
var selection = window.getSelection();
if (!selection.rangeCount || !selection.isCollapsed)
return false;
var selectionRange = selection.getRangeAt(0);
return selectionRange.startContainer === this || selectionRange.startContainer.isDescendant(this);
}
Element.prototype.__defineGetter__("totalOffsetLeft", function()
{
var total = 0;
for (var element = this; element; element = element.offsetParent)
total += element.offsetLeft;
return total;
});
Element.prototype.__defineGetter__("totalOffsetTop", function()
{
var total = 0;
for (var element = this; element; element = element.offsetParent)
total += element.offsetTop;
return total;
});
Element.prototype.firstChildSkippingWhitespace = firstChildSkippingWhitespace;
Element.prototype.lastChildSkippingWhitespace = lastChildSkippingWhitespace;
Node.prototype.isWhitespace = isNodeWhitespace;
Node.prototype.nodeTypeName = nodeTypeName;
Node.prototype.displayName = nodeDisplayName;
Node.prototype.contentPreview = nodeContentPreview;
Node.prototype.isAncestor = isAncestorNode;
Node.prototype.isDescendant = isDescendantNode;
Node.prototype.firstCommonAncestor = firstCommonNodeAncestor;
Node.prototype.nextSiblingSkippingWhitespace = nextSiblingSkippingWhitespace;
Node.prototype.previousSiblingSkippingWhitespace = previousSiblingSkippingWhitespace;
Node.prototype.traverseNextNode = traverseNextNode;
Node.prototype.traversePreviousNode = traversePreviousNode;
Node.prototype.onlyTextChild = onlyTextChild;
String.prototype.hasSubstring = function(string, caseInsensitive)
{
if (!caseInsensitive)
return this.indexOf(string) !== -1;
return this.match(new RegExp(string.escapeForRegExp(), "i"));
}
String.prototype.escapeCharacters = function(chars)
{
var foundChar = false;
for (var i = 0; i < chars.length; ++i) {
if (this.indexOf(chars.charAt(i)) !== -1) {
foundChar = true;
break;
}
}
if (!foundChar)
return this;
var result = "";
for (var i = 0; i < this.length; ++i) {
if (chars.indexOf(this.charAt(i)) !== -1)
result += "\\";
result += this.charAt(i);
}
return result;
}
String.prototype.escapeForRegExp = function()
{
return this.escapeCharacters("^[]{}()\\.$*+?|");
}
String.prototype.escapeHTML = function()
{
return this.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}
String.prototype.collapseWhitespace = function()
{
return this.replace(/[\s\xA0]+/g, " ");
}
String.prototype.trimLeadingWhitespace = function()
{
return this.replace(/^[\s\xA0]+/g, "");
}
String.prototype.trimTrailingWhitespace = function()
{
return this.replace(/[\s\xA0]+$/g, "");
}
String.prototype.trimWhitespace = function()
{
return this.replace(/^[\s\xA0]+|[\s\xA0]+$/g, "");
}
String.prototype.trimURL = function(baseURLDomain)
{
var result = this.replace(new RegExp("^http[s]?:\/\/", "i"), "");
if (baseURLDomain)
result = result.replace(new RegExp("^" + baseURLDomain.escapeForRegExp(), "i"), "");
return result;
}
function getStyleTextWithShorthands(style)
{
var cssText = "";
var foundProperties = {};
for (var i = 0; i < style.length; ++i) {
var individualProperty = style[i];
var shorthandProperty = style.getPropertyShorthand(individualProperty);
var propertyName = (shorthandProperty || individualProperty);
if (propertyName in foundProperties)
continue;
if (shorthandProperty) {
var value = getShorthandValue(style, shorthandProperty);
var priority = getShorthandPriority(style, shorthandProperty);
} else {
var value = style.getPropertyValue(individualProperty);
var priority = style.getPropertyPriority(individualProperty);
}
foundProperties[propertyName] = true;
cssText += propertyName + ": " + value;
if (priority)
cssText += " !" + priority;
cssText += "; ";
}
return cssText;
}
function getShorthandValue(style, shorthandProperty)
{
var value = style.getPropertyValue(shorthandProperty);
if (!value) {
// Some shorthands (like border) return a null value, so compute a shorthand value.
// FIXME: remove this when http://bugs.webkit.org/show_bug.cgi?id=15823 is fixed.
var foundProperties = {};
for (var i = 0; i < style.length; ++i) {
var individualProperty = style[i];
if (individualProperty in foundProperties || style.getPropertyShorthand(individualProperty) !== shorthandProperty)
continue;
var individualValue = style.getPropertyValue(individualProperty);
if (style.isPropertyImplicit(individualProperty) || individualValue === "initial")
continue;
foundProperties[individualProperty] = true;
if (!value)
value = "";
else if (value.length)
value += " ";
value += individualValue;
}
}
return value;
}
function getShorthandPriority(style, shorthandProperty)
{
var priority = style.getPropertyPriority(shorthandProperty);
if (!priority) {
for (var i = 0; i < style.length; ++i) {
var individualProperty = style[i];
if (style.getPropertyShorthand(individualProperty) !== shorthandProperty)
continue;
priority = style.getPropertyPriority(individualProperty);
break;
}
}
return priority;
}
function getLonghandProperties(style, shorthandProperty)
{
var properties = [];
var foundProperties = {};
for (var i = 0; i < style.length; ++i) {
var individualProperty = style[i];
if (individualProperty in foundProperties || style.getPropertyShorthand(individualProperty) !== shorthandProperty)
continue;
foundProperties[individualProperty] = true;
properties.push(individualProperty);
}
return properties;
}
function getUniqueStyleProperties(style)
{
var properties = [];
var foundProperties = {};
for (var i = 0; i < style.length; ++i) {
var property = style[i];
if (property in foundProperties)
continue;
foundProperties[property] = true;
properties.push(property);
}
return properties;
}
function isNodeWhitespace()
{
if (!this || this.nodeType !== Node.TEXT_NODE)
return false;
if (!this.nodeValue.length)
return true;
return this.nodeValue.match(/^[\s\xA0]+$/);
}
function nodeTypeName()
{
if (!this)
return "(unknown)";
switch (this.nodeType) {
case Node.ELEMENT_NODE: return "Element";
case Node.ATTRIBUTE_NODE: return "Attribute";
case Node.TEXT_NODE: return "Text";
case Node.CDATA_SECTION_NODE: return "Character Data";
case Node.ENTITY_REFERENCE_NODE: return "Entity Reference";
case Node.ENTITY_NODE: return "Entity";
case Node.PROCESSING_INSTRUCTION_NODE: return "Processing Instruction";
case Node.COMMENT_NODE: return "Comment";
case Node.DOCUMENT_NODE: return "Document";
case Node.DOCUMENT_TYPE_NODE: return "Document Type";
case Node.DOCUMENT_FRAGMENT_NODE: return "Document Fragment";
case Node.NOTATION_NODE: return "Notation";
}
return "(unknown)";
}
function nodeDisplayName()
{
if (!this)
return "";
switch (this.nodeType) {
case Node.DOCUMENT_NODE:
return "Document";
case Node.ELEMENT_NODE:
var name = "<" + this.nodeName.toLowerCase();
if (this.hasAttributes()) {
var value = this.getAttribute("id");
if (value)
name += " id=\"" + value + "\"";
value = this.getAttribute("class");
if (value)
name += " class=\"" + value + "\"";
if (this.nodeName.toLowerCase() === "a") {
value = this.getAttribute("name");
if (value)
name += " name=\"" + value + "\"";
value = this.getAttribute("href");
if (value)
name += " href=\"" + value + "\"";
} else if (this.nodeName.toLowerCase() === "img") {
value = this.getAttribute("src");
if (value)
name += " src=\"" + value + "\"";
} else if (this.nodeName.toLowerCase() === "iframe") {
value = this.getAttribute("src");
if (value)
name += " src=\"" + value + "\"";
} else if (this.nodeName.toLowerCase() === "input") {
value = this.getAttribute("name");
if (value)
name += " name=\"" + value + "\"";
value = this.getAttribute("type");
if (value)
name += " type=\"" + value + "\"";
} else if (this.nodeName.toLowerCase() === "form") {
value = this.getAttribute("action");
if (value)
name += " action=\"" + value + "\"";
}
}
return name + ">";
case Node.TEXT_NODE:
if (isNodeWhitespace.call(this))
return "(whitespace)";
return "\"" + this.nodeValue + "\"";
case Node.COMMENT_NODE:
return "<!--" + this.nodeValue + "-->";
case Node.DOCUMENT_TYPE_NODE:
var docType = "<!DOCTYPE " + this.nodeName;
if (this.publicId) {
docType += " PUBLIC \"" + this.publicId + "\"";
if (this.systemId)
docType += " \"" + this.systemId + "\"";
} else if (this.systemId)
docType += " SYSTEM \"" + this.systemId + "\"";
if (this.internalSubset)
docType += " [" + this.internalSubset + "]";
return docType + ">";
}
return this.nodeName.toLowerCase().collapseWhitespace();
}
function nodeContentPreview()
{
if (!this || !this.hasChildNodes || !this.hasChildNodes())
return "";
var limit = 0;
var preview = "";
// always skip whitespace here
var currentNode = traverseNextNode.call(this, true, this);
while (currentNode) {
if (currentNode.nodeType === Node.TEXT_NODE)
preview += currentNode.nodeValue.escapeHTML();
else
preview += nodeDisplayName.call(currentNode).escapeHTML();
currentNode = traverseNextNode.call(currentNode, true, this);
if (++limit > 4) {
preview += "&#x2026;"; // ellipsis
break;
}
}
return preview.collapseWhitespace();
}
function objectsAreSame(a, b)
{
// FIXME: Make this more generic so is works with any wrapped object, not just nodes.
// This function is used to compare nodes that might be JSInspectedObjectWrappers, since
// JavaScript equality is not true for JSInspectedObjectWrappers of the same node wrapped
// with different global ExecStates, we use isSameNode to compare them.
if (a === b)
return true;
if (!a || !b)
return false;
if (a.isSameNode && b.isSameNode)
return a.isSameNode(b);
return false;
}
function isAncestorNode(ancestor)
{
if (!this || !ancestor)
return false;
var currentNode = ancestor.parentNode;
while (currentNode) {
if (objectsAreSame(this, currentNode))
return true;
currentNode = currentNode.parentNode;
}
return false;
}
function isDescendantNode(descendant)
{
return isAncestorNode.call(descendant, this);
}
function firstCommonNodeAncestor(node)
{
if (!this || !node)
return;
var node1 = this.parentNode;
var node2 = node.parentNode;
if ((!node1 || !node2) || !objectsAreSame(node1, node2))
return null;
while (node1 && node2) {
if (!node1.parentNode || !node2.parentNode)
break;
if (!objectsAreSame(node1, node2))
break;
node1 = node1.parentNode;
node2 = node2.parentNode;
}
return node1;
}
function nextSiblingSkippingWhitespace()
{
if (!this)
return;
var node = this.nextSibling;
while (node && node.nodeType === Node.TEXT_NODE && isNodeWhitespace.call(node))
node = node.nextSibling;
return node;
}
function previousSiblingSkippingWhitespace()
{
if (!this)
return;
var node = this.previousSibling;
while (node && node.nodeType === Node.TEXT_NODE && isNodeWhitespace.call(node))
node = node.previousSibling;
return node;
}
function firstChildSkippingWhitespace()
{
if (!this)
return;
var node = this.firstChild;
while (node && node.nodeType === Node.TEXT_NODE && isNodeWhitespace.call(node))
node = nextSiblingSkippingWhitespace.call(node);
return node;
}
function lastChildSkippingWhitespace()
{
if (!this)
return;
var node = this.lastChild;
while (node && node.nodeType === Node.TEXT_NODE && isNodeWhitespace.call(node))
node = previousSiblingSkippingWhitespace.call(node);
return node;
}
function traverseNextNode(skipWhitespace, stayWithin)
{
if (!this)
return;
var node = skipWhitespace ? firstChildSkippingWhitespace.call(this) : this.firstChild;
if (node)
return node;
if (stayWithin && objectsAreSame(this, stayWithin))
return null;
node = skipWhitespace ? nextSiblingSkippingWhitespace.call(this) : this.nextSibling;
if (node)
return node;
node = this;
while (node && !(skipWhitespace ? nextSiblingSkippingWhitespace.call(node) : node.nextSibling) && (!stayWithin || !node.parentNode || !objectsAreSame(node.parentNode, stayWithin)))
node = node.parentNode;
if (!node)
return null;
return skipWhitespace ? nextSiblingSkippingWhitespace.call(node) : node.nextSibling;
}
function traversePreviousNode(skipWhitespace, stayWithin)
{
if (!this)
return;
if (stayWithin && objectsAreSame(this, stayWithin))
return null;
var node = skipWhitespace ? previousSiblingSkippingWhitespace.call(this) : this.previousSibling;
while (node && (skipWhitespace ? lastChildSkippingWhitespace.call(node) : node.lastChild) )
node = skipWhitespace ? lastChildSkippingWhitespace.call(node) : node.lastChild;
if (node)
return node;
return this.parentNode;
}
function onlyTextChild(ignoreWhitespace)
{
if (!this)
return null;
var firstChild = ignoreWhitespace ? firstChildSkippingWhitespace.call(this) : this.firstChild;
if (!firstChild || firstChild.nodeType !== Node.TEXT_NODE)
return null;
var sibling = ignoreWhitespace ? nextSiblingSkippingWhitespace.call(firstChild) : firstChild.nextSibling;
return sibling ? null : firstChild;
}
function nodeTitleInfo(hasChildren, linkify)
{
var info = {title: "", hasChildren: hasChildren};
switch (this.nodeType) {
case Node.DOCUMENT_NODE:
info.title = "Document";
break;
case Node.ELEMENT_NODE:
info.title = "<span class=\"webkit-html-tag\">&lt;" + this.nodeName.toLowerCase().escapeHTML();
if (this.hasAttributes()) {
for (var i = 0; i < this.attributes.length; ++i) {
var attr = this.attributes[i];
info.title += " <span class=\"webkit-html-attribute\"><span class=\"webkit-html-attribute-name\">" + attr.name.escapeHTML() + "</span>=&#8203;\"";
var value = attr.value;
if (linkify && (attr.name === "src" || attr.name === "href")) {
var value = value.replace(/([\/;:\)\]\}])/g, "$1\u200B");
info.title += linkify(attr.value, value, "webkit-html-attribute-value", this.nodeName.toLowerCase() == "a");
} else {
var value = value.escapeHTML();
value = value.replace(/([\/;:\)\]\}])/g, "$1&#8203;");
info.title += "<span class=\"webkit-html-attribute-value\">" + value + "</span>";
}
info.title += "\"</span>";
}
}
info.title += "&gt;</span>&#8203;";
// If this element only has a single child that is a text node,
// just show that text and the closing tag inline rather than
// create a subtree for them
var textChild = onlyTextChild.call(this, Preferences.ignoreWhitespace);
var showInlineText = textChild && textChild.textContent.length < Preferences.maxInlineTextChildLength;
if (showInlineText) {
info.title += "<span class=\"webkit-html-text-node\">" + textChild.nodeValue.escapeHTML() + "</span>&#8203;<span class=\"webkit-html-tag\">&lt;/" + this.nodeName.toLowerCase().escapeHTML() + "&gt;</span>";
info.hasChildren = false;
}
break;
case Node.TEXT_NODE:
if (isNodeWhitespace.call(this))
info.title = "(whitespace)";
else
info.title = "\"<span class=\"webkit-html-text-node\">" + this.nodeValue.escapeHTML() + "</span>\"";
break
case Node.COMMENT_NODE:
info.title = "<span class=\"webkit-html-comment\">&lt;!--" + this.nodeValue.escapeHTML() + "--&gt;</span>";
break;
case Node.DOCUMENT_TYPE_NODE:
info.title = "<span class=\"webkit-html-doctype\">&lt;!DOCTYPE " + this.nodeName;
if (this.publicId) {
info.title += " PUBLIC \"" + this.publicId + "\"";
if (this.systemId)
info.title += " \"" + this.systemId + "\"";
} else if (this.systemId)
info.title += " SYSTEM \"" + this.systemId + "\"";
if (this.internalSubset)
info.title += " [" + this.internalSubset + "]";
info.title += "&gt;</span>";
break;
default:
info.title = this.nodeName.toLowerCase().collapseWhitespace().escapeHTML();
}
return info;
}
function getDocumentForNode(node) {
return node.nodeType == Node.DOCUMENT_NODE ? node : node.ownerDocument;
}
function parentNodeOrFrameElement(node) {
var parent = node.parentNode;
if (parent)
return parent;
return getDocumentForNode(node).defaultView.frameElement;
}
function isAncestorIncludingParentFrames(a, b) {
if (objectsAreSame(a, b))
return false;
for (var node = b; node; node = getDocumentForNode(node).defaultView.frameElement)
if (objectsAreSame(a, node) || isAncestorNode.call(a, node))
return true;
return false;
}
Number.secondsToString = function(seconds, formatterFunction, higherResolution)
{
if (!formatterFunction)
formatterFunction = String.sprintf;
var ms = seconds * 1000;
if (higherResolution && ms < 1000)
return formatterFunction("%.3fms", ms);
else if (ms < 1000)
return formatterFunction("%.0fms", ms);
if (seconds < 60)
return formatterFunction("%.2fs", seconds);
var minutes = seconds / 60;
if (minutes < 60)
return formatterFunction("%.1fmin", minutes);
var hours = minutes / 60;
if (hours < 24)
return formatterFunction("%.1fhrs", hours);
var days = hours / 24;
return formatterFunction("%.1f days", days);
}
Number.bytesToString = function(bytes, formatterFunction)
{
if (!formatterFunction)
formatterFunction = String.sprintf;
if (bytes < 1024)
return formatterFunction("%.0fB", bytes);
var kilobytes = bytes / 1024;
if (kilobytes < 1024)
return formatterFunction("%.2fKB", kilobytes);
var megabytes = kilobytes / 1024;
return formatterFunction("%.3fMB", megabytes);
}
Number.constrain = function(num, min, max)
{
if (num < min)
num = min;
else if (num > max)
num = max;
return num;
}
HTMLTextAreaElement.prototype.moveCursorToEnd = function()
{
var length = this.value.length;
this.setSelectionRange(length, length);
}
Array.prototype.remove = function(value, onlyFirst)
{
if (onlyFirst) {
var index = this.indexOf(value);
if (index !== -1)
this.splice(index, 1);
return;
}
var length = this.length;
for (var i = 0; i < length; ++i) {
if (this[i] === value)
this.splice(i, 1);
}
}
String.sprintf = function(format)
{
return String.vsprintf(format, Array.prototype.slice.call(arguments, 1));
}
String.tokenizeFormatString = function(format)
{
var tokens = [];
var substitutionIndex = 0;
function addStringToken(str)
{
tokens.push({ type: "string", value: str });
}
function addSpecifierToken(specifier, precision, substitutionIndex)
{
tokens.push({ type: "specifier", specifier: specifier, precision: precision, substitutionIndex: substitutionIndex });
}
var index = 0;
for (var precentIndex = format.indexOf("%", index); precentIndex !== -1; precentIndex = format.indexOf("%", index)) {
addStringToken(format.substring(index, precentIndex));
index = precentIndex + 1;
if (format[index] === "%") {
addStringToken("%");
++index;
continue;
}
if (!isNaN(format[index])) {
// The first character is a number, it might be a substitution index.
var number = parseInt(format.substring(index));
while (!isNaN(format[index]))
++index;
// If the number is greater than zero and ends with a "$",
// then this is a substitution index.
if (number > 0 && format[index] === "$") {
substitutionIndex = (number - 1);
++index;
}
}
var precision = -1;
if (format[index] === ".") {
// This is a precision specifier. If no digit follows the ".",
// then the precision should be zero.
++index;
precision = parseInt(format.substring(index));
if (isNaN(precision))
precision = 0;
while (!isNaN(format[index]))
++index;
}
addSpecifierToken(format[index], precision, substitutionIndex);
++substitutionIndex;
++index;
}
addStringToken(format.substring(index));
return tokens;
}
String.standardFormatters = {
d: function(substitution)
{
substitution = parseInt(substitution);
return !isNaN(substitution) ? substitution : 0;
},
f: function(substitution, token)
{
substitution = parseFloat(substitution);
if (substitution && token.precision > -1)
substitution = substitution.toFixed(token.precision);
return !isNaN(substitution) ? substitution : (token.precision > -1 ? Number(0).toFixed(token.precision) : 0);
},
s: function(substitution)
{
return substitution;
},
};
String.vsprintf = function(format, substitutions)
{
return String.format(format, substitutions, String.standardFormatters, "", function(a, b) { return a + b; }).formattedResult;
}
String.format = function(format, substitutions, formatters, initialValue, append)
{
if (!format || !substitutions || !substitutions.length)
return { formattedResult: append(initialValue, format), unusedSubstitutions: substitutions };
function prettyFunctionName()
{
return "String.format(\"" + format + "\", \"" + substitutions.join("\", \"") + "\")";
}
function warn(msg)
{
console.warn(prettyFunctionName() + ": " + msg);
}
function error(msg)
{
console.error(prettyFunctionName() + ": " + msg);
}
var result = initialValue;
var tokens = String.tokenizeFormatString(format);
var usedSubstitutionIndexes = {};
for (var i = 0; i < tokens.length; ++i) {
var token = tokens[i];
if (token.type === "string") {
result = append(result, token.value);
continue;
}
if (token.type !== "specifier") {
error("Unknown token type \"" + token.type + "\" found.");
continue;
}
if (token.substitutionIndex >= substitutions.length) {
// If there are not enough substitutions for the current substitutionIndex
// just output the format specifier literally and move on.
error("not enough substitution arguments. Had " + substitutions.length + " but needed " + (token.substitutionIndex + 1) + ", so substitution was skipped.");
result = append(result, "%" + (token.precision > -1 ? token.precision : "") + token.specifier);
continue;
}
usedSubstitutionIndexes[token.substitutionIndex] = true;
if (!(token.specifier in formatters)) {
// Encountered an unsupported format character, treat as a string.
warn("unsupported format character \u201C" + token.specifier + "\u201D. Treating as a string.");
result = append(result, substitutions[token.substitutionIndex]);
continue;
}
result = append(result, formatters[token.specifier](substitutions[token.substitutionIndex], token));
}
var unusedSubstitutions = [];
for (var i = 0; i < substitutions.length; ++i) {
if (i in usedSubstitutionIndexes)
continue;
unusedSubstitutions.push(substitutions[i]);
}
return { formattedResult: result, unusedSubstitutions: unusedSubstitutions };
}
/* treeoutline.js */
/*
* Copyright (C) 2007 Apple Inc. All rights reserved.
*
* 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.
*/
function TreeOutline(listNode)
{
this.children = [];
this.selectedTreeElement = null;
this._childrenListNode = listNode;
this._childrenListNode.removeChildren();
this._knownTreeElements = [];
this._treeElementsExpandedState = [];
this.expandTreeElementsWhenArrowing = false;
this.root = true;
this.hasChildren = false;
this.expanded = true;
this.selected = false;
this.treeOutline = this;
}
TreeOutline._knownTreeElementNextIdentifier = 1;
TreeOutline._appendChild = function(child)
{
if (!child)
throw("child can't be undefined or null");
var lastChild = this.children[this.children.length - 1];
if (lastChild) {
lastChild.nextSibling = child;
child.previousSibling = lastChild;
} else {
child.previousSibling = null;
child.nextSibling = null;
}
this.children.push(child);
this.hasChildren = true;
child.parent = this;
child.treeOutline = this.treeOutline;
child.treeOutline._rememberTreeElement(child);
var current = child.children[0];
while (current) {
current.treeOutline = this.treeOutline;
current.treeOutline._rememberTreeElement(current);
current = current.traverseNextTreeElement(false, child, true);
}
if (child.hasChildren && child.treeOutline._treeElementsExpandedState[child.identifier] !== undefined)
child.expanded = child.treeOutline._treeElementsExpandedState[child.identifier];
if (!this._childrenListNode) {
this._childrenListNode = this.treeOutline._childrenListNode.ownerDocument.createElement("ol");
this._childrenListNode.parentTreeElement = this;
this._childrenListNode.addStyleClass("children");
if (this.hidden)
this._childrenListNode.addStyleClass("hidden");
}
child._attach();
}
TreeOutline._insertChild = function(child, index)
{
if (!child)
throw("child can't be undefined or null");
var previousChild = (index > 0 ? this.children[index - 1] : null);
if (previousChild) {
previousChild.nextSibling = child;
child.previousSibling = previousChild;
} else {
child.previousSibling = null;
}
var nextChild = this.children[index];
if (nextChild) {
nextChild.previousSibling = child;
child.nextSibling = nextChild;
} else {
child.nextSibling = null;
}
this.children.splice(index, 0, child);
this.hasChildren = true;
child.parent = this;
child.treeOutline = this.treeOutline;
child.treeOutline._rememberTreeElement(child);
var current = child.children[0];
while (current) {
current.treeOutline = this.treeOutline;
current.treeOutline._rememberTreeElement(current);
current = current.traverseNextTreeElement(false, child, true);
}
if (child.hasChildren && child.treeOutline._treeElementsExpandedState[child.identifier] !== undefined)
child.expanded = child.treeOutline._treeElementsExpandedState[child.identifier];
if (!this._childrenListNode) {
this._childrenListNode = this.treeOutline._childrenListNode.ownerDocument.createElement("ol");
this._childrenListNode.parentTreeElement = this;
this._childrenListNode.addStyleClass("children");
if (this.hidden)
this._childrenListNode.addStyleClass("hidden");
}
child._attach();
}
TreeOutline._removeChildAtIndex = function(childIndex)
{
if (childIndex < 0 || childIndex >= this.children.length)
throw("childIndex out of range");
var child = this.children[childIndex];
this.children.splice(childIndex, 1);
child.deselect();
if (child.previousSibling)
child.previousSibling.nextSibling = child.nextSibling;
if (child.nextSibling)
child.nextSibling.previousSibling = child.previousSibling;
if (child.treeOutline) {
child.treeOutline._forgetTreeElement(child);
child.treeOutline._forgetChildrenRecursive(child);
}
child._detach();
child.treeOutline = null;
child.parent = null;
child.nextSibling = null;
child.previousSibling = null;
}
TreeOutline._removeChild = function(child)
{
if (!child)
throw("child can't be undefined or null");
var childIndex = this.children.indexOf(child);
if (childIndex === -1)
throw("child not found in this node's children");
TreeOutline._removeChildAtIndex.call(this, childIndex);
}
TreeOutline._removeChildren = function()
{
for (var i = 0; i < this.children.length; ++i) {
var child = this.children[i];
child.deselect();
if (child.treeOutline) {
child.treeOutline._forgetTreeElement(child);
child.treeOutline._forgetChildrenRecursive(child);
}
child._detach();
child.treeOutline = null;
child.parent = null;
child.nextSibling = null;
child.previousSibling = null;
}
this.children = [];
}
TreeOutline._removeChildrenRecursive = function()
{
var childrenToRemove = this.children;
var child = this.children[0];
while (child) {
if (child.children.length)
childrenToRemove = childrenToRemove.concat(child.children);
child = child.traverseNextTreeElement(false, this, true);
}
for (var i = 0; i < childrenToRemove.length; ++i) {
var child = childrenToRemove[i];
child.deselect();
if (child.treeOutline)
child.treeOutline._forgetTreeElement(child);
child._detach();
child.children = [];
child.treeOutline = null;
child.parent = null;
child.nextSibling = null;
child.previousSibling = null;
}
this.children = [];
}
TreeOutline.prototype._rememberTreeElement = function(element)
{
if (!this._knownTreeElements[element.identifier])
this._knownTreeElements[element.identifier] = [];
// check if the element is already known
var elements = this._knownTreeElements[element.identifier];
if (elements.indexOf(element) !== -1)
return;
// add the element
elements.push(element);
}
TreeOutline.prototype._forgetTreeElement = function(element)
{
if (this._knownTreeElements[element.identifier])
this._knownTreeElements[element.identifier].remove(element, true);
}
TreeOutline.prototype._forgetChildrenRecursive = function(parentElement)
{
var child = parentElement.children[0];
while (child) {
this._forgetTreeElement(child);
child = child.traverseNextTreeElement(false, this, true);
}
}
TreeOutline.prototype.findTreeElement = function(representedObject, isAncestor, getParent, equal)
{
if (!representedObject)
return null;
if (!equal)
equal = function(a, b) { return a === b };
if ("__treeElementIdentifier" in representedObject) {
// If this representedObject has a tree element identifier, and it is a known TreeElement
// in our tree we can just return that tree element.
var elements = this._knownTreeElements[representedObject.__treeElementIdentifier];
if (elements) {
for (var i = 0; i < elements.length; ++i)
if (equal(elements[i].representedObject, representedObject))
return elements[i];
}
}
if (!isAncestor || !(isAncestor instanceof Function) || !getParent || !(getParent instanceof Function))
return null;
// The representedObject isn't know, so we start at the top of the tree and work down to find the first
// tree element that represents representedObject or one of its ancestors.
var item;
var found = false;
for (var i = 0; i < this.children.length; ++i) {
item = this.children[i];
if (equal(item.representedObject, representedObject) || isAncestor(item.representedObject, representedObject)) {
found = true;
break;
}
}
if (!found)
return null;
// Make sure the item that we found is connected to the root of the tree.
// Build up a list of representedObject's ancestors that aren't already in our tree.
var ancestors = [];
var currentObject = representedObject;
while (currentObject) {
ancestors.unshift(currentObject);
if (equal(currentObject, item.representedObject))
break;
currentObject = getParent(currentObject);
}
// For each of those ancestors we populate them to fill in the tree.
for (var i = 0; i < ancestors.length; ++i) {
// Make sure we don't call findTreeElement with the same representedObject
// again, to prevent infinite recursion.
if (equal(ancestors[i], representedObject))
continue;
// FIXME: we could do something faster than findTreeElement since we will know the next
// ancestor exists in the tree.
item = this.findTreeElement(ancestors[i], isAncestor, getParent, equal);
if (item && item.onpopulate)
item.onpopulate(item);
}
// Now that all the ancestors are populated, try to find the representedObject again. This time
// without the isAncestor and getParent functions to prevent an infinite recursion if it isn't found.
return this.findTreeElement(representedObject, null, null, equal);
}
TreeOutline.prototype.treeElementFromPoint = function(x, y)
{
var node = this._childrenListNode.ownerDocument.elementFromPoint(x, y);
var listNode = node.enclosingNodeOrSelfWithNodeNameInArray(["ol", "li"]);
if (listNode)
return listNode.parentTreeElement || listNode.treeElement;
return null;
}
TreeOutline.prototype.handleKeyEvent = function(event)
{
if (!this.selectedTreeElement || event.shiftKey || event.metaKey || event.ctrlKey)
return false;
var handled = false;
var nextSelectedElement;
if (event.keyIdentifier === "Up" && !event.altKey) {
nextSelectedElement = this.selectedTreeElement.traversePreviousTreeElement(true);
while (nextSelectedElement && !nextSelectedElement.selectable)
nextSelectedElement = nextSelectedElement.traversePreviousTreeElement(!this.expandTreeElementsWhenArrowing);
handled = nextSelectedElement ? true : false;
} else if (event.keyIdentifier === "Down" && !event.altKey) {
nextSelectedElement = this.selectedTreeElement.traverseNextTreeElement(true);
while (nextSelectedElement && !nextSelectedElement.selectable)
nextSelectedElement = nextSelectedElement.traverseNextTreeElement(!this.expandTreeElementsWhenArrowing);
handled = nextSelectedElement ? true : false;
} else if (event.keyIdentifier === "Left") {
if (this.selectedTreeElement.expanded) {
if (event.altKey)
this.selectedTreeElement.collapseRecursively();
else
this.selectedTreeElement.collapse();
handled = true;
} else if (this.selectedTreeElement.parent && !this.selectedTreeElement.parent.root) {
handled = true;
if (this.selectedTreeElement.parent.selectable) {
nextSelectedElement = this.selectedTreeElement.parent;
handled = nextSelectedElement ? true : false;
} else if (this.selectedTreeElement.parent)
this.selectedTreeElement.parent.collapse();
}
} else if (event.keyIdentifier === "Right") {
if (!this.selectedTreeElement.revealed()) {
this.selectedTreeElement.reveal();
handled = true;
} else if (this.selectedTreeElement.hasChildren) {
handled = true;
if (this.selectedTreeElement.expanded) {
nextSelectedElement = this.selectedTreeElement.children[0];
handled = nextSelectedElement ? true : false;
} else {
if (event.altKey)
this.selectedTreeElement.expandRecursively();
else
this.selectedTreeElement.expand();
}
}
}
if (nextSelectedElement) {
nextSelectedElement.reveal();
nextSelectedElement.select();
}
if (handled) {
event.preventDefault();
event.stopPropagation();
}
return handled;
}
TreeOutline.prototype.expand = function()
{
// this is the root, do nothing
}
TreeOutline.prototype.collapse = function()
{
// this is the root, do nothing
}
TreeOutline.prototype.revealed = function()
{
return true;
}
TreeOutline.prototype.reveal = function()
{
// this is the root, do nothing
}
TreeOutline.prototype.appendChild = TreeOutline._appendChild;
TreeOutline.prototype.insertChild = TreeOutline._insertChild;
TreeOutline.prototype.removeChild = TreeOutline._removeChild;
TreeOutline.prototype.removeChildAtIndex = TreeOutline._removeChildAtIndex;
TreeOutline.prototype.removeChildren = TreeOutline._removeChildren;
TreeOutline.prototype.removeChildrenRecursive = TreeOutline._removeChildrenRecursive;
function TreeElement(title, representedObject, hasChildren)
{
this._title = title;
this.representedObject = (representedObject || {});
if (this.representedObject.__treeElementIdentifier)
this.identifier = this.representedObject.__treeElementIdentifier;
else {
this.identifier = TreeOutline._knownTreeElementNextIdentifier++;
this.representedObject.__treeElementIdentifier = this.identifier;
}
this._hidden = false;
this.expanded = false;
this.selected = false;
this.hasChildren = hasChildren;
this.children = [];
this.treeOutline = null;
this.parent = null;
this.previousSibling = null;
this.nextSibling = null;
this._listItemNode = null;
}
TreeElement.prototype = {
selectable: true,
arrowToggleWidth: 10,
get listItemElement() {
return this._listItemNode;
},
get childrenListElement() {
return this._childrenListNode;
},
get title() {
return this._title;
},
set title(x) {
this._title = x;
if (this._listItemNode)
this._listItemNode.innerHTML = x;
},
get tooltip() {
return this._tooltip;
},
set tooltip(x) {
this._tooltip = x;
if (this._listItemNode)
this._listItemNode.title = x ? x : "";
},
get hasChildren() {
return this._hasChildren;
},
set hasChildren(x) {
if (this._hasChildren === x)
return;
this._hasChildren = x;
if (!this._listItemNode)
return;
if (x)
this._listItemNode.addStyleClass("parent");
else {
this._listItemNode.removeStyleClass("parent");
this.collapse();
}
},
get hidden() {
return this._hidden;
},
set hidden(x) {
if (this._hidden === x)
return;
this._hidden = x;
if (x) {
if (this._listItemNode)
this._listItemNode.addStyleClass("hidden");
if (this._childrenListNode)
this._childrenListNode.addStyleClass("hidden");
} else {
if (this._listItemNode)
this._listItemNode.removeStyleClass("hidden");
if (this._childrenListNode)
this._childrenListNode.removeStyleClass("hidden");
}
},
get shouldRefreshChildren() {
return this._shouldRefreshChildren;
},
set shouldRefreshChildren(x) {
this._shouldRefreshChildren = x;
if (x && this.expanded)
this.expand();
}
}
TreeElement.prototype.appendChild = TreeOutline._appendChild;
TreeElement.prototype.insertChild = TreeOutline._insertChild;
TreeElement.prototype.removeChild = TreeOutline._removeChild;
TreeElement.prototype.removeChildAtIndex = TreeOutline._removeChildAtIndex;
TreeElement.prototype.removeChildren = TreeOutline._removeChildren;
TreeElement.prototype.removeChildrenRecursive = TreeOutline._removeChildrenRecursive;
TreeElement.prototype._attach = function()
{
if (!this._listItemNode || this.parent._shouldRefreshChildren) {
if (this._listItemNode && this._listItemNode.parentNode)
this._listItemNode.parentNode.removeChild(this._listItemNode);
this._listItemNode = this.treeOutline._childrenListNode.ownerDocument.createElement("li");
this._listItemNode.treeElement = this;
this._listItemNode.innerHTML = this._title;
this._listItemNode.title = this._tooltip ? this._tooltip : "";
if (this.hidden)
this._listItemNode.addStyleClass("hidden");
if (this.hasChildren)
this._listItemNode.addStyleClass("parent");
if (this.expanded)
this._listItemNode.addStyleClass("expanded");
if (this.selected)
this._listItemNode.addStyleClass("selected");
this._listItemNode.addEventListener("mousedown", TreeElement.treeElementSelected, false);
this._listItemNode.addEventListener("click", TreeElement.treeElementToggled, false);
this._listItemNode.addEventListener("dblclick", TreeElement.treeElementDoubleClicked, false);
if (this.onattach)
this.onattach(this);
}
var nextSibling = null;
if (this.nextSibling && this.nextSibling._listItemNode && this.nextSibling._listItemNode.parentNode === this.parent._childrenListNode)
nextSibling = this.nextSibling._listItemNode;
this.parent._childrenListNode.insertBefore(this._listItemNode, nextSibling);
if (this._childrenListNode)
this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling);
if (this.selected)
this.select();
if (this.expanded)
this.expand();
}
TreeElement.prototype._detach = function()
{
if (this._listItemNode && this._listItemNode.parentNode)
this._listItemNode.parentNode.removeChild(this._listItemNode);
if (this._childrenListNode && this._childrenListNode.parentNode)
this._childrenListNode.parentNode.removeChild(this._childrenListNode);
}
TreeElement.treeElementSelected = function(event)
{
var element = event.currentTarget;
if (!element || !element.treeElement || !element.treeElement.selectable)
return;
if (element.treeElement.isEventWithinDisclosureTriangle(event))
return;
element.treeElement.select();
}
TreeElement.treeElementToggled = function(event)
{
var element = event.currentTarget;
if (!element || !element.treeElement)
return;
if (!element.treeElement.isEventWithinDisclosureTriangle(event))
return;
if (element.treeElement.expanded) {
if (event.altKey)
element.treeElement.collapseRecursively();
else
element.treeElement.collapse();
} else {
if (event.altKey)
element.treeElement.expandRecursively();
else
element.treeElement.expand();
}
}
TreeElement.treeElementDoubleClicked = function(event)
{
var element = event.currentTarget;
if (!element || !element.treeElement)
return;
if (element.treeElement.ondblclick)
element.treeElement.ondblclick(element.treeElement, event);
else if (element.treeElement.hasChildren && !element.treeElement.expanded)
element.treeElement.expand();
}
TreeElement.prototype.collapse = function()
{
if (this._listItemNode)
this._listItemNode.removeStyleClass("expanded");
if (this._childrenListNode)
this._childrenListNode.removeStyleClass("expanded");
this.expanded = false;
if (this.treeOutline)
this.treeOutline._treeElementsExpandedState[this.identifier] = true;
if (this.oncollapse)
this.oncollapse(this);
}
TreeElement.prototype.collapseRecursively = function()
{
var item = this;
while (item) {
if (item.expanded)
item.collapse();
item = item.traverseNextTreeElement(false, this, true);
}
}
TreeElement.prototype.expand = function()
{
if (!this.hasChildren || (this.expanded && !this._shouldRefreshChildren && this._childrenListNode))
return;
if (this.treeOutline && (!this._childrenListNode || this._shouldRefreshChildren)) {
if (this._childrenListNode && this._childrenListNode.parentNode)
this._childrenListNode.parentNode.removeChild(this._childrenListNode);
this._childrenListNode = this.treeOutline._childrenListNode.ownerDocument.createElement("ol");
this._childrenListNode.parentTreeElement = this;
this._childrenListNode.addStyleClass("children");
if (this.hidden)
this._childrenListNode.addStyleClass("hidden");
if (this.onpopulate)
this.onpopulate(this);
for (var i = 0; i < this.children.length; ++i)
this.children[i]._attach();
delete this._shouldRefreshChildren;
}
if (this._listItemNode) {
this._listItemNode.addStyleClass("expanded");
if (this._childrenListNode && this._childrenListNode.parentNode != this._listItemNode.parentNode)
this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling);
}
if (this._childrenListNode)
this._childrenListNode.addStyleClass("expanded");
this.expanded = true;
if (this.treeOutline)
this.treeOutline._treeElementsExpandedState[this.identifier] = true;
if (this.onexpand)
this.onexpand(this);
}
TreeElement.prototype.expandRecursively = function(maxDepth)
{
var item = this;
var info = {};
var depth = 0;
// The Inspector uses TreeOutlines to represents object properties, so recursive expansion
// in some case can be infinite, since JavaScript objects can hold circular references.
// So default to a recursion cap of 3 levels, since that gives fairly good results.
if (typeof maxDepth === "undefined" || typeof maxDepth === "null")
maxDepth = 3;
while (item) {
if (depth < maxDepth)
item.expand();
item = item.traverseNextTreeElement(false, this, (depth >= maxDepth), info);
depth += info.depthChange;
}
}
TreeElement.prototype.hasAncestor = function(ancestor) {
if (!ancestor)
return false;
var currentNode = this.parent;
while (currentNode) {
if (ancestor === currentNode)
return true;
currentNode = currentNode.parent;
}
return false;
}
TreeElement.prototype.reveal = function()
{
var currentAncestor = this.parent;
while (currentAncestor && !currentAncestor.root) {
if (!currentAncestor.expanded)
currentAncestor.expand();
currentAncestor = currentAncestor.parent;
}
if (this.onreveal)
this.onreveal(this);
}
TreeElement.prototype.revealed = function()
{
var currentAncestor = this.parent;
while (currentAncestor && !currentAncestor.root) {
if (!currentAncestor.expanded)
return false;
currentAncestor = currentAncestor.parent;
}
return true;
}
TreeElement.prototype.select = function(supressOnSelect)
{
if (!this.treeOutline || !this.selectable || this.selected)
return;
if (this.treeOutline.selectedTreeElement)
this.treeOutline.selectedTreeElement.deselect();
this.selected = true;
this.treeOutline.selectedTreeElement = this;
if (this._listItemNode)
this._listItemNode.addStyleClass("selected");
if (this.onselect && !supressOnSelect)
this.onselect(this);
}
TreeElement.prototype.deselect = function(supressOnDeselect)
{
if (!this.treeOutline || this.treeOutline.selectedTreeElement !== this || !this.selected)
return;
this.selected = false;
this.treeOutline.selectedTreeElement = null;
if (this._listItemNode)
this._listItemNode.removeStyleClass("selected");
if (this.ondeselect && !supressOnDeselect)
this.ondeselect(this);
}
TreeElement.prototype.traverseNextTreeElement = function(skipHidden, stayWithin, dontPopulate, info)
{
if (!dontPopulate && this.hasChildren && this.onpopulate)
this.onpopulate(this);
if (info)
info.depthChange = 0;
var element = skipHidden ? (this.revealed() ? this.children[0] : null) : this.children[0];
if (element && (!skipHidden || (skipHidden && this.expanded))) {
if (info)
info.depthChange = 1;
return element;
}
if (this === stayWithin)
return null;
element = skipHidden ? (this.revealed() ? this.nextSibling : null) : this.nextSibling;
if (element)
return element;
element = this;
while (element && !element.root && !(skipHidden ? (element.revealed() ? element.nextSibling : null) : element.nextSibling) && element.parent !== stayWithin) {
if (info)
info.depthChange -= 1;
element = element.parent;
}
if (!element)
return null;
return (skipHidden ? (element.revealed() ? element.nextSibling : null) : element.nextSibling);
}
TreeElement.prototype.traversePreviousTreeElement = function(skipHidden, dontPopulate)
{
var element = skipHidden ? (this.revealed() ? this.previousSibling : null) : this.previousSibling;
if (!dontPopulate && element && element.hasChildren && element.onpopulate)
element.onpopulate(element);
while (element && (skipHidden ? (element.revealed() && element.expanded ? element.children[element.children.length - 1] : null) : element.children[element.children.length - 1])) {
if (!dontPopulate && element.hasChildren && element.onpopulate)
element.onpopulate(element);
element = (skipHidden ? (element.revealed() && element.expanded ? element.children[element.children.length - 1] : null) : element.children[element.children.length - 1]);
}
if (element)
return element;
if (!this.parent || this.parent.root)
return null;
return this.parent;
}
TreeElement.prototype.isEventWithinDisclosureTriangle = function(event)
{
var left = this._listItemNode.totalOffsetLeft;
return event.pageX >= left && event.pageX <= left + this.arrowToggleWidth && this.hasChildren;
}
/* inspector.js */
/*
* Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
* Copyright (C) 2007 Matt Lilek (pewtermoose@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 Preferences = {
ignoreWhitespace: true,
showUserAgentStyles: true,
maxInlineTextChildLength: 80,
minConsoleHeight: 75,
minSidebarWidth: 100,
minElementsSidebarWidth: 200,
minScriptsSidebarWidth: 200,
showInheritedComputedStyleProperties: false,
showMissingLocalizedStrings: false
}
var WebInspector = {
resources: [],
resourceURLMap: {},
missingLocalizedStrings: {},
get previousFocusElement()
{
return this._previousFocusElement;
},
get currentFocusElement()
{
return this._currentFocusElement;
},
set currentFocusElement(x)
{
if (this._currentFocusElement !== x)
this._previousFocusElement = this._currentFocusElement;
this._currentFocusElement = x;
if (this._currentFocusElement) {
this._currentFocusElement.focus();
// Make a caret selection inside the new element if there isn't a range selection and
// there isn't already a caret selection inside.
var selection = window.getSelection();
if (selection.isCollapsed && !this._currentFocusElement.isInsertionCaretInside()) {
var selectionRange = document.createRange();
selectionRange.setStart(this._currentFocusElement, 0);
selectionRange.setEnd(this._currentFocusElement, 0);
selection.removeAllRanges();
selection.addRange(selectionRange);
}
} else if (this._previousFocusElement)
this._previousFocusElement.blur();
},
get currentPanel()
{
return this._currentPanel;
},
set currentPanel(x)
{
if (this._currentPanel === x)
return;
if (this._currentPanel)
this._currentPanel.hide();
this._currentPanel = x;
this.updateSearchLabel();
if (x) {
x.show();
if (this.currentQuery) {
if (x.performSearch) {
function performPanelSearch()
{
this.updateSearchMatchesCount();
x.currentQuery = this.currentQuery;
x.performSearch(this.currentQuery);
}
// Perform the search on a timeout so the panel switches fast.
setTimeout(performPanelSearch.bind(this), 0);
} else {
// Update to show Not found for panels that can't be searched.
this.updateSearchMatchesCount();
}
}
}
},
get attached()
{
return this._attached;
},
set attached(x)
{
if (this._attached === x)
return;
this._attached = x;
this.updateSearchLabel();
var dockToggleButton = document.getElementById("dock-status-bar-item");
var body = document.body;
if (x) {
InspectorController.attach();
body.removeStyleClass("detached");
body.addStyleClass("attached");
dockToggleButton.title = WebInspector.UIString("Undock into separate window.");
} else {
InspectorController.detach();
body.removeStyleClass("attached");
body.addStyleClass("detached");
dockToggleButton.title = WebInspector.UIString("Dock to main window.");
}
},
get errors()
{
return this._errors || 0;
},
set errors(x)
{
x = Math.max(x, 0);
if (this._errors === x)
return;
this._errors = x;
this._updateErrorAndWarningCounts();
},
get warnings()
{
return this._warnings || 0;
},
set warnings(x)
{
x = Math.max(x, 0);
if (this._warnings === x)
return;
this._warnings = x;
this._updateErrorAndWarningCounts();
},
_updateErrorAndWarningCounts: function()
{
var errorWarningElement = document.getElementById("error-warning-count");
if (!errorWarningElement)
return;
if (!this.errors && !this.warnings) {
errorWarningElement.addStyleClass("hidden");
return;
}
errorWarningElement.removeStyleClass("hidden");
errorWarningElement.removeChildren();
if (this.errors) {
var errorElement = document.createElement("span");
errorElement.id = "error-count";
errorElement.textContent = this.errors;
errorWarningElement.appendChild(errorElement);
}
if (this.warnings) {
var warningsElement = document.createElement("span");
warningsElement.id = "warning-count";
warningsElement.textContent = this.warnings;
errorWarningElement.appendChild(warningsElement);
}
if (this.errors) {
if (this.warnings) {
if (this.errors == 1) {
if (this.warnings == 1)
errorWarningElement.title = WebInspector.UIString("%d error, %d warning", this.errors, this.warnings);
else
errorWarningElement.title = WebInspector.UIString("%d error, %d warnings", this.errors, this.warnings);
} else if (this.warnings == 1)
errorWarningElement.title = WebInspector.UIString("%d errors, %d warning", this.errors, this.warnings);
else
errorWarningElement.title = WebInspector.UIString("%d errors, %d warnings", this.errors, this.warnings);
} else if (this.errors == 1)
errorWarningElement.title = WebInspector.UIString("%d error", this.errors);
else
errorWarningElement.title = WebInspector.UIString("%d errors", this.errors);
} else if (this.warnings == 1)
errorWarningElement.title = WebInspector.UIString("%d warning", this.warnings);
else if (this.warnings)
errorWarningElement.title = WebInspector.UIString("%d warnings", this.warnings);
else
errorWarningElement.title = null;
},
get hoveredDOMNode()
{
return this._hoveredDOMNode;
},
set hoveredDOMNode(x)
{
if (objectsAreSame(this._hoveredDOMNode, x))
return;
this._hoveredDOMNode = x;
if (this._hoveredDOMNode)
this._updateHoverHighlightSoon(this.showingDOMNodeHighlight ? 50 : 500);
else
this._updateHoverHighlight();
},
_updateHoverHighlightSoon: function(delay)
{
if ("_updateHoverHighlightTimeout" in this)
clearTimeout(this._updateHoverHighlightTimeout);
this._updateHoverHighlightTimeout = setTimeout(this._updateHoverHighlight.bind(this), delay);
},
_updateHoverHighlight: function()
{
if ("_updateHoverHighlightTimeout" in this) {
clearTimeout(this._updateHoverHighlightTimeout);
delete this._updateHoverHighlightTimeout;
}
if (this._hoveredDOMNode) {
InspectorController.highlightDOMNode(this._hoveredDOMNode);
this.showingDOMNodeHighlight = true;
} else {
InspectorController.hideDOMNodeHighlight();
this.showingDOMNodeHighlight = false;
}
}
}
WebInspector.loaded = function()
{
var platform = InspectorController.platform();
document.body.addStyleClass("platform-" + platform);
this.console = new WebInspector.Console();
this.panels = {
elements: new WebInspector.ElementsPanel(),
resources: new WebInspector.ResourcesPanel(),
scripts: new WebInspector.ScriptsPanel(),
profiles: new WebInspector.ProfilesPanel(),
databases: new WebInspector.DatabasesPanel()
};
var toolbarElement = document.getElementById("toolbar");
var previousToolbarItem = toolbarElement.children[0];
for (var panelName in this.panels) {
var panel = this.panels[panelName];
var panelToolbarItem = panel.toolbarItem;
panelToolbarItem.addEventListener("click", this._toolbarItemClicked.bind(this));
if (previousToolbarItem)
toolbarElement.insertBefore(panelToolbarItem, previousToolbarItem.nextSibling);
else
toolbarElement.insertBefore(panelToolbarItem, toolbarElement.firstChild);
previousToolbarItem = panelToolbarItem;
}
this.currentPanel = this.panels.elements;
this.resourceCategories = {
documents: new WebInspector.ResourceCategory(WebInspector.UIString("Documents"), "documents"),
stylesheets: new WebInspector.ResourceCategory(WebInspector.UIString("Stylesheets"), "stylesheets"),
images: new WebInspector.ResourceCategory(WebInspector.UIString("Images"), "images"),
scripts: new WebInspector.ResourceCategory(WebInspector.UIString("Scripts"), "scripts"),
xhr: new WebInspector.ResourceCategory(WebInspector.UIString("XHR"), "xhr"),
fonts: new WebInspector.ResourceCategory(WebInspector.UIString("Fonts"), "fonts"),
other: new WebInspector.ResourceCategory(WebInspector.UIString("Other"), "other")
};
this.Tips = {
ResourceNotCompressed: {id: 0, message: WebInspector.UIString("You could save bandwidth by having your web server compress this transfer with gzip or zlib.")}
};
this.Warnings = {
IncorrectMIMEType: {id: 0, message: WebInspector.UIString("Resource interpreted as %s but transferred with MIME type %s.")}
};
this.addMainEventListeners(document);
window.addEventListener("unload", this.windowUnload.bind(this), true);
window.addEventListener("resize", this.windowResize.bind(this), true);
document.addEventListener("focus", this.focusChanged.bind(this), true);
document.addEventListener("keydown", this.documentKeyDown.bind(this), true);
document.addEventListener("keyup", this.documentKeyUp.bind(this), true);
document.addEventListener("beforecopy", this.documentCanCopy.bind(this), true);
document.addEventListener("copy", this.documentCopy.bind(this), true);
var mainPanelsElement = document.getElementById("main-panels");
mainPanelsElement.handleKeyEvent = this.mainKeyDown.bind(this);
mainPanelsElement.handleKeyUpEvent = this.mainKeyUp.bind(this);
mainPanelsElement.handleCopyEvent = this.mainCopy.bind(this);
// Focus the mainPanelsElement in a timeout so it happens after the initial focus,
// so it doesn't get reset to the first toolbar button. This initial focus happens
// on Mac when the window is made key and the WebHTMLView becomes the first responder.
setTimeout(function() { WebInspector.currentFocusElement = mainPanelsElement }, 0);
var dockToggleButton = document.getElementById("dock-status-bar-item");
dockToggleButton.addEventListener("click", this.toggleAttach.bind(this), false);
if (this.attached)
dockToggleButton.title = WebInspector.UIString("Undock into separate window.");
else
dockToggleButton.title = WebInspector.UIString("Dock to main window.");
var errorWarningCount = document.getElementById("error-warning-count");
errorWarningCount.addEventListener("click", this.console.show.bind(this.console), false);
this._updateErrorAndWarningCounts();
var searchField = document.getElementById("search");
searchField.addEventListener("keydown", this.searchKeyDown.bind(this), false);
searchField.addEventListener("keyup", this.searchKeyUp.bind(this), false);
searchField.addEventListener("search", this.performSearch.bind(this), false); // when the search is emptied
document.getElementById("toolbar").addEventListener("mousedown", this.toolbarDragStart, true);
document.getElementById("close-button").addEventListener("click", this.close, true);
InspectorController.loaded();
}
var windowLoaded = function()
{
var localizedStringsURL = InspectorController.localizedStringsURL();
if (localizedStringsURL) {
var localizedStringsScriptElement = document.createElement("script");
localizedStringsScriptElement.addEventListener("load", WebInspector.loaded.bind(WebInspector), false);
localizedStringsScriptElement.type = "text/javascript";
localizedStringsScriptElement.src = localizedStringsURL;
document.getElementsByTagName("head").item(0).appendChild(localizedStringsScriptElement);
} else
WebInspector.loaded();
window.removeEventListener("load", windowLoaded, false);
delete windowLoaded;
};
window.addEventListener("load", windowLoaded, false);
WebInspector.windowUnload = function(event)
{
InspectorController.windowUnloading();
}
WebInspector.windowResize = function(event)
{
if (this.currentPanel && this.currentPanel.resize)
this.currentPanel.resize();
}
WebInspector.windowFocused = function(event)
{
if (event.target.nodeType === Node.DOCUMENT_NODE)
document.body.removeStyleClass("inactive");
}
WebInspector.windowBlured = function(event)
{
if (event.target.nodeType === Node.DOCUMENT_NODE)
document.body.addStyleClass("inactive");
}
WebInspector.focusChanged = function(event)
{
this.currentFocusElement = event.target;
}
WebInspector.setAttachedWindow = function(attached)
{
this.attached = attached;
}
WebInspector.close = function(event)
{
InspectorController.closeWindow();
}
WebInspector.documentClick = function(event)
{
var anchor = event.target.enclosingNodeOrSelfWithNodeName("a");
if (!anchor)
return;
// Prevent the link from navigating, since we don't do any navigation by following links normally.
event.preventDefault();
function followLink()
{
// FIXME: support webkit-html-external-link links here.
if (anchor.href in WebInspector.resourceURLMap) {
if (anchor.hasStyleClass("webkit-html-external-link")) {
anchor.removeStyleClass("webkit-html-external-link");
anchor.addStyleClass("webkit-html-resource-link");
}
WebInspector.showResourceForURL(anchor.href, anchor.lineNumber, anchor.preferredPanel);
} else {
console.log("followLink");
var profileStringRegEx = new RegExp("webkit-profile://.+/([0-9]+)");
var profileString = profileStringRegEx.exec(anchor.href);
if (profileString)
WebInspector.showProfileById(profileString[1])
}
}
if (WebInspector.followLinkTimeout)
clearTimeout(WebInspector.followLinkTimeout);
if (anchor.preventFollowOnDoubleClick) {
// Start a timeout if this is the first click, if the timeout is canceled
// before it fires, then a double clicked happened or another link was clicked.
if (event.detail === 1)
WebInspector.followLinkTimeout = setTimeout(followLink, 333);
return;
}
followLink();
}
WebInspector.documentKeyDown = function(event)
{
if (!this.currentFocusElement)
return;
if (this.currentFocusElement.handleKeyEvent)
this.currentFocusElement.handleKeyEvent(event);
else if (this.currentFocusElement.id && this.currentFocusElement.id.length && WebInspector[this.currentFocusElement.id + "KeyDown"])
WebInspector[this.currentFocusElement.id + "KeyDown"](event);
if (!event.handled) {
var isMac = InspectorController.platform().indexOf("mac-") === 0;
switch (event.keyIdentifier) {
case "U+001B": // Escape key
this.console.visible = !this.console.visible;
event.preventDefault();
break;
case "U+0046": // F key
if (isMac)
var isFindKey = event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey;
else
var isFindKey = event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey;
if (isFindKey) {
var searchField = document.getElementById("search");
searchField.focus();
searchField.select();
event.preventDefault();
}
break;
case "U+0047": // G key
if (isMac)
var isFindAgainKey = event.metaKey && !event.ctrlKey && !event.altKey;
else
var isFindAgainKey = event.ctrlKey && !event.metaKey && !event.altKey;
if (isFindAgainKey) {
if (event.shiftKey) {
if (this.currentPanel.jumpToPreviousSearchResult)
this.currentPanel.jumpToPreviousSearchResult();
} else if (this.currentPanel.jumpToNextSearchResult)
this.currentPanel.jumpToNextSearchResult();
event.preventDefault();
}
break;
}
}
}
WebInspector.documentKeyUp = function(event)
{
if (!this.currentFocusElement || !this.currentFocusElement.handleKeyUpEvent)
return;
this.currentFocusElement.handleKeyUpEvent(event);
}
WebInspector.documentCanCopy = function(event)
{
if (!this.currentFocusElement)
return;
// Calling preventDefault() will say "we support copying, so enable the Copy menu".
if (this.currentFocusElement.handleCopyEvent)
event.preventDefault();
else if (this.currentFocusElement.id && this.currentFocusElement.id.length && WebInspector[this.currentFocusElement.id + "Copy"])
event.preventDefault();
}
WebInspector.documentCopy = function(event)
{
if (!this.currentFocusElement)
return;
if (this.currentFocusElement.handleCopyEvent)
this.currentFocusElement.handleCopyEvent(event);
else if (this.currentFocusElement.id && this.currentFocusElement.id.length && WebInspector[this.currentFocusElement.id + "Copy"])
WebInspector[this.currentFocusElement.id + "Copy"](event);
}
WebInspector.mainKeyDown = function(event)
{
if (this.currentPanel && this.currentPanel.handleKeyEvent)
this.currentPanel.handleKeyEvent(event);
}
WebInspector.mainKeyUp = function(event)
{
if (this.currentPanel && this.currentPanel.handleKeyUpEvent)
this.currentPanel.handleKeyUpEvent(event);
}
WebInspector.mainCopy = function(event)
{
if (this.currentPanel && this.currentPanel.handleCopyEvent)
this.currentPanel.handleCopyEvent(event);
}
WebInspector.animateStyle = function(animations, duration, callback, complete)
{
if (complete === undefined)
complete = 0;
var slice = (1000 / 30); // 30 frames per second
var defaultUnit = "px";
var propertyUnit = {opacity: ""};
for (var i = 0; i < animations.length; ++i) {
var animation = animations[i];
var element = null;
var start = null;
var current = null;
var end = null;
for (key in animation) {
if (key === "element")
element = animation[key];
else if (key === "start")
start = animation[key];
else if (key === "current")
current = animation[key];
else if (key === "end")
end = animation[key];
}
if (!element || !end)
continue;
var computedStyle = element.ownerDocument.defaultView.getComputedStyle(element);
if (!start) {
start = {};
for (key in end)
start[key] = parseInt(computedStyle.getPropertyValue(key));
animation.start = start;
} else if (complete == 0)
for (key in start)
element.style.setProperty(key, start[key] + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
if (!current) {
current = {};
for (key in start)
current[key] = start[key];
animation.current = current;
}
function cubicInOut(t, b, c, d)
{
if ((t/=d/2) < 1) return c/2*t*t*t + b;
return c/2*((t-=2)*t*t + 2) + b;
}
var style = element.style;
for (key in end) {
var startValue = start[key];
var currentValue = current[key];
var endValue = end[key];
if ((complete + slice) < duration) {
var delta = (endValue - startValue) / (duration / slice);
var newValue = cubicInOut(complete, startValue, endValue - startValue, duration);
style.setProperty(key, newValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
current[key] = newValue;
} else {
style.setProperty(key, endValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
}
}
}
if (complete < duration)
setTimeout(WebInspector.animateStyle, slice, animations, duration, callback, complete + slice);
else if (callback)
callback();
}
WebInspector.updateSearchLabel = function()
{
if (!this.currentPanel)
return;
var newLabel = WebInspector.UIString("Search %s", this.currentPanel.toolbarItemLabel);
if (this.attached)
document.getElementById("search").setAttribute("placeholder", newLabel);
else {
document.getElementById("search").removeAttribute("placeholder");
document.getElementById("search-toolbar-label").textContent = newLabel;
}
}
WebInspector.toggleAttach = function()
{
this.attached = !this.attached;
}
WebInspector.toolbarDragStart = function(event)
{
if (!WebInspector.attached && InspectorController.platform() !== "mac-leopard")
return;
var target = event.target;
if (target.hasStyleClass("toolbar-item") && target.hasStyleClass("toggleable"))
return;
var toolbar = document.getElementById("toolbar");
if (target !== toolbar && !target.hasStyleClass("toolbar-item"))
return;
toolbar.lastScreenX = event.screenX;
toolbar.lastScreenY = event.screenY;
WebInspector.elementDragStart(toolbar, WebInspector.toolbarDrag, WebInspector.toolbarDragEnd, event, (WebInspector.attached ? "row-resize" : "default"));
}
WebInspector.toolbarDragEnd = function(event)
{
var toolbar = document.getElementById("toolbar");
WebInspector.elementDragEnd(event);
delete toolbar.lastScreenX;
delete toolbar.lastScreenY;
}
WebInspector.toolbarDrag = function(event)
{
var toolbar = document.getElementById("toolbar");
if (WebInspector.attached) {
var height = window.innerHeight - (event.screenY - toolbar.lastScreenY);
InspectorController.setAttachedWindowHeight(height);
} else {
var x = event.screenX - toolbar.lastScreenX;
var y = event.screenY - toolbar.lastScreenY;
// We cannot call window.moveBy here because it restricts the movement
// of the window at the edges.
InspectorController.moveByUnrestricted(x, y);
}
toolbar.lastScreenX = event.screenX;
toolbar.lastScreenY = event.screenY;
event.preventDefault();
}
WebInspector.elementDragStart = function(element, dividerDrag, elementDragEnd, event, cursor)
{
if (this._elementDraggingEventListener || this._elementEndDraggingEventListener)
this.elementDragEnd(event);
this._elementDraggingEventListener = dividerDrag;
this._elementEndDraggingEventListener = elementDragEnd;
document.addEventListener("mousemove", dividerDrag, true);
document.addEventListener("mouseup", elementDragEnd, true);
document.body.style.cursor = cursor;
event.preventDefault();
}
WebInspector.elementDragEnd = function(event)
{
document.removeEventListener("mousemove", this._elementDraggingEventListener, true);
document.removeEventListener("mouseup", this._elementEndDraggingEventListener, true);
document.body.style.removeProperty("cursor");
delete this._elementDraggingEventListener;
delete this._elementEndDraggingEventListener;
event.preventDefault();
}
WebInspector.showConsole = function()
{
this.console.show();
}
WebInspector.showElementsPanel = function()
{
this.currentPanel = this.panels.elements;
}
WebInspector.showResourcesPanel = function()
{
this.currentPanel = this.panels.resources;
}
WebInspector.showScriptsPanel = function()
{
this.currentPanel = this.panels.scripts;
}
WebInspector.showProfilesPanel = function()
{
this.currentPanel = this.panels.profiles;
}
WebInspector.showDatabasesPanel = function()
{
this.currentPanel = this.panels.databases;
}
WebInspector.addResource = function(resource)
{
this.resources.push(resource);
this.resourceURLMap[resource.url] = resource;
if (resource.mainResource) {
this.mainResource = resource;
this.panels.elements.reset();
}
this.panels.resources.addResource(resource);
}
WebInspector.removeResource = function(resource)
{
resource.category.removeResource(resource);
delete this.resourceURLMap[resource.url];
this.resources.remove(resource, true);
this.panels.resources.removeResource(resource);
}
WebInspector.addDatabase = function(database)
{
this.panels.databases.addDatabase(database);
}
WebInspector.debuggerAttached = function()
{
this.panels.scripts.debuggerAttached();
}
WebInspector.debuggerDetached = function()
{
this.panels.scripts.debuggerDetached();
}
WebInspector.parsedScriptSource = function(sourceID, sourceURL, source, startingLine)
{
this.panels.scripts.addScript(sourceID, sourceURL, source, startingLine);
}
WebInspector.failedToParseScriptSource = function(sourceURL, source, startingLine, errorLine, errorMessage)
{
this.panels.scripts.addScript(null, sourceURL, source, startingLine, errorLine, errorMessage);
}
WebInspector.pausedScript = function()
{
this.panels.scripts.debuggerPaused();
}
WebInspector.populateInterface = function()
{
for (var panelName in this.panels) {
var panel = this.panels[panelName];
if ("populateInterface" in panel)
panel.populateInterface();
}
}
WebInspector.reset = function()
{
for (var panelName in this.panels) {
var panel = this.panels[panelName];
if ("reset" in panel)
panel.reset();
}
for (var category in this.resourceCategories)
this.resourceCategories[category].removeAllResources();
this.resources = [];
this.resourceURLMap = {};
this.hoveredDOMNode = null;
delete this.mainResource;
this.console.clearMessages();
}
WebInspector.inspectedWindowCleared = function(inspectedWindow)
{
this.panels.elements.inspectedWindowCleared(inspectedWindow);
}
WebInspector.resourceURLChanged = function(resource, oldURL)
{
delete this.resourceURLMap[oldURL];
this.resourceURLMap[resource.url] = resource;
}
WebInspector.addMessageToConsole = function(msg)
{
this.console.addMessage(msg);
}
WebInspector.addProfile = function(profile)
{
this.panels.profiles.addProfile(profile);
}
WebInspector.setRecordingProfile = function(isProfiling)
{
this.panels.profiles.setRecordingProfile(isProfiling);
}
WebInspector.drawLoadingPieChart = function(canvas, percent) {
var g = canvas.getContext("2d");
var darkColor = "rgb(122, 168, 218)";
var lightColor = "rgb(228, 241, 251)";
var cx = 8;
var cy = 8;
var r = 7;
g.beginPath();
g.arc(cx, cy, r, 0, Math.PI * 2, false);
g.closePath();
g.lineWidth = 1;
g.strokeStyle = darkColor;
g.fillStyle = lightColor;
g.fill();
g.stroke();
var startangle = -Math.PI / 2;
var endangle = startangle + (percent * Math.PI * 2);
g.beginPath();
g.moveTo(cx, cy);
g.arc(cx, cy, r, startangle, endangle, false);
g.closePath();
g.fillStyle = darkColor;
g.fill();
}
WebInspector.updateFocusedNode = function(node)
{
if (!node)
// FIXME: Should we deselect if null is passed in?
return;
this.currentPanel = this.panels.elements;
this.panels.elements.focusedDOMNode = node;
}
WebInspector.displayNameForURL = function(url)
{
if (!url)
return "";
var resource = this.resourceURLMap[url];
if (resource)
return resource.displayName;
return url.trimURL(WebInspector.mainResource ? WebInspector.mainResource.domain : "");
}
WebInspector.resourceForURL = function(url)
{
if (url in this.resourceURLMap)
return this.resourceURLMap[url];
// No direct match found. Search for resources that contain
// a substring of the URL.
for (var resourceURL in this.resourceURLMap) {
if (resourceURL.hasSubstring(url))
return this.resourceURLMap[resourceURL];
}
return null;
}
WebInspector.showResourceForURL = function(url, line, preferredPanel)
{
var resource = this.resourceForURL(url);
if (!resource)
return false;
if (preferredPanel && preferredPanel in WebInspector.panels) {
var panel = this.panels[preferredPanel];
if (!("showResource" in panel))
panel = null;
else if ("canShowResource" in panel && !panel.canShowResource(resource))
panel = null;
}
this.currentPanel = panel || this.panels.resources;
this.currentPanel.showResource(resource, line);
return true;
}
WebInspector.linkifyStringAsFragment = function(string)
{
var container = document.createDocumentFragment();
var linkStringRegEx = new RegExp("(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}://|www\\.)[\\w$\\-_+*'=\\|/\\\\(){}[\\]%@&#~,:;.!?]{2,}[\\w$\\-_+*=\\|/\\\\({%@&#~]");
while (string) {
var linkString = linkStringRegEx.exec(string);
if (!linkString)
break;
linkString = linkString[0];
var title = linkString;
var linkIndex = string.indexOf(linkString);
var nonLink = string.substring(0, linkIndex);
container.appendChild(document.createTextNode(nonLink));
var profileStringRegEx = new RegExp("webkit-profile://(.+)/[0-9]+");
var profileStringMatches = profileStringRegEx.exec(title);
var profileTitle;
if (profileStringMatches)
profileTitle = profileStringMatches[1];
if (profileTitle)
title = WebInspector.panels.profiles.displayTitleForProfileLink(profileTitle);
var realURL = (linkString.indexOf("www.") === 0 ? "http://" + linkString : linkString);
container.appendChild(WebInspector.linkifyURLAsNode(realURL, title, null, (realURL in WebInspector.resourceURLMap)));
string = string.substring(linkIndex + linkString.length, string.length);
}
if (string)
container.appendChild(document.createTextNode(string));
return container;
}
WebInspector.showProfileById = function(uid) {
WebInspector.showProfilesPanel();
WebInspector.panels.profiles.showProfileById(uid);
}
WebInspector.linkifyURLAsNode = function(url, linkText, classes, isExternal)
{
if (!linkText)
linkText = url;
classes = (classes ? classes + " " : "");
classes += isExternal ? "webkit-html-external-link" : "webkit-html-resource-link";
var a = document.createElement("a");
a.href = url;
a.className = classes;
a.title = url;
a.target = "_blank";
a.textContent = linkText;
return a;
}
WebInspector.linkifyURL = function(url, linkText, classes, isExternal)
{
// Use the DOM version of this function so as to avoid needing to escape attributes.
// FIXME: Get rid of linkifyURL entirely.
return WebInspector.linkifyURLAsNode(url, linkText, classes, isExternal).outerHTML;
}
WebInspector.addMainEventListeners = function(doc)
{
doc.defaultView.addEventListener("focus", this.windowFocused.bind(this), true);
doc.defaultView.addEventListener("blur", this.windowBlured.bind(this), true);
doc.addEventListener("click", this.documentClick.bind(this), true);
}
WebInspector.searchKeyDown = function(event)
{
if (event.keyIdentifier !== "Enter")
return;
// Call preventDefault since this was the Enter key. This prevents a "search" event
// from firing for key down. We handle the Enter key on key up in searchKeyUp. This
// stops performSearch from being called twice in a row.
event.preventDefault();
}
WebInspector.searchKeyUp = function(event)
{
if (event.keyIdentifier !== "Enter")
return;
// Select all of the text so the user can easily type an entirely new query.
event.target.select();
// Only call performSearch if the Enter key was pressed. Otherwise the search
// performance is poor because of searching on every key. The search field has
// the incremental attribute set, so we still get incremental searches.
this.performSearch(event);
}
WebInspector.performSearch = function(event)
{
var query = event.target.value;
var forceSearch = event.keyIdentifier === "Enter";
if (!query || !query.length || (!forceSearch && query.length < 3)) {
delete this.currentQuery;
for (var panelName in this.panels) {
var panel = this.panels[panelName];
if (panel.currentQuery && panel.searchCanceled)
panel.searchCanceled();
delete panel.currentQuery;
}
this.updateSearchMatchesCount();
return;
}
if (query === this.currentPanel.currentQuery && this.currentPanel.currentQuery === this.currentQuery) {
// When this is the same query and a forced search, jump to the next
// search result for a good user experience.
if (forceSearch && this.currentPanel.jumpToNextSearchResult)
this.currentPanel.jumpToNextSearchResult();
return;
}
this.currentQuery = query;
this.updateSearchMatchesCount();
if (!this.currentPanel.performSearch)
return;
this.currentPanel.currentQuery = query;
this.currentPanel.performSearch(query);
}
WebInspector.updateSearchMatchesCount = function(matches, panel)
{
if (!panel)
panel = this.currentPanel;
panel.currentSearchMatches = matches;
if (panel !== this.currentPanel)
return;
if (!this.currentPanel.currentQuery) {
document.getElementById("search-results-matches").addStyleClass("hidden");
return;
}
if (matches) {
if (matches === 1)
var matchesString = WebInspector.UIString("1 match");
else
var matchesString = WebInspector.UIString("%d matches", matches);
} else
var matchesString = WebInspector.UIString("Not Found");
var matchesToolbarElement = document.getElementById("search-results-matches");
matchesToolbarElement.removeStyleClass("hidden");
matchesToolbarElement.textContent = matchesString;
}
WebInspector.UIString = function(string)
{
if (window.localizedStrings && string in window.localizedStrings)
string = window.localizedStrings[string];
else {
if (!(string in this.missingLocalizedStrings)) {
console.error("Localized string \"" + string + "\" not found.");
this.missingLocalizedStrings[string] = true;
}
if (Preferences.showMissingLocalizedStrings)
string += " (not localized)";
}
return String.vsprintf(string, Array.prototype.slice.call(arguments, 1));
}
WebInspector.isBeingEdited = function(element)
{
return element.__editing;
}
WebInspector.startEditing = function(element, committedCallback, cancelledCallback, context)
{
if (element.__editing)
return;
element.__editing = true;
var oldText = element.textContent;
var oldHandleKeyEvent = element.handleKeyEvent;
element.addStyleClass("editing");
var oldTabIndex = element.tabIndex;
if (element.tabIndex < 0)
element.tabIndex = 0;
function blurEventListener() {
editingCommitted.call(element);
}
function cleanUpAfterEditing() {
delete this.__editing;
this.removeStyleClass("editing");
this.tabIndex = oldTabIndex;
this.scrollTop = 0;
this.scrollLeft = 0;
this.handleKeyEvent = oldHandleKeyEvent;
element.removeEventListener("blur", blurEventListener, false);
if (element === WebInspector.currentFocusElement || element.isAncestor(WebInspector.currentFocusElement))
WebInspector.currentFocusElement = WebInspector.previousFocusElement;
}
function editingCancelled() {
this.innerText = oldText;
cleanUpAfterEditing.call(this);
cancelledCallback(this, context);
}
function editingCommitted() {
cleanUpAfterEditing.call(this);
committedCallback(this, this.textContent, oldText, context);
}
element.handleKeyEvent = function(event) {
if (oldHandleKeyEvent)
oldHandleKeyEvent(event);
if (event.handled)
return;
if (event.keyIdentifier === "Enter") {
editingCommitted.call(element);
event.preventDefault();
} else if (event.keyCode === 27) { // Escape key
editingCancelled.call(element);
event.preventDefault();
event.handled = true;
}
}
element.addEventListener("blur", blurEventListener, false);
WebInspector.currentFocusElement = element;
}
WebInspector._toolbarItemClicked = function(event)
{
var toolbarItem = event.currentTarget;
this.currentPanel = toolbarItem.panel;
}
// This table maps MIME types to the Resource.Types which are valid for them.
// The following line:
// "text/html": {0: 1},
// means that text/html is a valid MIME type for resources that have type
// WebInspector.Resource.Type.Document (which has a value of 0).
WebInspector.MIMETypes = {
"text/html": {0: true},
"text/xml": {0: true},
"text/plain": {0: true},
"application/xhtml+xml": {0: true},
"text/css": {1: true},
"text/xsl": {1: true},
"image/jpeg": {2: true},
"image/png": {2: true},
"image/gif": {2: true},
"image/bmp": {2: true},
"image/x-icon": {2: true},
"image/x-xbitmap": {2: true},
"font/ttf": {3: true},
"font/opentype": {3: true},
"application/x-font-type1": {3: true},
"application/x-font-ttf": {3: true},
"application/x-truetype-font": {3: true},
"text/javascript": {4: true},
"text/ecmascript": {4: true},
"application/javascript": {4: true},
"application/ecmascript": {4: true},
"application/x-javascript": {4: true},
"text/javascript1.1": {4: true},
"text/javascript1.2": {4: true},
"text/javascript1.3": {4: true},
"text/jscript": {4: true},
"text/livescript": {4: true},
}
/* Object.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
WebInspector.Object = function() {
}
WebInspector.Object.prototype = {
addEventListener: function(eventType, listener, thisObject) {
if (!("_listeners" in this))
this._listeners = {};
if (!(eventType in this._listeners))
this._listeners[eventType] = [];
this._listeners[eventType].push({ thisObject: thisObject, listener: listener });
},
removeEventListener: function(eventType, listener, thisObject) {
if (!("_listeners" in this) || !(eventType in this._listeners))
return;
var listeners = this._listeners[eventType];
for (var i = 0; i < listeners.length; ++i) {
if (listener && listeners[i].listener === listener && listeners[i].thisObject === thisObject)
listeners.splice(i, 1);
else if (!listener && thisObject && listeners[i].thisObject === thisObject)
listeners.splice(i, 1);
}
if (!listeners.length)
delete this._listeners[eventType];
},
dispatchEventToListeners: function(eventType) {
if (!("_listeners" in this) || !(eventType in this._listeners))
return;
var stoppedPropagation = false;
function stopPropagation()
{
stoppedPropagation = true;
}
function preventDefault()
{
this.defaultPrevented = true;
}
var event = {target: this, type: eventType, defaultPrevented: false};
event.stopPropagation = stopPropagation.bind(event);
event.preventDefault = preventDefault.bind(event);
var listeners = this._listeners[eventType];
for (var i = 0; i < listeners.length; ++i) {
listeners[i].listener.call(listeners[i].thisObject, event);
if (stoppedPropagation)
break;
}
return event.defaultPrevented;
}
}
/* TextPrompt.js */
/*
* Copyright (C) 2008 Apple Inc. All rights reserved.
*
* 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.
*/
WebInspector.TextPrompt = function(element, completions, stopCharacters)
{
this.element = element;
this.completions = completions;
this.completionStopCharacters = stopCharacters;
this.history = [];
this.historyOffset = 0;
}
WebInspector.TextPrompt.prototype = {
get text()
{
return this.element.textContent;
},
set text(x)
{
if (!x) {
// Append a break element instead of setting textContent to make sure the selection is inside the prompt.
this.element.removeChildren();
this.element.appendChild(document.createElement("br"));
} else
this.element.textContent = x;
this.moveCaretToEndOfPrompt();
},
handleKeyEvent: function(event)
{
switch (event.keyIdentifier) {
case "Up":
this._upKeyPressed(event);
break;
case "Down":
this._downKeyPressed(event);
break;
case "U+0009": // Tab
this._tabKeyPressed(event);
break;
case "Right":
if (!this.acceptAutoComplete())
this.autoCompleteSoon();
break;
default:
this.clearAutoComplete();
this.autoCompleteSoon();
break;
}
},
acceptAutoComplete: function()
{
if (!this.autoCompleteElement || !this.autoCompleteElement.parentNode)
return false;
var text = this.autoCompleteElement.textContent;
var textNode = document.createTextNode(text);
this.autoCompleteElement.parentNode.replaceChild(textNode, this.autoCompleteElement);
delete this.autoCompleteElement;
var finalSelectionRange = document.createRange();
finalSelectionRange.setStart(textNode, text.length);
finalSelectionRange.setEnd(textNode, text.length);
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(finalSelectionRange);
return true;
},
clearAutoComplete: function(includeTimeout)
{
if (includeTimeout && "_completeTimeout" in this) {
clearTimeout(this._completeTimeout);
delete this._completeTimeout;
}
if (!this.autoCompleteElement)
return;
if (this.autoCompleteElement.parentNode)
this.autoCompleteElement.parentNode.removeChild(this.autoCompleteElement);
delete this.autoCompleteElement;
if (!this._userEnteredRange || !this._userEnteredText)
return;
this._userEnteredRange.deleteContents();
var userTextNode = document.createTextNode(this._userEnteredText);
this._userEnteredRange.insertNode(userTextNode);
var selectionRange = document.createRange();
selectionRange.setStart(userTextNode, this._userEnteredText.length);
selectionRange.setEnd(userTextNode, this._userEnteredText.length);
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(selectionRange);
delete this._userEnteredRange;
delete this._userEnteredText;
},
autoCompleteSoon: function()
{
if (!("_completeTimeout" in this))
this._completeTimeout = setTimeout(this.complete.bind(this, true), 250);
},
complete: function(auto)
{
this.clearAutoComplete(true);
var selection = window.getSelection();
if (!selection.rangeCount)
return;
var selectionRange = selection.getRangeAt(0);
if (!selectionRange.commonAncestorContainer.isDescendant(this.element))
return;
if (auto && !this.isCaretAtEndOfPrompt())
return;
var wordPrefixRange = selectionRange.startContainer.rangeOfWord(selectionRange.startOffset, this.completionStopCharacters, this.element, "backward");
var completions = this.completions(wordPrefixRange, auto);
if (!completions || !completions.length)
return;
var fullWordRange = document.createRange();
fullWordRange.setStart(wordPrefixRange.startContainer, wordPrefixRange.startOffset);
fullWordRange.setEnd(selectionRange.endContainer, selectionRange.endOffset);
if (completions.length === 1 || selection.isCollapsed || auto) {
var completionText = completions[0];
} else {
var currentText = fullWordRange.toString();
var foundIndex = null;
for (var i = 0; i < completions.length; ++i) {
if (completions[i] === currentText)
foundIndex = i;
}
if (foundIndex === null || (foundIndex + 1) >= completions.length)
var completionText = completions[0];
else
var completionText = completions[foundIndex + 1];
}
var wordPrefixLength = wordPrefixRange.toString().length;
this._userEnteredRange = fullWordRange;
this._userEnteredText = fullWordRange.toString();
fullWordRange.deleteContents();
var finalSelectionRange = document.createRange();
if (auto) {
var prefixText = completionText.substring(0, wordPrefixLength);
var suffixText = completionText.substring(wordPrefixLength);
var prefixTextNode = document.createTextNode(prefixText);
fullWordRange.insertNode(prefixTextNode);
this.autoCompleteElement = document.createElement("span");
this.autoCompleteElement.className = "auto-complete-text";
this.autoCompleteElement.textContent = suffixText;
prefixTextNode.parentNode.insertBefore(this.autoCompleteElement, prefixTextNode.nextSibling);
finalSelectionRange.setStart(prefixTextNode, wordPrefixLength);
finalSelectionRange.setEnd(prefixTextNode, wordPrefixLength);
} else {
var completionTextNode = document.createTextNode(completionText);
fullWordRange.insertNode(completionTextNode);
if (completions.length > 1)
finalSelectionRange.setStart(completionTextNode, wordPrefixLength);
else
finalSelectionRange.setStart(completionTextNode, completionText.length);
finalSelectionRange.setEnd(completionTextNode, completionText.length);
}
selection.removeAllRanges();
selection.addRange(finalSelectionRange);
},
isCaretInsidePrompt: function()
{
return this.element.isInsertionCaretInside();
},
isCaretAtEndOfPrompt: function()
{
var selection = window.getSelection();
if (!selection.rangeCount || !selection.isCollapsed)
return false;
var selectionRange = selection.getRangeAt(0);
var node = selectionRange.startContainer;
if (node !== this.element && !node.isDescendant(this.element))
return false;
if (node.nodeType === Node.TEXT_NODE && selectionRange.startOffset < node.nodeValue.length)
return false;
var foundNextText = false;
while (node) {
if (node.nodeType === Node.TEXT_NODE && node.nodeValue.length) {
if (foundNextText)
return false;
foundNextText = true;
}
node = node.traverseNextNode(false, this.element);
}
return true;
},
moveCaretToEndOfPrompt: function()
{
var selection = window.getSelection();
var selectionRange = document.createRange();
var offset = this.element.childNodes.length;
selectionRange.setStart(this.element, offset);
selectionRange.setEnd(this.element, offset);
selection.removeAllRanges();
selection.addRange(selectionRange);
},
_tabKeyPressed: function(event)
{
event.preventDefault();
event.stopPropagation();
this.complete();
},
_upKeyPressed: function(event)
{
event.preventDefault();
event.stopPropagation();
if (this.historyOffset == this.history.length)
return;
this.clearAutoComplete(true);
if (this.historyOffset == 0)
this.tempSavedCommand = this.text;
++this.historyOffset;
this.text = this.history[this.history.length - this.historyOffset];
},
_downKeyPressed: function(event)
{
event.preventDefault();
event.stopPropagation();
if (this.historyOffset == 0)
return;
this.clearAutoComplete(true);
--this.historyOffset;
if (this.historyOffset == 0) {
this.text = this.tempSavedCommand;
delete this.tempSavedCommand;
return;
}
this.text = this.history[this.history.length - this.historyOffset];
}
}
/* Placard.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
WebInspector.Placard = function(title, subtitle)
{
this.element = document.createElement("div");
this.element.className = "placard";
this.element.placard = this;
this.titleElement = document.createElement("div");
this.titleElement.className = "title";
this.subtitleElement = document.createElement("div");
this.subtitleElement.className = "subtitle";
this.element.appendChild(this.subtitleElement);
this.element.appendChild(this.titleElement);
this.title = title;
this.subtitle = subtitle;
this.selected = false;
}
WebInspector.Placard.prototype = {
get title()
{
return this._title;
},
set title(x)
{
if (this._title === x)
return;
this._title = x;
this.titleElement.textContent = x;
},
get subtitle()
{
return this._subtitle;
},
set subtitle(x)
{
if (this._subtitle === x)
return;
this._subtitle = x;
this.subtitleElement.innerHTML = x;
},
get selected()
{
return this._selected;
},
set selected(x)
{
if (x)
this.select();
else
this.deselect();
},
select: function()
{
if (this._selected)
return;
this._selected = true;
this.element.addStyleClass("selected");
},
deselect: function()
{
if (!this._selected)
return;
this._selected = false;
this.element.removeStyleClass("selected");
},
toggleSelected: function()
{
this.selected = !this.selected;
}
}
/* View.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
WebInspector.View = function(element)
{
this.element = element || document.createElement("div");
this._visible = false;
}
WebInspector.View.prototype = {
get visible()
{
return this._visible;
},
set visible(x)
{
if (this._visible === x)
return;
if (x)
this.show();
else
this.hide();
},
show: function(parentElement)
{
this._visible = true;
if (parentElement && parentElement !== this.element.parentNode) {
this.detach();
parentElement.appendChild(this.element);
}
if (!this.element.parentNode && this.attach)
this.attach();
this.element.addStyleClass("visible");
},
hide: function()
{
this.element.removeStyleClass("visible");
this._visible = false;
},
detach: function()
{
if (this.element.parentNode)
this.element.parentNode.removeChild(this.element);
}
}
/* Console.js */
/*
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
*
* 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.
*/
WebInspector.Console = function()
{
this.messages = [];
WebInspector.View.call(this, document.getElementById("console"));
this.messagesElement = document.getElementById("console-messages");
this.messagesElement.addEventListener("selectstart", this._messagesSelectStart.bind(this), false);
this.messagesElement.addEventListener("click", this._messagesClicked.bind(this), true);
this.promptElement = document.getElementById("console-prompt");
this.promptElement.handleKeyEvent = this._promptKeyDown.bind(this);
this.prompt = new WebInspector.TextPrompt(this.promptElement, this.completions.bind(this), " .=:[({;");
this.toggleButton = document.getElementById("console-status-bar-item");
this.toggleButton.title = WebInspector.UIString("Show console.");
this.toggleButton.addEventListener("click", this._toggleButtonClicked.bind(this), false);
this.clearButton = document.getElementById("clear-console-status-bar-item");
this.clearButton.title = WebInspector.UIString("Clear console log.");
this.clearButton.addEventListener("click", this._clearButtonClicked.bind(this), false);
this.topGroup = new WebInspector.ConsoleGroup(null, 0);
this.messagesElement.insertBefore(this.topGroup.element, this.promptElement);
this.groupLevel = 0;
this.currentGroup = this.topGroup;
document.getElementById("main-status-bar").addEventListener("mousedown", this._startStatusBarDragging.bind(this), true);
}
WebInspector.Console.prototype = {
show: function()
{
if (this._animating || this.visible)
return;
WebInspector.View.prototype.show.call(this);
this._animating = true;
this.toggleButton.addStyleClass("toggled-on");
this.toggleButton.title = WebInspector.UIString("Hide console.");
document.body.addStyleClass("console-visible");
var anchoredItems = document.getElementById("anchored-status-bar-items");
var animations = [
{element: document.getElementById("main"), end: {bottom: this.element.offsetHeight}},
{element: document.getElementById("main-status-bar"), start: {"padding-left": anchoredItems.offsetWidth - 1}, end: {"padding-left": 0}},
{element: document.getElementById("other-console-status-bar-items"), start: {opacity: 0}, end: {opacity: 1}}
];
var consoleStatusBar = document.getElementById("console-status-bar");
consoleStatusBar.insertBefore(anchoredItems, consoleStatusBar.firstChild);
function animationFinished()
{
if ("updateStatusBarItems" in WebInspector.currentPanel)
WebInspector.currentPanel.updateStatusBarItems();
WebInspector.currentFocusElement = this.promptElement;
delete this._animating;
}
WebInspector.animateStyle(animations, window.event && window.event.shiftKey ? 2000 : 250, animationFinished.bind(this));
if (!this.prompt.isCaretInsidePrompt())
this.prompt.moveCaretToEndOfPrompt();
},
hide: function()
{
if (this._animating || !this.visible)
return;
WebInspector.View.prototype.hide.call(this);
this._animating = true;
this.toggleButton.removeStyleClass("toggled-on");
this.toggleButton.title = WebInspector.UIString("Show console.");
if (this.element === WebInspector.currentFocusElement || this.element.isAncestor(WebInspector.currentFocusElement))
WebInspector.currentFocusElement = WebInspector.previousFocusElement;
var anchoredItems = document.getElementById("anchored-status-bar-items");
// Temporally set properties and classes to mimic the post-animation values so panels
// like Elements in their updateStatusBarItems call will size things to fit the final location.
document.getElementById("main-status-bar").style.setProperty("padding-left", (anchoredItems.offsetWidth - 1) + "px");
document.body.removeStyleClass("console-visible");
if ("updateStatusBarItems" in WebInspector.currentPanel)
WebInspector.currentPanel.updateStatusBarItems();
document.body.addStyleClass("console-visible");
var animations = [
{element: document.getElementById("main"), end: {bottom: 0}},
{element: document.getElementById("main-status-bar"), start: {"padding-left": 0}, end: {"padding-left": anchoredItems.offsetWidth - 1}},
{element: document.getElementById("other-console-status-bar-items"), start: {opacity: 1}, end: {opacity: 0}}
];
function animationFinished()
{
var mainStatusBar = document.getElementById("main-status-bar");
mainStatusBar.insertBefore(anchoredItems, mainStatusBar.firstChild);
mainStatusBar.style.removeProperty("padding-left");
document.body.removeStyleClass("console-visible");
delete this._animating;
}
WebInspector.animateStyle(animations, window.event && window.event.shiftKey ? 2000 : 250, animationFinished.bind(this));
},
addMessage: function(msg)
{
if (msg instanceof WebInspector.ConsoleMessage) {
msg.totalRepeatCount = msg.repeatCount;
msg.repeatDelta = msg.repeatCount;
var messageRepeated = false;
if (msg.isEqual && msg.isEqual(this.previousMessage)) {
// Because sometimes we get a large number of repeated messages and sometimes
// we get them one at a time, we need to know the difference between how many
// repeats we used to have and how many we have now.
msg.repeatDelta -= this.previousMessage.totalRepeatCount;
if (!isNaN(this.repeatCountBeforeCommand))
msg.repeatCount -= this.repeatCountBeforeCommand;
if (!this.commandSincePreviousMessage) {
// Recreate the previous message element to reset the repeat count.
var messagesElement = this.currentGroup.messagesElement;
messagesElement.removeChild(messagesElement.lastChild);
messagesElement.appendChild(msg.toMessageElement());
messageRepeated = true;
}
} else
delete this.repeatCountBeforeCommand;
// Increment the error or warning count
switch (msg.level) {
case WebInspector.ConsoleMessage.MessageLevel.Warning:
WebInspector.warnings += msg.repeatDelta;
break;
case WebInspector.ConsoleMessage.MessageLevel.Error:
WebInspector.errors += msg.repeatDelta;
break;
}
// Add message to the resource panel
if (msg.url in WebInspector.resourceURLMap) {
msg.resource = WebInspector.resourceURLMap[msg.url];
WebInspector.panels.resources.addMessageToResource(msg.resource, msg);
}
this.commandSincePreviousMessage = false;
this.previousMessage = msg;
if (messageRepeated)
return;
} else if (msg instanceof WebInspector.ConsoleCommand) {
if (this.previousMessage) {
this.commandSincePreviousMessage = true;
this.repeatCountBeforeCommand = this.previousMessage.totalRepeatCount;
}
}
this.messages.push(msg);
if (msg.level === WebInspector.ConsoleMessage.MessageLevel.EndGroup) {
if (this.groupLevel < 1)
return;
this.groupLevel--;
this.currentGroup = this.currentGroup.parentGroup;
} else {
if (msg.level === WebInspector.ConsoleMessage.MessageLevel.StartGroup) {
this.groupLevel++;
var group = new WebInspector.ConsoleGroup(this.currentGroup, this.groupLevel);
this.currentGroup.messagesElement.appendChild(group.element);
this.currentGroup = group;
}
this.currentGroup.addMessage(msg);
}
this.promptElement.scrollIntoView(false);
},
clearMessages: function(clearInspectorController)
{
if (clearInspectorController)
InspectorController.clearMessages();
WebInspector.panels.resources.clearMessages();
this.messages = [];
this.groupLevel = 0;
this.currentGroup = this.topGroup;
this.topGroup.messagesElement.removeChildren();
WebInspector.errors = 0;
WebInspector.warnings = 0;
delete this.commandSincePreviousMessage;
delete this.repeatCountBeforeCommand;
delete this.previousMessage;
},
completions: function(wordRange, bestMatchOnly)
{
// Pass less stop characters to rangeOfWord so the range will be a more complete expression.
const expressionStopCharacters = " =:{;";
var expressionRange = wordRange.startContainer.rangeOfWord(wordRange.startOffset, expressionStopCharacters, this.promptElement, "backward");
var expressionString = expressionRange.toString();
var lastIndex = expressionString.length - 1;
var dotNotation = (expressionString[lastIndex] === ".");
var bracketNotation = (expressionString[lastIndex] === "[");
if (dotNotation || bracketNotation)
expressionString = expressionString.substr(0, lastIndex);
var prefix = wordRange.toString();
if (!expressionString && !prefix)
return;
var result;
if (expressionString) {
try {
result = this._evalInInspectedWindow(expressionString);
} catch(e) {
// Do nothing, the prefix will be considered a window property.
}
} else {
// There is no expressionString, so the completion should happen against global properties.
// Or if the debugger is paused, against properties in scope of the selected call frame.
if (WebInspector.panels.scripts.paused)
result = WebInspector.panels.scripts.variablesInScopeForSelectedCallFrame();
else
result = InspectorController.inspectedWindow();
}
if (bracketNotation) {
if (prefix.length && prefix[0] === "'")
var quoteUsed = "'";
else
var quoteUsed = "\"";
}
var results = [];
var properties = Object.sortedProperties(result);
for (var i = 0; i < properties.length; ++i) {
var property = properties[i];
if (bracketNotation)
property = quoteUsed + property.escapeCharacters(quoteUsed + "\\") + quoteUsed + "]";
if (property.length < prefix.length)
continue;
if (property.indexOf(prefix) !== 0)
continue;
results.push(property);
if (bestMatchOnly)
break;
}
return results;
},
_toggleButtonClicked: function()
{
this.visible = !this.visible;
},
_clearButtonClicked: function()
{
this.clearMessages(true);
},
_messagesSelectStart: function(event)
{
if (this._selectionTimeout)
clearTimeout(this._selectionTimeout);
this.prompt.clearAutoComplete();
function moveBackIfOutside()
{
delete this._selectionTimeout;
if (!this.prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed)
this.prompt.moveCaretToEndOfPrompt();
this.prompt.autoCompleteSoon();
}
this._selectionTimeout = setTimeout(moveBackIfOutside.bind(this), 100);
},
_messagesClicked: function(event)
{
var link = event.target.enclosingNodeOrSelfWithNodeName("a");
if (!link || !link.representedNode)
return;
WebInspector.updateFocusedNode(link.representedNode);
event.stopPropagation();
event.preventDefault();
},
_promptKeyDown: function(event)
{
switch (event.keyIdentifier) {
case "Enter":
this._enterKeyPressed(event);
return;
}
this.prompt.handleKeyEvent(event);
},
_startStatusBarDragging: function(event)
{
if (!this.visible || event.target !== document.getElementById("main-status-bar"))
return;
WebInspector.elementDragStart(document.getElementById("main-status-bar"), this._statusBarDragging.bind(this), this._endStatusBarDragging.bind(this), event, "row-resize");
this._statusBarDragOffset = event.pageY - this.element.totalOffsetTop;
event.stopPropagation();
},
_statusBarDragging: function(event)
{
var mainElement = document.getElementById("main");
var height = window.innerHeight - event.pageY + this._statusBarDragOffset;
height = Number.constrain(height, Preferences.minConsoleHeight, window.innerHeight - mainElement.totalOffsetTop - Preferences.minConsoleHeight);
mainElement.style.bottom = height + "px";
this.element.style.height = height + "px";
event.preventDefault();
event.stopPropagation();
},
_endStatusBarDragging: function(event)
{
WebInspector.elementDragEnd(event);
delete this._statusBarDragOffset;
event.stopPropagation();
},
_evalInInspectedWindow: function(expression)
{
if (WebInspector.panels.scripts.paused)
return WebInspector.panels.scripts.evaluateInSelectedCallFrame(expression);
var inspectedWindow = InspectorController.inspectedWindow();
if (!inspectedWindow._inspectorCommandLineAPI) {
inspectedWindow.eval("window._inspectorCommandLineAPI = { \
$: function() { return document.getElementById.apply(document, arguments) }, \
$$: function() { return document.querySelectorAll.apply(document, arguments) }, \
$x: function(xpath, context) { \
var nodes = []; \
try { \
var doc = context || document; \
var results = doc.evaluate(xpath, doc, null, XPathResult.ANY_TYPE, null); \
var node; \
while (node = results.iterateNext()) nodes.push(node); \
} catch (e) {} \
return nodes; \
}, \
dir: function() { return console.dir.apply(console, arguments) }, \
dirxml: function() { return console.dirxml.apply(console, arguments) }, \
keys: function(o) { var a = []; for (k in o) a.push(k); return a; }, \
values: function(o) { var a = []; for (k in o) a.push(o[k]); return a; }, \
profile: function() { return console.profile.apply(console, arguments) }, \
profileEnd: function() { return console.profileEnd.apply(console, arguments) } \
};");
inspectedWindow._inspectorCommandLineAPI.clear = InspectorController.wrapCallback(this.clearMessages.bind(this));
}
// Surround the expression in with statements to inject our command line API so that
// the window object properties still take more precedent than our API functions.
expression = "with (window._inspectorCommandLineAPI) { with (window) { " + expression + " } }";
return inspectedWindow.eval(expression);
},
_enterKeyPressed: function(event)
{
if (event.altKey)
return;
event.preventDefault();
event.stopPropagation();
this.prompt.clearAutoComplete(true);
var str = this.prompt.text;
if (!str.length)
return;
var result;
var exception = false;
try {
result = this._evalInInspectedWindow(str);
} catch(e) {
result = e;
exception = true;
}
this.prompt.history.push(str);
this.prompt.historyOffset = 0;
this.prompt.text = "";
var level = exception ? WebInspector.ConsoleMessage.MessageLevel.Error : WebInspector.ConsoleMessage.MessageLevel.Log;
this.addMessage(new WebInspector.ConsoleCommand(str, result, this._format(result), level));
},
_mouseOverNode: function(event)
{
var anchorElement = event.target.enclosingNodeOrSelfWithNodeName("a");
WebInspector.hoveredDOMNode = (anchorElement ? anchorElement.representedNode : null);
},
_mouseOutOfNode: function(event)
{
var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
var anchorElement = nodeUnderMouse.enclosingNodeOrSelfWithNodeName("a");
if (!anchorElement || !anchorElement.representedNode)
WebInspector.hoveredDOMNode = null;
},
_format: function(output)
{
var type = Object.type(output, InspectorController.inspectedWindow());
if (type === "object") {
if (output instanceof InspectorController.inspectedWindow().Node)
type = "node";
}
// We don't perform any special formatting on these types, so we just
// pass them through the simple _formatvalue function.
var undecoratedTypes = {
"undefined": 1,
"null": 1,
"boolean": 1,
"number": 1,
"date": 1,
"function": 1,
};
var formatter;
if (type in undecoratedTypes)
formatter = "_formatvalue";
else {
formatter = "_format" + type;
if (!(formatter in this)) {
formatter = "_formatobject";
type = "object";
}
}
var span = document.createElement("span");
span.addStyleClass("console-formatted-" + type);
this[formatter](output, span);
return span;
},
_formatvalue: function(val, elem)
{
elem.appendChild(document.createTextNode(val));
},
_formatstring: function(str, elem)
{
elem.appendChild(document.createTextNode("\"" + str + "\""));
},
_formatregexp: function(re, elem)
{
var formatted = String(re).replace(/([\\\/])/g, "\\$1").replace(/\\(\/[gim]*)$/, "$1").substring(1);
elem.appendChild(document.createTextNode(formatted));
},
_formatarray: function(arr, elem)
{
elem.appendChild(document.createTextNode("["));
for (var i = 0; i < arr.length; ++i) {
elem.appendChild(this._format(arr[i]));
if (i < arr.length - 1)
elem.appendChild(document.createTextNode(", "));
}
elem.appendChild(document.createTextNode("]"));
},
_formatnode: function(node, elem)
{
var anchor = document.createElement("a");
anchor.className = "inspectible-node";
anchor.innerHTML = nodeTitleInfo.call(node).title;
anchor.representedNode = node;
anchor.addEventListener("mouseover", this._mouseOverNode.bind(this), false);
anchor.addEventListener("mouseout", this._mouseOutOfNode.bind(this), false);
elem.appendChild(anchor);
},
_formatobject: function(obj, elem)
{
elem.appendChild(document.createTextNode(Object.describe(obj)));
},
_formaterror: function(obj, elem)
{
elem.appendChild(document.createTextNode(obj.name + ": " + obj.message + " "));
if (obj.sourceURL) {
var urlElement = document.createElement("a");
urlElement.className = "console-message-url webkit-html-resource-link";
urlElement.href = obj.sourceURL;
urlElement.lineNumber = obj.line;
urlElement.preferredPanel = "scripts";
if (obj.line > 0)
urlElement.textContent = WebInspector.UIString("%s (line %d)", obj.sourceURL, obj.line);
else
urlElement.textContent = obj.sourceURL;
elem.appendChild(urlElement);
}
},
}
WebInspector.Console.prototype.__proto__ = WebInspector.View.prototype;
WebInspector.ConsoleMessage = function(source, level, line, url, groupLevel, repeatCount)
{
this.source = source;
this.level = level;
this.line = line;
this.url = url;
this.groupLevel = groupLevel;
this.repeatCount = repeatCount;
switch (this.level) {
case WebInspector.ConsoleMessage.MessageLevel.Object:
var propertiesSection = new WebInspector.ObjectPropertiesSection(arguments[6], null, null, null, true);
propertiesSection.element.addStyleClass("console-message");
this.propertiesSection = propertiesSection;
break;
case WebInspector.ConsoleMessage.MessageLevel.Node:
var node = arguments[6];
if (!(node instanceof InspectorController.inspectedWindow().Node))
return;
this.elementsTreeOutline = new WebInspector.ElementsTreeOutline();
this.elementsTreeOutline.rootDOMNode = node;
break;
case WebInspector.ConsoleMessage.MessageLevel.Trace:
var span = document.createElement("span");
span.addStyleClass("console-formatted-trace");
var stack = Array.prototype.slice.call(arguments, 6);
var funcNames = stack.map(function(f) {
return f.name || WebInspector.UIString("(anonymous function)");
});
span.appendChild(document.createTextNode(funcNames.join("\n")));
this.formattedMessage = span;
break;
default:
// The formatedMessage property is used for the rich and interactive console.
this.formattedMessage = this._format(Array.prototype.slice.call(arguments, 6));
// This is used for inline message bubbles in SourceFrames, or other plain-text representations.
this.message = this.formattedMessage.textContent;
break;
}
}
WebInspector.ConsoleMessage.prototype = {
isErrorOrWarning: function()
{
return (this.level === WebInspector.ConsoleMessage.MessageLevel.Warning || this.level === WebInspector.ConsoleMessage.MessageLevel.Error);
},
_format: function(parameters)
{
var formattedResult = document.createElement("span");
if (!parameters.length)
return formattedResult;
function formatForConsole(obj)
{
return WebInspector.console._format(obj);
}
if (Object.type(parameters[0], InspectorController.inspectedWindow()) === "string") {
var formatters = {}
for (var i in String.standardFormatters)
formatters[i] = String.standardFormatters[i];
// Firebug uses %o for formatting objects.
formatters.o = formatForConsole;
// Firebug allows both %i and %d for formatting integers.
formatters.i = formatters.d;
function append(a, b)
{
if (!(b instanceof Node))
a.appendChild(WebInspector.linkifyStringAsFragment(b.toString()));
else
a.appendChild(b);
return a;
}
var result = String.format(parameters[0], parameters.slice(1), formatters, formattedResult, append);
formattedResult = result.formattedResult;
parameters = result.unusedSubstitutions;
if (parameters.length)
formattedResult.appendChild(document.createTextNode(" "));
}
for (var i = 0; i < parameters.length; ++i) {
if (typeof parameters[i] === "string")
formattedResult.appendChild(WebInspector.linkifyStringAsFragment(parameters[i]));
else
formattedResult.appendChild(formatForConsole(parameters[i]));
if (i < parameters.length - 1)
formattedResult.appendChild(document.createTextNode(" "));
}
return formattedResult;
},
toMessageElement: function()
{
if (this.propertiesSection)
return this.propertiesSection.element;
var element = document.createElement("div");
element.message = this;
element.className = "console-message";
switch (this.source) {
case WebInspector.ConsoleMessage.MessageSource.HTML:
element.addStyleClass("console-html-source");
break;
case WebInspector.ConsoleMessage.MessageSource.XML:
element.addStyleClass("console-xml-source");
break;
case WebInspector.ConsoleMessage.MessageSource.JS:
element.addStyleClass("console-js-source");
break;
case WebInspector.ConsoleMessage.MessageSource.CSS:
element.addStyleClass("console-css-source");
break;
case WebInspector.ConsoleMessage.MessageSource.Other:
element.addStyleClass("console-other-source");
break;
}
switch (this.level) {
case WebInspector.ConsoleMessage.MessageLevel.Tip:
element.addStyleClass("console-tip-level");
break;
case WebInspector.ConsoleMessage.MessageLevel.Log:
element.addStyleClass("console-log-level");
break;
case WebInspector.ConsoleMessage.MessageLevel.Warning:
element.addStyleClass("console-warning-level");
break;
case WebInspector.ConsoleMessage.MessageLevel.Error:
element.addStyleClass("console-error-level");
break;
case WebInspector.ConsoleMessage.MessageLevel.StartGroup:
element.addStyleClass("console-group-title-level");
}
if (this.elementsTreeOutline) {
element.addStyleClass("outline-disclosure");
element.appendChild(this.elementsTreeOutline.element);
return element;
}
if (this.repeatCount > 1) {
var messageRepeatCountElement = document.createElement("span");
messageRepeatCountElement.className = "bubble";
messageRepeatCountElement.textContent = this.repeatCount;
element.appendChild(messageRepeatCountElement);
element.addStyleClass("repeated-message");
}
if (this.url && this.url !== "undefined") {
var urlElement = document.createElement("a");
urlElement.className = "console-message-url webkit-html-resource-link";
urlElement.href = this.url;
urlElement.lineNumber = this.line;
if (this.source === WebInspector.ConsoleMessage.MessageSource.JS)
urlElement.preferredPanel = "scripts";
if (this.line > 0)
urlElement.textContent = WebInspector.UIString("%s (line %d)", WebInspector.displayNameForURL(this.url), this.line);
else
urlElement.textContent = WebInspector.displayNameForURL(this.url);
element.appendChild(urlElement);
}
var messageTextElement = document.createElement("span");
messageTextElement.className = "console-message-text";
messageTextElement.appendChild(this.formattedMessage);
element.appendChild(messageTextElement);
return element;
},
toString: function()
{
var sourceString;
switch (this.source) {
case WebInspector.ConsoleMessage.MessageSource.HTML:
sourceString = "HTML";
break;
case WebInspector.ConsoleMessage.MessageSource.XML:
sourceString = "XML";
break;
case WebInspector.ConsoleMessage.MessageSource.JS:
sourceString = "JS";
break;
case WebInspector.ConsoleMessage.MessageSource.CSS:
sourceString = "CSS";
break;
case WebInspector.ConsoleMessage.MessageSource.Other:
sourceString = "Other";
break;
}
var levelString;
switch (this.level) {
case WebInspector.ConsoleMessage.MessageLevel.Tip:
levelString = "Tip";
break;
case WebInspector.ConsoleMessage.MessageLevel.Log:
levelString = "Log";
break;
case WebInspector.ConsoleMessage.MessageLevel.Warning:
levelString = "Warning";
break;
case WebInspector.ConsoleMessage.MessageLevel.Error:
levelString = "Error";
break;
case WebInspector.ConsoleMessage.MessageLevel.Object:
levelString = "Object";
break;
case WebInspector.ConsoleMessage.MessageLevel.GroupTitle:
levelString = "GroupTitle";
break;
}
return sourceString + " " + levelString + ": " + this.formattedMessage.textContent + "\n" + this.url + " line " + this.line;
},
isEqual: function(msg, disreguardGroup)
{
if (!msg)
return false;
var ret = (this.source == msg.source)
&& (this.level == msg.level)
&& (this.line == msg.line)
&& (this.url == msg.url)
&& (this.message == msg.message);
return (disreguardGroup ? ret : (ret && (this.groupLevel == msg.groupLevel)));
}
}
// Note: Keep these constants in sync with the ones in Chrome.h
WebInspector.ConsoleMessage.MessageSource = {
HTML: 0,
XML: 1,
JS: 2,
CSS: 3,
Other: 4,
}
WebInspector.ConsoleMessage.MessageLevel = {
Tip: 0,
Log: 1,
Warning: 2,
Error: 3,
Object: 4,
Node: 5,
Trace: 6,
StartGroup: 7,
EndGroup: 8
}
WebInspector.ConsoleCommand = function(command, result, formattedResultElement, level)
{
this.command = command;
this.formattedResultElement = formattedResultElement;
this.level = level;
}
WebInspector.ConsoleCommand.prototype = {
toMessageElement: function()
{
var element = document.createElement("div");
element.command = this;
element.className = "console-user-command";
var commandTextElement = document.createElement("span");
commandTextElement.className = "console-message-text";
commandTextElement.textContent = this.command;
element.appendChild(commandTextElement);
var resultElement = document.createElement("div");
resultElement.className = "console-message";
element.appendChild(resultElement);
switch (this.level) {
case WebInspector.ConsoleMessage.MessageLevel.Log:
resultElement.addStyleClass("console-log-level");
break;
case WebInspector.ConsoleMessage.MessageLevel.Warning:
resultElement.addStyleClass("console-warning-level");
break;
case WebInspector.ConsoleMessage.MessageLevel.Error:
resultElement.addStyleClass("console-error-level");
}
var resultTextElement = document.createElement("span");
resultTextElement.className = "console-message-text";
resultTextElement.appendChild(this.formattedResultElement);
resultElement.appendChild(resultTextElement);
return element;
}
}
WebInspector.ConsoleGroup = function(parentGroup, level)
{
this.parentGroup = parentGroup;
this.level = level;
var element = document.createElement("div");
element.className = "console-group";
element.group = this;
this.element = element;
var messagesElement = document.createElement("div");
messagesElement.className = "console-group-messages";
element.appendChild(messagesElement);
this.messagesElement = messagesElement;
}
WebInspector.ConsoleGroup.prototype = {
addMessage: function(msg)
{
var element = msg.toMessageElement();
if (msg.level === WebInspector.ConsoleMessage.MessageLevel.StartGroup) {
this.messagesElement.parentNode.insertBefore(element, this.messagesElement);
element.addEventListener("click", this._titleClicked.bind(this), true);
} else
this.messagesElement.appendChild(element);
},
_titleClicked: function(event)
{
var groupTitleElement = event.target.enclosingNodeOrSelfWithClass("console-group-title-level");
if (groupTitleElement) {
var groupElement = groupTitleElement.enclosingNodeOrSelfWithClass("console-group");
if (groupElement)
if (groupElement.hasStyleClass("collapsed"))
groupElement.removeStyleClass("collapsed");
else
groupElement.addStyleClass("collapsed");
groupTitleElement.scrollIntoViewIfNeeded(true);
}
event.stopPropagation();
event.preventDefault();
}
}
/* Resource.js */
/*
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
*
* 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.
*/
WebInspector.Resource = function(requestHeaders, url, domain, path, lastPathComponent, identifier, mainResource, cached)
{
this.identifier = identifier;
this.startTime = -1;
this.endTime = -1;
this.mainResource = mainResource;
this.requestHeaders = requestHeaders;
this.url = url;
this.domain = domain;
this.path = path;
this.lastPathComponent = lastPathComponent;
this.cached = cached;
this.category = WebInspector.resourceCategories.other;
}
// Keep these in sync with WebCore::InspectorResource::Type
WebInspector.Resource.Type = {
Document: 0,
Stylesheet: 1,
Image: 2,
Font: 3,
Script: 4,
XHR: 5,
Other: 6,
isTextType: function(type)
{
return (type === this.Document) || (type === this.Stylesheet) || (type === this.Script) || (type === this.XHR);
},
toString: function(type)
{
switch (type) {
case this.Document:
return WebInspector.UIString("document");
case this.Stylesheet:
return WebInspector.UIString("stylesheet");
case this.Image:
return WebInspector.UIString("image");
case this.Font:
return WebInspector.UIString("font");
case this.Script:
return WebInspector.UIString("script");
case this.XHR:
return WebInspector.UIString("XHR");
case this.Other:
default:
return WebInspector.UIString("other");
}
}
}
WebInspector.Resource.prototype = {
get url()
{
return this._url;
},
set url(x)
{
if (this._url === x)
return;
var oldURL = this._url;
this._url = x;
// FIXME: We should make the WebInspector object listen for the "url changed" event.
// Then resourceURLChanged can be removed.
WebInspector.resourceURLChanged(this, oldURL);
this.dispatchEventToListeners("url changed");
},
get domain()
{
return this._domain;
},
set domain(x)
{
if (this._domain === x)
return;
this._domain = x;
},
get lastPathComponent()
{
return this._lastPathComponent;
},
set lastPathComponent(x)
{
if (this._lastPathComponent === x)
return;
this._lastPathComponent = x;
this._lastPathComponentLowerCase = x ? x.toLowerCase() : null;
},
get displayName()
{
var title = this.lastPathComponent;
if (!title)
title = this.displayDomain;
if (!title && this.url)
title = this.url.trimURL(WebInspector.mainResource ? WebInspector.mainResource.domain : "");
if (title === "/")
title = this.url;
return title;
},
get displayDomain()
{
// WebInspector.Database calls this, so don't access more than this.domain.
if (this.domain && (!WebInspector.mainResource || (WebInspector.mainResource && this.domain !== WebInspector.mainResource.domain)))
return this.domain;
return "";
},
get startTime()
{
return this._startTime;
},
set startTime(x)
{
if (this._startTime === x)
return;
this._startTime = x;
if (WebInspector.panels.resources)
WebInspector.panels.resources.refreshResource(this);
},
get responseReceivedTime()
{
return this._responseReceivedTime;
},
set responseReceivedTime(x)
{
if (this._responseReceivedTime === x)
return;
this._responseReceivedTime = x;
if (WebInspector.panels.resources)
WebInspector.panels.resources.refreshResource(this);
},
get endTime()
{
return this._endTime;
},
set endTime(x)
{
if (this._endTime === x)
return;
this._endTime = x;
if (WebInspector.panels.resources)
WebInspector.panels.resources.refreshResource(this);
},
get duration()
{
if (this._endTime === -1 || this._startTime === -1)
return -1;
return this._endTime - this._startTime;
},
get latency()
{
if (this._responseReceivedTime === -1 || this._startTime === -1)
return -1;
return this._responseReceivedTime - this._startTime;
},
get contentLength()
{
return this._contentLength;
},
set contentLength(x)
{
if (this._contentLength === x)
return;
this._contentLength = x;
if (WebInspector.panels.resources)
WebInspector.panels.resources.refreshResource(this);
},
get expectedContentLength()
{
return this._expectedContentLength;
},
set expectedContentLength(x)
{
if (this._expectedContentLength === x)
return;
this._expectedContentLength = x;
},
get finished()
{
return this._finished;
},
set finished(x)
{
if (this._finished === x)
return;
this._finished = x;
if (x) {
this._checkTips();
this._checkWarnings();
this.dispatchEventToListeners("finished");
}
},
get failed()
{
return this._failed;
},
set failed(x)
{
this._failed = x;
},
get category()
{
return this._category;
},
set category(x)
{
if (this._category === x)
return;
var oldCategory = this._category;
if (oldCategory)
oldCategory.removeResource(this);
this._category = x;
if (this._category)
this._category.addResource(this);
if (WebInspector.panels.resources) {
WebInspector.panels.resources.refreshResource(this);
WebInspector.panels.resources.recreateViewForResourceIfNeeded(this);
}
},
get mimeType()
{
return this._mimeType;
},
set mimeType(x)
{
if (this._mimeType === x)
return;
this._mimeType = x;
},
get type()
{
return this._type;
},
set type(x)
{
if (this._type === x)
return;
this._type = x;
switch (x) {
case WebInspector.Resource.Type.Document:
this.category = WebInspector.resourceCategories.documents;
break;
case WebInspector.Resource.Type.Stylesheet:
this.category = WebInspector.resourceCategories.stylesheets;
break;
case WebInspector.Resource.Type.Script:
this.category = WebInspector.resourceCategories.scripts;
break;
case WebInspector.Resource.Type.Image:
this.category = WebInspector.resourceCategories.images;
break;
case WebInspector.Resource.Type.Font:
this.category = WebInspector.resourceCategories.fonts;
break;
case WebInspector.Resource.Type.XHR:
this.category = WebInspector.resourceCategories.xhr;
break;
case WebInspector.Resource.Type.Other:
default:
this.category = WebInspector.resourceCategories.other;
break;
}
},
get documentNode() {
if ("identifier" in this)
return InspectorController.getResourceDocumentNode(this.identifier);
return null;
},
get requestHeaders()
{
if (this._requestHeaders === undefined)
this._requestHeaders = {};
return this._requestHeaders;
},
set requestHeaders(x)
{
if (this._requestHeaders === x)
return;
this._requestHeaders = x;
delete this._sortedRequestHeaders;
this.dispatchEventToListeners("requestHeaders changed");
},
get sortedRequestHeaders()
{
if (this._sortedRequestHeaders !== undefined)
return this._sortedRequestHeaders;
this._sortedRequestHeaders = [];
for (var key in this.requestHeaders)
this._sortedRequestHeaders.push({header: key, value: this.requestHeaders[key]});
this._sortedRequestHeaders.sort(function(a,b) { return a.header.localeCompare(b.header) });
return this._sortedRequestHeaders;
},
get responseHeaders()
{
if (this._responseHeaders === undefined)
this._responseHeaders = {};
return this._responseHeaders;
},
set responseHeaders(x)
{
if (this._responseHeaders === x)
return;
this._responseHeaders = x;
delete this._sortedResponseHeaders;
this.dispatchEventToListeners("responseHeaders changed");
},
get sortedResponseHeaders()
{
if (this._sortedResponseHeaders !== undefined)
return this._sortedResponseHeaders;
this._sortedResponseHeaders = [];
for (var key in this.responseHeaders)
this._sortedResponseHeaders.push({header: key, value: this.responseHeaders[key]});
this._sortedResponseHeaders.sort(function(a,b) { return a.header.localeCompare(b.header) });
return this._sortedResponseHeaders;
},
get scripts()
{
if (!("_scripts" in this))
this._scripts = [];
return this._scripts;
},
addScript: function(script)
{
if (!script)
return;
this.scripts.unshift(script);
script.resource = this;
},
removeAllScripts: function()
{
if (!this._scripts)
return;
for (var i = 0; i < this._scripts.length; ++i) {
if (this._scripts[i].resource === this)
delete this._scripts[i].resource;
}
delete this._scripts;
},
removeScript: function(script)
{
if (!script)
return;
if (script.resource === this)
delete script.resource;
if (!this._scripts)
return;
this._scripts.remove(script);
},
get errors()
{
if (!("_errors" in this))
this._errors = 0;
return this._errors;
},
set errors(x)
{
if (this._errors === x)
return;
this._errors = x;
},
get warnings()
{
if (!("_warnings" in this))
this._warnings = 0;
return this._warnings;
},
set warnings(x)
{
if (this._warnings === x)
return;
this._warnings = x;
},
get tips()
{
if (!("_tips" in this))
this._tips = {};
return this._tips;
},
_addTip: function(tip)
{
if (tip.id in this.tips)
return;
this.tips[tip.id] = tip;
// FIXME: Re-enable this code once we have a scope bar in the Console.
// Otherwise, we flood the Console with too many tips.
/*
var msg = new WebInspector.ConsoleMessage(WebInspector.ConsoleMessage.MessageSource.Other,
WebInspector.ConsoleMessage.MessageLevel.Tip, -1, this.url, null, 1, tip.message);
WebInspector.console.addMessage(msg);
*/
},
_checkTips: function()
{
for (var tip in WebInspector.Tips)
this._checkTip(WebInspector.Tips[tip]);
},
_checkTip: function(tip)
{
var addTip = false;
switch (tip.id) {
case WebInspector.Tips.ResourceNotCompressed.id:
addTip = this._shouldCompress();
break;
}
if (addTip)
this._addTip(tip);
},
_shouldCompress: function()
{
return WebInspector.Resource.Type.isTextType(this.type)
&& this.domain
&& !("Content-Encoding" in this.responseHeaders)
&& this.contentLength !== undefined
&& this.contentLength >= 512;
},
_mimeTypeIsConsistentWithType: function()
{
if (typeof this.type === "undefined"
|| this.type === WebInspector.Resource.Type.Other
|| this.type === WebInspector.Resource.Type.XHR)
return true;
if (this.mimeType in WebInspector.MIMETypes)
return this.type in WebInspector.MIMETypes[this.mimeType];
return true;
},
_checkWarnings: function()
{
for (var warning in WebInspector.Warnings)
this._checkWarning(WebInspector.Warnings[warning]);
},
_checkWarning: function(warning)
{
var addWarning = false;
var msg;
switch (warning.id) {
case WebInspector.Warnings.IncorrectMIMEType.id:
if (!this._mimeTypeIsConsistentWithType())
msg = new WebInspector.ConsoleMessage(WebInspector.ConsoleMessage.MessageSource.Other,
WebInspector.ConsoleMessage.MessageLevel.Warning, -1, this.url, null, 1,
String.sprintf(WebInspector.Warnings.IncorrectMIMEType.message,
WebInspector.Resource.Type.toString(this.type), this.mimeType));
break;
}
if (msg)
WebInspector.console.addMessage(msg);
}
}
WebInspector.Resource.prototype.__proto__ = WebInspector.Object.prototype;
WebInspector.Resource.CompareByStartTime = function(a, b)
{
if (a.startTime < b.startTime)
return -1;
if (a.startTime > b.startTime)
return 1;
return 0;
}
WebInspector.Resource.CompareByResponseReceivedTime = function(a, b)
{
if (a.responseReceivedTime < b.responseReceivedTime)
return -1;
if (a.responseReceivedTime > b.responseReceivedTime)
return 1;
return 0;
}
WebInspector.Resource.CompareByEndTime = function(a, b)
{
if (a.endTime < b.endTime)
return -1;
if (a.endTime > b.endTime)
return 1;
return 0;
}
WebInspector.Resource.CompareByDuration = function(a, b)
{
if (a.duration < b.duration)
return -1;
if (a.duration > b.duration)
return 1;
return 0;
}
WebInspector.Resource.CompareByLatency = function(a, b)
{
if (a.latency < b.latency)
return -1;
if (a.latency > b.latency)
return 1;
return 0;
}
WebInspector.Resource.CompareBySize = function(a, b)
{
if (a.contentLength < b.contentLength)
return -1;
if (a.contentLength > b.contentLength)
return 1;
return 0;
}
/* ResourceCategory.js */
/*
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
*
* 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.
*/
WebInspector.ResourceCategory = function(title, name)
{
this.name = name;
this.title = title;
this.resources = [];
}
WebInspector.ResourceCategory.prototype = {
toString: function()
{
return this.title;
},
addResource: function(resource)
{
var a = resource;
var resourcesLength = this.resources.length;
for (var i = 0; i < resourcesLength; ++i) {
var b = this.resources[i];
if (a._lastPathComponentLowerCase && b._lastPathComponentLowerCase)
if (a._lastPathComponentLowerCase < b._lastPathComponentLowerCase)
break;
else if (a.name && b.name)
if (a.name < b.name)
break;
}
this.resources.splice(i, 0, resource);
},
removeResource: function(resource)
{
this.resources.remove(resource, true);
},
removeAllResources: function(resource)
{
this.resources = [];
}
}
/* Database.js */
/*
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
*
* 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.
*/
WebInspector.Database = function(database, domain, name, version)
{
this.database = database;
this.domain = domain;
this.name = name;
this.version = version;
}
WebInspector.Database.prototype = {
get database()
{
return this._database;
},
set database(x)
{
if (this._database === x)
return;
this._database = x;
},
get name()
{
return this._name;
},
set name(x)
{
if (this._name === x)
return;
this._name = x;
},
get version()
{
return this._version;
},
set version(x)
{
if (this._version === x)
return;
this._version = x;
},
get domain()
{
return this._domain;
},
set domain(x)
{
if (this._domain === x)
return;
this._domain = x;
},
get displayDomain()
{
return WebInspector.Resource.prototype.__lookupGetter__("displayDomain").call(this);
},
get tableNames()
{
return InspectorController.databaseTableNames(this.database).sort();
}
}
/* DataGrid.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
WebInspector.DataGrid = function(columns)
{
this.element = document.createElement("div");
this.element.className = "data-grid";
this.element.tabIndex = 0;
this.element.addEventListener("keydown", this._keyDown.bind(this), false);
this._headerTable = document.createElement("table");
this._headerTable.className = "header";
this._dataTable = document.createElement("table");
this._dataTable.className = "data";
this._dataTable.addEventListener("mousedown", this._mouseDownInDataTable.bind(this), true);
this._dataTable.addEventListener("click", this._clickInDataTable.bind(this), true);
var scrollContainer = document.createElement("div");
scrollContainer.className = "data-container";
scrollContainer.appendChild(this._dataTable);
this.element.appendChild(this._headerTable);
this.element.appendChild(scrollContainer);
var headerRow = document.createElement("tr");
var columnGroup = document.createElement("colgroup");
var columnCount = 0;
for (var columnIdentifier in columns) {
var column = columns[columnIdentifier];
if (column.disclosure)
this.disclosureColumnIdentifier = columnIdentifier;
var col = document.createElement("col");
if (column.width)
col.style.width = column.width;
columnGroup.appendChild(col);
var cell = document.createElement("th");
cell.className = columnIdentifier + "-column";
cell.columnIdentifier = columnIdentifier;
var div = document.createElement("div");
div.textContent = column.title;
cell.appendChild(div);
if (column.sort) {
cell.addStyleClass("sort-" + column.sort);
this._sortColumnCell = cell;
}
if (column.sortable) {
cell.addEventListener("click", this._clickInHeaderCell.bind(this), false);
cell.addStyleClass("sortable");
}
headerRow.appendChild(cell);
++columnCount;
}
columnGroup.span = columnCount;
var cell = document.createElement("th");
cell.className = "corner";
headerRow.appendChild(cell);
this._headerTable.appendChild(columnGroup);
this.headerTableBody.appendChild(headerRow);
var fillerRow = document.createElement("tr");
fillerRow.className = "filler";
for (var i = 0; i < columnCount; ++i) {
var cell = document.createElement("td");
fillerRow.appendChild(cell);
}
this._dataTable.appendChild(columnGroup.cloneNode(true));
this.dataTableBody.appendChild(fillerRow);
this.columns = columns || {};
this.children = [];
this.selectedNode = null;
this.expandNodesWhenArrowing = false;
this.root = true;
this.hasChildren = false;
this.expanded = true;
this.revealed = true;
this.selected = false;
this.dataGrid = this;
this.indentWidth = 15;
}
WebInspector.DataGrid.prototype = {
get sortColumnIdentifier()
{
if (!this._sortColumnCell)
return null;
return this._sortColumnCell.columnIdentifier;
},
get sortOrder()
{
if (!this._sortColumnCell || this._sortColumnCell.hasStyleClass("sort-ascending"))
return "ascending";
if (this._sortColumnCell.hasStyleClass("sort-descending"))
return "descending";
return null;
},
get headerTableBody()
{
if ("_headerTableBody" in this)
return this._headerTableBody;
this._headerTableBody = this._headerTable.getElementsByTagName("tbody")[0];
if (!this._headerTableBody) {
this._headerTableBody = this.element.ownerDocument.createElement("tbody");
this._headerTable.insertBefore(this._headerTableBody, this._headerTable.tFoot);
}
return this._headerTableBody;
},
get dataTableBody()
{
if ("_dataTableBody" in this)
return this._dataTableBody;
this._dataTableBody = this._dataTable.getElementsByTagName("tbody")[0];
if (!this._dataTableBody) {
this._dataTableBody = this.element.ownerDocument.createElement("tbody");
this._dataTable.insertBefore(this._dataTableBody, this._dataTable.tFoot);
}
return this._dataTableBody;
},
appendChild: function(child)
{
this.insertChild(child, this.children.length);
},
insertChild: function(child, index)
{
if (!child)
throw("Node can't be undefined or null.");
if (child.parent === this)
throw("Node is already a child of this node.");
if (child.parent)
child.parent.removeChild(child);
var previousChild = (index > 0 ? this.children[index - 1] : null);
if (previousChild) {
previousChild.nextSibling = child;
child.previousSibling = previousChild;
} else
child.previousSibling = null;
var nextChild = this.children[index];
if (nextChild) {
nextChild.previousSibling = child;
child.nextSibling = nextChild;
} else
child.nextSibling = null;
this.children.splice(index, 0, child);
this.hasChildren = true;
child.parent = this;
child.dataGrid = this.dataGrid;
delete child._depth;
delete child._revealed;
delete child._attached;
var current = child.children[0];
while (current) {
current.dataGrid = this.dataGrid;
delete current._depth;
delete current._revealed;
delete current._attached;
current = current.traverseNextNode(false, child, true);
}
if (this.expanded)
child._attach();
},
removeChild: function(child)
{
if (!child)
throw("Node can't be undefined or null.");
if (child.parent !== this)
throw("Node is not a child of this node.");
child.deselect();
this.children.remove(child, true);
if (child.previousSibling)
child.previousSibling.nextSibling = child.nextSibling;
if (child.nextSibling)
child.nextSibling.previousSibling = child.previousSibling;
child.dataGrid = null;
child.parent = null;
child.nextSibling = null;
child.previousSibling = null;
},
removeChildren: function()
{
for (var i = 0; i < this.children.length; ++i) {
var child = this.children[i];
child.deselect();
child._detach();
child.dataGrid = null;
child.parent = null;
child.nextSibling = null;
child.previousSibling = null;
}
this.children = [];
},
removeChildrenRecursive: function()
{
var childrenToRemove = this.children;
var child = this.children[0];
while (child) {
if (child.children.length)
childrenToRemove = childrenToRemove.concat(child.children);
child = child.traverseNextNode(false, this, true);
}
for (var i = 0; i < childrenToRemove.length; ++i) {
var child = childrenToRemove[i];
child.deselect();
child._detach();
child.children = [];
child.dataGrid = null;
child.parent = null;
child.nextSibling = null;
child.previousSibling = null;
}
this.children = [];
},
handleKeyEvent: function(event)
{
if (!this.selectedNode || event.shiftKey || event.metaKey || event.ctrlKey)
return false;
var handled = false;
var nextSelectedNode;
if (event.keyIdentifier === "Up" && !event.altKey) {
nextSelectedNode = this.selectedNode.traversePreviousNode(true);
while (nextSelectedNode && !nextSelectedNode.selectable)
nextSelectedNode = nextSelectedNode.traversePreviousNode(!this.expandTreeNodesWhenArrowing);
handled = nextSelectedNode ? true : false;
} else if (event.keyIdentifier === "Down" && !event.altKey) {
nextSelectedNode = this.selectedNode.traverseNextNode(true);
while (nextSelectedNode && !nextSelectedNode.selectable)
nextSelectedNode = nextSelectedNode.traverseNextNode(!this.expandTreeNodesWhenArrowing);
handled = nextSelectedNode ? true : false;
} else if (event.keyIdentifier === "Left") {
if (this.selectedNode.expanded) {
if (event.altKey)
this.selectedNode.collapseRecursively();
else
this.selectedNode.collapse();
handled = true;
} else if (this.selectedNode.parent && !this.selectedNode.parent.root) {
handled = true;
if (this.selectedNode.parent.selectable) {
nextSelectedNode = this.selectedNode.parent;
handled = nextSelectedNode ? true : false;
} else if (this.selectedNode.parent)
this.selectedNode.parent.collapse();
}
} else if (event.keyIdentifier === "Right") {
if (!this.selectedNode.revealed) {
this.selectedNode.reveal();
handled = true;
} else if (this.selectedNode.hasChildren) {
handled = true;
if (this.selectedNode.expanded) {
nextSelectedNode = this.selectedNode.children[0];
handled = nextSelectedNode ? true : false;
} else {
if (event.altKey)
this.selectedNode.expandRecursively();
else
this.selectedNode.expand();
}
}
}
if (nextSelectedNode) {
nextSelectedNode.reveal();
nextSelectedNode.select();
}
if (handled) {
event.preventDefault();
event.stopPropagation();
}
return handled;
},
expand: function()
{
// This is the root, do nothing.
},
collapse: function()
{
// This is the root, do nothing.
},
reveal: function()
{
// This is the root, do nothing.
},
dataGridNodeFromEvent: function(event)
{
var rowElement = event.target.enclosingNodeOrSelfWithNodeName("tr");
return rowElement._dataGridNode;
},
dataGridNodeFromPoint: function(x, y)
{
var node = this._dataTable.ownerDocument.elementFromPoint(x, y);
var rowElement = node.enclosingNodeOrSelfWithNodeName("tr");
return rowElement._dataGridNode;
},
_keyDown: function(event)
{
this.handleKeyEvent(event);
},
_clickInHeaderCell: function(event)
{
var cell = event.target.enclosingNodeOrSelfWithNodeName("th");
if (!cell || !cell.columnIdentifier || !cell.hasStyleClass("sortable"))
return;
var sortOrder = this.sortOrder;
if (this._sortColumnCell) {
this._sortColumnCell.removeStyleClass("sort-ascending");
this._sortColumnCell.removeStyleClass("sort-descending");
}
if (cell == this._sortColumnCell) {
if (sortOrder == "ascending")
sortOrder = "descending";
else
sortOrder = "ascending";
}
this._sortColumnCell = cell;
cell.addStyleClass("sort-" + sortOrder);
this.dispatchEventToListeners("sorting changed");
},
_mouseDownInDataTable: function(event)
{
var gridNode = this.dataGridNodeFromEvent(event);
if (!gridNode || !gridNode.selectable)
return;
if (gridNode.isEventWithinDisclosureTriangle(event))
return;
if (event.metaKey) {
if (gridNode.selected)
gridNode.deselect();
else
gridNode.select();
} else
gridNode.select();
},
_clickInDataTable: function(event)
{
var gridNode = this.dataGridNodeFromEvent(event);
if (!gridNode || !gridNode.hasChildren)
return;
if (!gridNode.isEventWithinDisclosureTriangle(event))
return;
if (gridNode.expanded) {
if (event.altKey)
gridNode.collapseRecursively();
else
gridNode.collapse();
} else {
if (event.altKey)
gridNode.expandRecursively();
else
gridNode.expand();
}
}
}
WebInspector.DataGrid.prototype.__proto__ = WebInspector.Object.prototype;
WebInspector.DataGridNode = function(data, hasChildren)
{
this._expanded = false;
this._selected = false;
this._shouldRefreshChildren = true;
this._data = data || {};
this.hasChildren = hasChildren || false;
this.children = [];
this.dataGrid = null;
this.parent = null;
this.previousSibling = null;
this.nextSibling = null;
this.disclosureToggleWidth = 10;
}
WebInspector.DataGridNode.prototype = {
selectable: true,
get element()
{
if (this._element)
return this._element;
if (!this.dataGrid)
return null;
this._element = document.createElement("tr");
this._element._dataGridNode = this;
if (this.hasChildren)
this._element.addStyleClass("parent");
if (this.expanded)
this._element.addStyleClass("expanded");
if (this.selected)
this._element.addStyleClass("selected");
if (this.revealed)
this._element.addStyleClass("revealed");
for (var columnIdentifier in this.dataGrid.columns) {
var cell = this.createCell(columnIdentifier);
this._element.appendChild(cell);
}
return this._element;
},
get data()
{
return this._data;
},
set data(x)
{
this._data = x || {};
this.refresh();
},
get revealed()
{
if ("_revealed" in this)
return this._revealed;
var currentAncestor = this.parent;
while (currentAncestor && !currentAncestor.root) {
if (!currentAncestor.expanded) {
this._revealed = false;
return false;
}
currentAncestor = currentAncestor.parent;
}
this._revealed = true;
return true;
},
set revealed(x)
{
if (this._revealed === x)
return;
this._revealed = x;
if (this._element) {
if (this._revealed)
this._element.addStyleClass("revealed");
else
this._element.removeStyleClass("revealed");
}
for (var i = 0; i < this.children.length; ++i)
this.children[i].revealed = x && this.expanded;
},
get depth()
{
if ("_depth" in this)
return this._depth;
if (this.parent && !this.parent.root)
this._depth = this.parent.depth + 1;
else
this._depth = 0;
return this._depth;
},
get shouldRefreshChildren()
{
return this._shouldRefreshChildren;
},
set shouldRefreshChildren(x)
{
this._shouldRefreshChildren = x;
if (x && this.expanded)
this.expand();
},
get selected()
{
return this._selected;
},
set selected(x)
{
if (x)
this.select();
else
this.deselect();
},
get expanded()
{
return this._expanded;
},
set expanded(x)
{
if (x)
this.expand();
else
this.collapse();
},
refresh: function()
{
if (!this._element || !this.dataGrid)
return;
this._element.removeChildren();
for (var columnIdentifier in this.dataGrid.columns) {
var cell = this.createCell(columnIdentifier);
this._element.appendChild(cell);
}
},
createCell: function(columnIdentifier)
{
var cell = document.createElement("td");
cell.className = columnIdentifier + "-column";
var div = document.createElement("div");
div.textContent = this.data[columnIdentifier];
cell.appendChild(div);
if (columnIdentifier === this.dataGrid.disclosureColumnIdentifier) {
cell.addStyleClass("disclosure");
if (this.depth)
cell.style.setProperty("padding-left", (this.depth * this.dataGrid.indentWidth) + "px");
}
return cell;
},
// Share these functions with DataGrid. They are written to work with a DataGridNode this object.
appendChild: WebInspector.DataGrid.prototype.appendChild,
insertChild: WebInspector.DataGrid.prototype.insertChild,
removeChild: WebInspector.DataGrid.prototype.removeChild,
removeChildren: WebInspector.DataGrid.prototype.removeChildren,
removeChildrenRecursive: WebInspector.DataGrid.prototype.removeChildrenRecursive,
collapse: function()
{
if (this._element)
this._element.removeStyleClass("expanded");
this._expanded = false;
for (var i = 0; i < this.children.length; ++i)
this.children[i].revealed = false;
this.dispatchEventToListeners("collapsed");
},
collapseRecursively: function()
{
var item = this;
while (item) {
if (item.expanded)
item.collapse();
item = item.traverseNextNode(false, this, true);
}
},
expand: function()
{
if (!this.hasChildren || this.expanded)
return;
if (this.revealed && !this._shouldRefreshChildren)
for (var i = 0; i < this.children.length; ++i)
this.children[i].revealed = true;
if (this._shouldRefreshChildren) {
for (var i = 0; i < this.children.length; ++i)
this.children[i]._detach();
this.dispatchEventToListeners("populate");
if (this._attached) {
for (var i = 0; i < this.children.length; ++i) {
var child = this.children[i];
if (this.revealed)
child.revealed = true;
child._attach();
}
}
delete this._shouldRefreshChildren;
}
if (this._element)
this._element.addStyleClass("expanded");
this._expanded = true;
this.dispatchEventToListeners("expanded");
},
expandRecursively: function()
{
var item = this;
while (item) {
item.expand();
item = item.traverseNextNode(false, this);
}
},
reveal: function()
{
var currentAncestor = this.parent;
while (currentAncestor && !currentAncestor.root) {
if (!currentAncestor.expanded)
currentAncestor.expand();
currentAncestor = currentAncestor.parent;
}
this.element.scrollIntoViewIfNeeded(false);
this.dispatchEventToListeners("revealed");
},
select: function(supressSelectedEvent)
{
if (!this.dataGrid || !this.selectable || this.selected)
return;
if (this.dataGrid.selectedNode)
this.dataGrid.selectedNode.deselect();
this._selected = true;
this.dataGrid.selectedNode = this;
if (this._element)
this._element.addStyleClass("selected");
if (!supressSelectedEvent)
this.dispatchEventToListeners("selected");
},
deselect: function(supressDeselectedEvent)
{
if (!this.dataGrid || this.dataGrid.selectedNode !== this || !this.selected)
return;
this._selected = false;
this.dataGrid.selectedNode = null;
if (this._element)
this._element.removeStyleClass("selected");
if (!supressDeselectedEvent)
this.dispatchEventToListeners("deselected");
},
traverseNextNode: function(skipHidden, stayWithin, dontPopulate, info)
{
if (!dontPopulate && this.hasChildren)
this.dispatchEventToListeners("populate");
if (info)
info.depthChange = 0;
var node = (!skipHidden || this.revealed) ? this.children[0] : null;
if (node && (!skipHidden || this.expanded)) {
if (info)
info.depthChange = 1;
return node;
}
if (this === stayWithin)
return null;
node = (!skipHidden || this.revealed) ? this.nextSibling : null;
if (node)
return node;
node = this;
while (node && !node.root && !((!skipHidden || node.revealed) ? node.nextSibling : null) && node.parent !== stayWithin) {
if (info)
info.depthChange -= 1;
node = node.parent;
}
if (!node)
return null;
return (!skipHidden || node.revealed) ? node.nextSibling : null;
},
traversePreviousNode: function(skipHidden, dontPopulate)
{
var node = (!skipHidden || this.revealed) ? this.previousSibling : null;
if (!dontPopulate && node && node.hasChildren)
node.dispatchEventToListeners("populate");
while (node && ((!skipHidden || (node.revealed && node.expanded)) ? node.children[node.children.length - 1] : null)) {
if (!dontPopulate && node.hasChildren)
node.dispatchEventToListeners("populate");
node = ((!skipHidden || (node.revealed && node.expanded)) ? node.children[node.children.length - 1] : null);
}
if (node)
return node;
if (!this.parent || this.parent.root)
return null;
return this.parent;
},
isEventWithinDisclosureTriangle: function(event)
{
if (!this.hasChildren)
return false;
var cell = event.target.enclosingNodeOrSelfWithNodeName("td");
if (!cell.hasStyleClass("disclosure"))
return false;
var computedLeftPadding = window.getComputedStyle(cell).getPropertyCSSValue("padding-left").getFloatValue(CSSPrimitiveValue.CSS_PX);
var left = cell.totalOffsetLeft + computedLeftPadding;
return event.pageX >= left && event.pageX <= left + this.disclosureToggleWidth;
},
_attach: function()
{
if (!this.dataGrid || this._attached)
return;
this._attached = true;
var nextNode = null;
var previousNode = this.traversePreviousNode(true, true);
if (previousNode && previousNode.element.parentNode && previousNode.element.nextSibling)
var nextNode = previousNode.element.nextSibling;
if (!nextNode)
nextNode = this.dataGrid.dataTableBody.lastChild;
this.dataGrid.dataTableBody.insertBefore(this.element, nextNode);
if (this.expanded)
for (var i = 0; i < this.children.length; ++i)
this.children[i]._attach();
},
_detach: function()
{
if (!this._attached)
return;
this._attached = false;
if (this._element && this._element.parentNode)
this._element.parentNode.removeChild(this._element);
for (var i = 0; i < this.children.length; ++i)
this.children[i]._detach();
}
}
WebInspector.DataGridNode.prototype.__proto__ = WebInspector.Object.prototype;
/* Script.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
WebInspector.Script = function(sourceID, sourceURL, source, startingLine, errorLine, errorMessage)
{
this.sourceID = sourceID;
this.sourceURL = sourceURL;
this.source = source;
this.startingLine = startingLine;
this.errorLine = errorLine;
this.errorMessage = errorMessage;
}
WebInspector.Script.prototype = {
}
/* Breakpoint.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
WebInspector.Breakpoint = function(url, line, sourceID)
{
this.url = url;
this.line = line;
this.sourceID = sourceID;
this._enabled = true;
}
WebInspector.Breakpoint.prototype = {
get enabled()
{
return this._enabled;
},
set enabled(x)
{
if (this._enabled === x)
return;
this._enabled = x;
if (this._enabled)
this.dispatchEventToListeners("enabled");
else
this.dispatchEventToListeners("disabled");
}
}
WebInspector.Breakpoint.prototype.__proto__ = WebInspector.Object.prototype;
/* SidebarPane.js */
/*
* Copyright (C) 2007 Apple Inc. All rights reserved.
*
* 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.
*/
WebInspector.SidebarPane = function(title)
{
this.element = document.createElement("div");
this.element.className = "pane";
this.titleElement = document.createElement("div");
this.titleElement.className = "title";
this.titleElement.addEventListener("click", this.toggleExpanded.bind(this), false);
this.bodyElement = document.createElement("div");
this.bodyElement.className = "body";
this.element.appendChild(this.titleElement);
this.element.appendChild(this.bodyElement);
this.title = title;
this.growbarVisible = false;
this.expanded = false;
}
WebInspector.SidebarPane.prototype = {
get title()
{
return this._title;
},
set title(x)
{
if (this._title === x)
return;
this._title = x;
this.titleElement.textContent = x;
},
get growbarVisible()
{
return this._growbarVisible;
},
set growbarVisible(x)
{
if (this._growbarVisible === x)
return;
this._growbarVisible = x;
if (x && !this._growbarElement) {
this._growbarElement = document.createElement("div");
this._growbarElement.className = "growbar";
this.element.appendChild(this._growbarElement);
} else if (!x && this._growbarElement) {
if (this._growbarElement.parentNode)
this._growbarElement.parentNode(this._growbarElement);
delete this._growbarElement;
}
},
get expanded()
{
return this._expanded;
},
set expanded(x)
{
if (x)
this.expand();
else
this.collapse();
},
expand: function()
{
if (this._expanded)
return;
this._expanded = true;
this.element.addStyleClass("expanded");
if (this.onexpand)
this.onexpand(this);
},
collapse: function()
{
if (!this._expanded)
return;
this._expanded = false;
this.element.removeStyleClass("expanded");
if (this.oncollapse)
this.oncollapse(this);
},
toggleExpanded: function()
{
this.expanded = !this.expanded;
}
}
WebInspector.SidebarPane.prototype.__proto__ = WebInspector.Object.prototype;
/* ElementsTreeOutline.js */
WebInspector.ElementsTreeOutline = function() {
this.element = document.createElement("ol");
this.element.addEventListener("mousedown", this._onmousedown.bind(this), false);
this.element.addEventListener("dblclick", this._ondblclick.bind(this), false);
this.element.addEventListener("mousemove", this._onmousemove.bind(this), false);
this.element.addEventListener("mouseout", this._onmouseout.bind(this), false);
TreeOutline.call(this, this.element);
this.includeRootDOMNode = true;
this.selectEnabled = false;
this.rootDOMNode = null;
this.focusedDOMNode = null;
}
WebInspector.ElementsTreeOutline.prototype = {
get rootDOMNode()
{
return this._rootDOMNode;
},
set rootDOMNode(x)
{
if (objectsAreSame(this._rootDOMNode, x))
return;
this._rootDOMNode = x;
this.update();
},
get focusedDOMNode()
{
return this._focusedDOMNode;
},
set focusedDOMNode(x)
{
if (objectsAreSame(this._focusedDOMNode, x)) {
this.revealAndSelectNode(x);
return;
}
this._focusedDOMNode = x;
this.revealAndSelectNode(x);
// The revealAndSelectNode() method might find a different element if there is inlined text,
// and the select() call would change the focusedDOMNode and reenter this setter. So to
// avoid calling focusedNodeChanged() twice, first check if _focusedDOMNode is the same
// node as the one passed in.
if (objectsAreSame(this._focusedDOMNode, x)) {
this.focusedNodeChanged();
if (x && !this.suppressSelectHighlight) {
InspectorController.highlightDOMNode(x);
if ("_restorePreviousHighlightNodeTimeout" in this)
clearTimeout(this._restorePreviousHighlightNodeTimeout);
function restoreHighlightToHoveredNode()
{
var hoveredNode = WebInspector.hoveredDOMNode;
if (hoveredNode)
InspectorController.highlightDOMNode(hoveredNode);
else
InspectorController.hideDOMNodeHighlight();
}
this._restorePreviousHighlightNodeTimeout = setTimeout(restoreHighlightToHoveredNode, 2000);
}
}
},
update: function()
{
this.removeChildren();
if (!this.rootDOMNode)
return;
var treeElement;
if (this.includeRootDOMNode) {
treeElement = new WebInspector.ElementsTreeElement(this.rootDOMNode);
treeElement.selectable = this.selectEnabled;
this.appendChild(treeElement);
} else {
// FIXME: this could use findTreeElement to reuse a tree element if it already exists
var node = (Preferences.ignoreWhitespace ? firstChildSkippingWhitespace.call(this.rootDOMNode) : this.rootDOMNode.firstChild);
while (node) {
treeElement = new WebInspector.ElementsTreeElement(node);
treeElement.selectable = this.selectEnabled;
this.appendChild(treeElement);
node = Preferences.ignoreWhitespace ? nextSiblingSkippingWhitespace.call(node) : node.nextSibling;
}
}
this.updateSelection();
},
updateSelection: function()
{
if (!this.selectedTreeElement)
return;
var element = this.treeOutline.selectedTreeElement;
element.updateSelection();
},
focusedNodeChanged: function(forceUpdate) {},
findTreeElement: function(node, isAncestor, getParent, equal)
{
if (typeof isAncestor === "undefined")
isAncestor = isAncestorIncludingParentFrames;
if (typeof getParent === "undefined")
getParent = parentNodeOrFrameElement;
if (typeof equal === "undefined")
equal = objectsAreSame;
var treeElement = TreeOutline.prototype.findTreeElement.call(this, node, isAncestor, getParent, equal);
if (!treeElement && node.nodeType === Node.TEXT_NODE) {
// The text node might have been inlined if it was short, so try to find the parent element.
treeElement = TreeOutline.prototype.findTreeElement.call(this, node.parentNode, isAncestor, getParent, equal);
}
return treeElement;
},
revealAndSelectNode: function(node)
{
if (!node)
return;
var treeElement = this.findTreeElement(node);
if (!treeElement)
return;
treeElement.reveal();
treeElement.select();
},
_treeElementFromEvent: function(event)
{
var root = this.element;
// We choose this X coordinate based on the knowledge that our list
// items extend nearly to the right edge of the outer <ol>.
var x = root.totalOffsetLeft + root.offsetWidth - 20;
var y = event.pageY;
// Our list items have 1-pixel cracks between them vertically. We avoid
// the cracks by checking slightly above and slightly below the mouse
// and seeing if we hit the same element each time.
var elementUnderMouse = this.treeElementFromPoint(x, y);
var elementAboveMouse = this.treeElementFromPoint(x, y - 2);
var element;
if (elementUnderMouse === elementAboveMouse)
element = elementUnderMouse;
else
element = this.treeElementFromPoint(x, y + 2);
return element;
},
_ondblclick: function(event)
{
var element = this._treeElementFromEvent(event);
if (!element || !element.ondblclick)
return;
element.ondblclick(element, event);
},
_onmousedown: function(event)
{
var element = this._treeElementFromEvent(event);
if (!element || element.isEventWithinDisclosureTriangle(event))
return;
element.select();
},
_onmousemove: function(event)
{
if (this._previousHoveredElement) {
this._previousHoveredElement.hovered = false;
delete this._previousHoveredElement;
}
var element = this._treeElementFromEvent(event);
if (element && !element.elementCloseTag) {
element.hovered = true;
this._previousHoveredElement = element;
}
WebInspector.hoveredDOMNode = (element && !element.elementCloseTag ? element.representedObject : null);
},
_onmouseout: function(event)
{
var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
if (nodeUnderMouse.isDescendant(this.element))
return;
if (this._previousHoveredElement) {
this._previousHoveredElement.hovered = false;
delete this._previousHoveredElement;
}
WebInspector.hoveredDOMNode = null;
}
}
WebInspector.ElementsTreeOutline.prototype.__proto__ = TreeOutline.prototype;
WebInspector.ElementsTreeElement = function(node)
{
var hasChildren = node.contentDocument || (Preferences.ignoreWhitespace ? (firstChildSkippingWhitespace.call(node) ? true : false) : node.hasChildNodes());
var titleInfo = nodeTitleInfo.call(node, hasChildren, WebInspector.linkifyURL);
if (titleInfo.hasChildren)
this.whitespaceIgnored = Preferences.ignoreWhitespace;
// The title will be updated in onattach.
TreeElement.call(this, "", node, titleInfo.hasChildren);
}
WebInspector.ElementsTreeElement.prototype = {
get highlighted()
{
return this._highlighted;
},
set highlighted(x)
{
if (this._highlighted === x)
return;
this._highlighted = x;
if (this.listItemElement) {
if (x)
this.listItemElement.addStyleClass("highlighted");
else
this.listItemElement.removeStyleClass("highlighted");
}
},
get hovered()
{
return this._hovered;
},
set hovered(x)
{
if (this._hovered === x)
return;
this._hovered = x;
if (this.listItemElement) {
if (x) {
this.updateSelection();
this.listItemElement.addStyleClass("hovered");
} else
this.listItemElement.removeStyleClass("hovered");
}
},
updateSelection: function()
{
var listItemElement = this.listItemElement;
if (!listItemElement)
return;
if (document.body.offsetWidth <= 0) {
// The stylesheet hasn't loaded yet or the window is closed,
// so we can't calculate what is need. Return early.
return;
}
if (!this.selectionElement) {
this.selectionElement = document.createElement("div");
this.selectionElement.className = "selection selected";
listItemElement.insertBefore(this.selectionElement, listItemElement.firstChild);
}
this.selectionElement.style.height = listItemElement.offsetHeight + "px";
},
onattach: function()
{
this.listItemElement.addEventListener("mousedown", this.onmousedown.bind(this), false);
if (this._highlighted)
this.listItemElement.addStyleClass("highlighted");
if (this._hovered) {
this.updateSelection();
this.listItemElement.addStyleClass("hovered");
}
this._updateTitle();
this._preventFollowingLinksOnDoubleClick();
},
_preventFollowingLinksOnDoubleClick: function()
{
var links = this.listItemElement.querySelectorAll("li > .webkit-html-tag > .webkit-html-attribute > .webkit-html-external-link, li > .webkit-html-tag > .webkit-html-attribute > .webkit-html-resource-link");
if (!links)
return;
for (var i = 0; i < links.length; ++i)
links[i].preventFollowOnDoubleClick = true;
},
onpopulate: function()
{
if (this.children.length || this.whitespaceIgnored !== Preferences.ignoreWhitespace)
return;
this.whitespaceIgnored = Preferences.ignoreWhitespace;
this.updateChildren();
},
updateChildren: function(fullRefresh)
{
if (fullRefresh) {
var selectedTreeElement = this.treeOutline.selectedTreeElement;
if (selectedTreeElement && selectedTreeElement.hasAncestor(this))
this.select();
this.removeChildren();
}
var treeElement = this;
var treeChildIndex = 0;
function updateChildrenOfNode(node)
{
var treeOutline = treeElement.treeOutline;
var child = (Preferences.ignoreWhitespace ? firstChildSkippingWhitespace.call(node) : node.firstChild);
while (child) {
var currentTreeElement = treeElement.children[treeChildIndex];
if (!currentTreeElement || !objectsAreSame(currentTreeElement.representedObject, child)) {
// Find any existing element that is later in the children list.
var existingTreeElement = null;
for (var i = (treeChildIndex + 1); i < treeElement.children.length; ++i) {
if (objectsAreSame(treeElement.children[i].representedObject, child)) {
existingTreeElement = treeElement.children[i];
break;
}
}
if (existingTreeElement && existingTreeElement.parent === treeElement) {
// If an existing element was found and it has the same parent, just move it.
var wasSelected = existingTreeElement.selected;
treeElement.removeChild(existingTreeElement);
treeElement.insertChild(existingTreeElement, treeChildIndex);
if (wasSelected)
existingTreeElement.select();
} else {
// No existing element found, insert a new element.
var newElement = new WebInspector.ElementsTreeElement(child);
newElement.selectable = treeOutline.selectEnabled;
treeElement.insertChild(newElement, treeChildIndex);
}
}
child = Preferences.ignoreWhitespace ? nextSiblingSkippingWhitespace.call(child) : child.nextSibling;
++treeChildIndex;
}
}
// Remove any tree elements that no longer have this node (or this node's contentDocument) as their parent.
for (var i = (this.children.length - 1); i >= 0; --i) {
if ("elementCloseTag" in this.children[i])
continue;
var currentChild = this.children[i];
var currentNode = currentChild.representedObject;
var currentParentNode = currentNode.parentNode;
if (objectsAreSame(currentParentNode, this.representedObject))
continue;
if (this.representedObject.contentDocument && objectsAreSame(currentParentNode, this.representedObject.contentDocument))
continue;
var selectedTreeElement = this.treeOutline.selectedTreeElement;
if (selectedTreeElement && (selectedTreeElement === currentChild || selectedTreeElement.hasAncestor(currentChild)))
this.select();
this.removeChildAtIndex(i);
if (this.treeOutline.panel && currentNode.contentDocument)
this.treeOutline.panel.unregisterMutationEventListeners(currentNode.contentDocument.defaultView);
}
if (this.representedObject.contentDocument)
updateChildrenOfNode(this.representedObject.contentDocument);
updateChildrenOfNode(this.representedObject);
var lastChild = this.children[this.children.length - 1];
if (this.representedObject.nodeType == Node.ELEMENT_NODE && (!lastChild || !lastChild.elementCloseTag)) {
var title = "<span class=\"webkit-html-tag close\">&lt;/" + this.representedObject.nodeName.toLowerCase().escapeHTML() + "&gt;</span>";
var item = new TreeElement(title, null, false);
item.selectable = false;
item.elementCloseTag = true;
this.appendChild(item);
}
},
onexpand: function()
{
this.treeOutline.updateSelection();
if (this.treeOutline.panel && this.representedObject.contentDocument)
this.treeOutline.panel.registerMutationEventListeners(this.representedObject.contentDocument.defaultView);
},
oncollapse: function()
{
this.treeOutline.updateSelection();
},
onreveal: function()
{
if (this.listItemElement)
this.listItemElement.scrollIntoViewIfNeeded(false);
},
onselect: function()
{
this.treeOutline.focusedDOMNode = this.representedObject;
this.updateSelection();
},
onmousedown: function(event)
{
if (this._editing)
return;
// Prevent selecting the nearest word on double click.
if (event.detail >= 2)
event.preventDefault();
},
ondblclick: function(treeElement, event)
{
if (this._editing)
return;
if (this._startEditing(event))
return;
if (this.treeOutline.panel) {
this.treeOutline.rootDOMNode = this.parent.representedObject;
this.treeOutline.focusedDOMNode = this.representedObject;
}
if (this.hasChildren && !this.expanded)
this.expand();
},
_startEditing: function(event)
{
if (this.treeOutline.focusedDOMNode != this.representedObject)
return;
if (this.representedObject.nodeType != Node.ELEMENT_NODE && this.representedObject.nodeType != Node.TEXT_NODE)
return false;
var textNode = event.target.enclosingNodeOrSelfWithClass("webkit-html-text-node");
if (textNode)
return this._startEditingTextNode(textNode);
var attribute = event.target.enclosingNodeOrSelfWithClass("webkit-html-attribute");
if (attribute)
return this._startEditingAttribute(attribute, event);
return false;
},
_startEditingAttribute: function(attribute, event)
{
if (WebInspector.isBeingEdited(attribute))
return true;
var attributeNameElement = attribute.getElementsByClassName("webkit-html-attribute-name")[0];
if (!attributeNameElement)
return false;
var attributeName = attributeNameElement.innerText;
function removeZeroWidthSpaceRecursive(node)
{
if (node.nodeType === Node.TEXT_NODE) {
node.nodeValue = node.nodeValue.replace(/\u200B/g, "");
return;
}
if (node.nodeType !== Node.ELEMENT_NODE)
return;
for (var child = node.firstChild; child; child = child.nextSibling)
removeZeroWidthSpaceRecursive(child);
}
// Remove zero-width spaces that were added by nodeTitleInfo.
removeZeroWidthSpaceRecursive(attribute);
this._editing = true;
WebInspector.startEditing(attribute, this._attributeEditingCommitted.bind(this), this._editingCancelled.bind(this), attributeName);
window.getSelection().setBaseAndExtent(event.target, 0, event.target, 1);
return true;
},
_startEditingTextNode: function(textNode)
{
if (WebInspector.isBeingEdited(textNode))
return true;
this._editing = true;
WebInspector.startEditing(textNode, this._textNodeEditingCommitted.bind(this), this._editingCancelled.bind(this));
window.getSelection().setBaseAndExtent(textNode, 0, textNode, 1);
return true;
},
_attributeEditingCommitted: function(element, newText, oldText, attributeName)
{
delete this._editing;
var parseContainerElement = document.createElement("span");
parseContainerElement.innerHTML = "<span " + newText + "></span>";
var parseElement = parseContainerElement.firstChild;
if (!parseElement || !parseElement.hasAttributes()) {
editingCancelled(element, context);
return;
}
var foundOriginalAttribute = false;
for (var i = 0; i < parseElement.attributes.length; ++i) {
var attr = parseElement.attributes[i];
foundOriginalAttribute = foundOriginalAttribute || attr.name === attributeName;
InspectorController.inspectedWindow().Element.prototype.setAttribute.call(this.representedObject, attr.name, attr.value);
}
if (!foundOriginalAttribute)
InspectorController.inspectedWindow().Element.prototype.removeAttribute.call(this.representedObject, attributeName);
this._updateTitle();
this.treeOutline.focusedNodeChanged(true);
},
_textNodeEditingCommitted: function(element, newText)
{
delete this._editing;
var textNode;
if (this.representedObject.nodeType == Node.ELEMENT_NODE) {
// We only show text nodes inline in elements if the element only
// has a single child, and that child is a text node.
textNode = this.representedObject.firstChild;
} else if (this.representedObject.nodeType == Node.TEXT_NODE)
textNode = this.representedObject;
textNode.nodeValue = newText;
this._updateTitle();
},
_editingCancelled: function(element, context)
{
delete this._editing;
this._updateTitle();
},
_updateTitle: function()
{
var title = nodeTitleInfo.call(this.representedObject, this.hasChildren, WebInspector.linkifyURL).title;
this.title = "<span class=\"highlight\">" + title + "</span>";
delete this.selectionElement;
this.updateSelection();
this._preventFollowingLinksOnDoubleClick();
},
}
WebInspector.ElementsTreeElement.prototype.__proto__ = TreeElement.prototype;
/* SidebarTreeElement.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
WebInspector.SidebarSectionTreeElement = function(title, representedObject, hasChildren)
{
TreeElement.call(this, title.escapeHTML(), representedObject || {}, hasChildren);
}
WebInspector.SidebarSectionTreeElement.prototype = {
selectable: false,
get smallChildren()
{
return this._smallChildren;
},
set smallChildren(x)
{
if (this._smallChildren === x)
return;
this._smallChildren = x;
if (this._smallChildren)
this._childrenListNode.addStyleClass("small");
else
this._childrenListNode.removeStyleClass("small");
},
onattach: function()
{
this._listItemNode.addStyleClass("sidebar-tree-section");
},
onreveal: function()
{
if (this.listItemElement)
this.listItemElement.scrollIntoViewIfNeeded(false);
}
}
WebInspector.SidebarSectionTreeElement.prototype.__proto__ = TreeElement.prototype;
WebInspector.SidebarTreeElement = function(className, title, subtitle, representedObject, hasChildren)
{
TreeElement.call(this, "", representedObject || {}, hasChildren);
if (hasChildren) {
this.disclosureButton = document.createElement("button");
this.disclosureButton.className = "disclosure-button";
}
if (!this.iconElement) {
this.iconElement = document.createElement("img");
this.iconElement.className = "icon";
}
this.statusElement = document.createElement("div");
this.statusElement.className = "status";
this.titlesElement = document.createElement("div");
this.titlesElement.className = "titles";
this.titleElement = document.createElement("span");
this.titleElement.className = "title";
this.titlesElement.appendChild(this.titleElement);
this.subtitleElement = document.createElement("span");
this.subtitleElement.className = "subtitle";
this.titlesElement.appendChild(this.subtitleElement);
this.className = className;
this.mainTitle = title;
this.subtitle = subtitle;
}
WebInspector.SidebarTreeElement.prototype = {
get small()
{
return this._small;
},
set small(x)
{
this._small = x;
if (this._listItemNode) {
if (this._small)
this._listItemNode.addStyleClass("small");
else
this._listItemNode.removeStyleClass("small");
}
},
get mainTitle()
{
return this._mainTitle;
},
set mainTitle(x)
{
this._mainTitle = x;
this.refreshTitles();
},
get subtitle()
{
return this._subtitle;
},
set subtitle(x)
{
this._subtitle = x;
this.refreshTitles();
},
get bubbleText()
{
return this._bubbleText;
},
set bubbleText(x)
{
if (!this.bubbleElement) {
this.bubbleElement = document.createElement("div");
this.bubbleElement.className = "bubble";
this.statusElement.appendChild(this.bubbleElement);
}
this._bubbleText = x;
this.bubbleElement.textContent = x;
},
refreshTitles: function()
{
var mainTitle = this.mainTitle;
if (this.titleElement.textContent !== mainTitle)
this.titleElement.textContent = mainTitle;
var subtitle = this.subtitle;
if (subtitle) {
if (this.subtitleElement.textContent !== subtitle)
this.subtitleElement.textContent = subtitle;
this.titlesElement.removeStyleClass("no-subtitle");
} else
this.titlesElement.addStyleClass("no-subtitle");
},
isEventWithinDisclosureTriangle: function(event)
{
return event.target === this.disclosureButton;
},
onattach: function()
{
this._listItemNode.addStyleClass("sidebar-tree-item");
if (this.className)
this._listItemNode.addStyleClass(this.className);
if (this.small)
this._listItemNode.addStyleClass("small");
if (this.hasChildren && this.disclosureButton)
this._listItemNode.appendChild(this.disclosureButton);
this._listItemNode.appendChild(this.iconElement);
this._listItemNode.appendChild(this.statusElement);
this._listItemNode.appendChild(this.titlesElement);
},
onreveal: function()
{
if (this._listItemNode)
this._listItemNode.scrollIntoViewIfNeeded(false);
}
}
WebInspector.SidebarTreeElement.prototype.__proto__ = TreeElement.prototype;
/* PropertiesSection.js */
/*
* Copyright (C) 2007 Apple Inc. All rights reserved.
*
* 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.
*/
WebInspector.PropertiesSection = function(title, subtitle)
{
this.element = document.createElement("div");
this.element.className = "section";
this.headerElement = document.createElement("div");
this.headerElement.className = "header";
this.titleElement = document.createElement("div");
this.titleElement.className = "title";
this.subtitleElement = document.createElement("div");
this.subtitleElement.className = "subtitle";
this.headerElement.appendChild(this.subtitleElement);
this.headerElement.appendChild(this.titleElement);
this.headerElement.addEventListener("click", this.toggleExpanded.bind(this), false);
this.propertiesElement = document.createElement("ol");
this.propertiesElement.className = "properties";
this.propertiesTreeOutline = new TreeOutline(this.propertiesElement);
this.propertiesTreeOutline.section = this;
this.element.appendChild(this.headerElement);
this.element.appendChild(this.propertiesElement);
this.title = title;
this.subtitle = subtitle;
this.expanded = false;
}
WebInspector.PropertiesSection.prototype = {
get title()
{
return this._title;
},
set title(x)
{
if (this._title === x)
return;
this._title = x;
this.titleElement.textContent = x;
},
get subtitle()
{
return this._subtitle;
},
set subtitle(x)
{
if (this._subtitle === x)
return;
this._subtitle = x;
this.subtitleElement.innerHTML = x;
},
get expanded()
{
return this._expanded;
},
set expanded(x)
{
if (x)
this.expand();
else
this.collapse();
},
get populated()
{
return this._populated;
},
set populated(x)
{
this._populated = x;
if (!x && this.onpopulate && this._expanded) {
this.onpopulate(this);
this._populated = true;
}
},
expand: function()
{
if (this._expanded)
return;
this._expanded = true;
this.element.addStyleClass("expanded");
if (!this._populated && this.onpopulate) {
this.onpopulate(this);
this._populated = true;
}
},
collapse: function()
{
if (!this._expanded)
return;
this._expanded = false;
this.element.removeStyleClass("expanded");
},
toggleExpanded: function()
{
this.expanded = !this.expanded;
}
}
/* ObjectPropertiesSection.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
WebInspector.ObjectPropertiesSection = function(object, title, subtitle, emptyPlaceholder, ignoreHasOwnProperty, extraProperties, treeElementConstructor)
{
if (!title) {
title = Object.describe(object);
if (title.match(/Prototype$/)) {
title = title.replace(/Prototype$/, "");
if (!subtitle)
subtitle = WebInspector.UIString("Prototype");
}
}
this.emptyPlaceholder = (emptyPlaceholder || WebInspector.UIString("No Properties"));
this.object = object;
this.ignoreHasOwnProperty = ignoreHasOwnProperty;
this.extraProperties = extraProperties;
this.treeElementConstructor = treeElementConstructor || WebInspector.ObjectPropertyTreeElement;
this.editable = true;
WebInspector.PropertiesSection.call(this, title, subtitle);
}
WebInspector.ObjectPropertiesSection.prototype = {
onpopulate: function()
{
this.update();
},
update: function()
{
var properties = [];
for (var prop in this.object)
properties.push(prop);
if (this.extraProperties)
for (var prop in this.extraProperties)
properties.push(prop);
properties.sort();
this.propertiesTreeOutline.removeChildren();
for (var i = 0; i < properties.length; ++i) {
var object = this.object;
var propertyName = properties[i];
if (this.extraProperties && propertyName in this.extraProperties)
object = this.extraProperties;
if (propertyName === "__treeElementIdentifier")
continue;
if (!this.ignoreHasOwnProperty && "hasOwnProperty" in object && !object.hasOwnProperty(propertyName))
continue;
this.propertiesTreeOutline.appendChild(new this.treeElementConstructor(object, propertyName));
}
if (!this.propertiesTreeOutline.children.length) {
var title = "<div class=\"info\">" + this.emptyPlaceholder + "</div>";
var infoElement = new TreeElement(title, null, false);
this.propertiesTreeOutline.appendChild(infoElement);
}
}
}
WebInspector.ObjectPropertiesSection.prototype.__proto__ = WebInspector.PropertiesSection.prototype;
WebInspector.ObjectPropertyTreeElement = function(parentObject, propertyName)
{
this.parentObject = parentObject;
this.propertyName = propertyName;
// Pass an empty title, the title gets made later in onattach.
TreeElement.call(this, "", null, false);
}
WebInspector.ObjectPropertyTreeElement.prototype = {
safePropertyValue: function(object, propertyName)
{
if (object["__lookupGetter__"] && object.__lookupGetter__(propertyName))
return;
return object[propertyName];
},
onpopulate: function()
{
if (this.children.length && !this.shouldRefreshChildren)
return;
this.removeChildren();
var childObject = this.safePropertyValue(this.parentObject, this.propertyName);
var properties = Object.sortedProperties(childObject);
for (var i = 0; i < properties.length; ++i) {
var propertyName = properties[i];
if (propertyName === "__treeElementIdentifier")
continue;
this.appendChild(new this.treeOutline.section.treeElementConstructor(childObject, propertyName));
}
},
ondblclick: function(element, event)
{
this.startEditing();
},
onattach: function()
{
this.update();
},
update: function()
{
var childObject = this.safePropertyValue(this.parentObject, this.propertyName);
var isGetter = ("__lookupGetter__" in this.parentObject && this.parentObject.__lookupGetter__(this.propertyName));
var nameElement = document.createElement("span");
nameElement.className = "name";
nameElement.textContent = this.propertyName;
this.valueElement = document.createElement("span");
this.valueElement.className = "value";
if (!isGetter) {
this.valueElement.textContent = Object.describe(childObject, true);
} else {
// FIXME: this should show something like "getter" (bug 16734).
this.valueElement.textContent = "\u2014"; // em dash
this.valueElement.addStyleClass("dimmed");
}
this.listItemElement.removeChildren();
this.listItemElement.appendChild(nameElement);
this.listItemElement.appendChild(document.createTextNode(": "));
this.listItemElement.appendChild(this.valueElement);
var hasSubProperties = false;
var type = typeof childObject;
if (childObject && (type === "object" || type === "function")) {
for (subPropertyName in childObject) {
if (subPropertyName === "__treeElementIdentifier")
continue;
hasSubProperties = true;
break;
}
}
this.hasChildren = hasSubProperties;
},
updateSiblings: function()
{
if (this.parent.root)
this.treeOutline.section.update();
else
this.parent.shouldRefreshChildren = true;
},
startEditing: function()
{
if (WebInspector.isBeingEdited(this.valueElement) || !this.treeOutline.section.editable)
return;
var context = { expanded: this.expanded };
// Lie about our children to prevent expanding on double click and to collapse subproperties.
this.hasChildren = false;
this.listItemElement.addStyleClass("editing-sub-part");
WebInspector.startEditing(this.valueElement, this.editingCommitted.bind(this), this.editingCancelled.bind(this), context);
},
editingEnded: function(context)
{
this.listItemElement.scrollLeft = 0;
this.listItemElement.removeStyleClass("editing-sub-part");
if (context.expanded)
this.expand();
},
editingCancelled: function(element, context)
{
this.update();
this.editingEnded(context);
},
editingCommitted: function(element, userInput, previousContent, context)
{
if (userInput === previousContent)
return this.editingCancelled(element, context); // nothing changed, so cancel
this.applyExpression(userInput, true);
this.editingEnded(context);
},
evaluateExpression: function(expression)
{
// Evaluate in the currently selected call frame if the debugger is paused.
// Otherwise evaluate in against the inspected window.
if (WebInspector.panels.scripts.paused && this.treeOutline.section.editInSelectedCallFrameWhenPaused)
return WebInspector.panels.scripts.evaluateInSelectedCallFrame(expression, false);
return InspectorController.inspectedWindow().eval(expression);
},
applyExpression: function(expression, updateInterface)
{
var expressionLength = expression.trimWhitespace().length;
if (!expressionLength) {
// The user deleted everything, so try to delete the property.
delete this.parentObject[this.propertyName];
if (updateInterface) {
if (this.propertyName in this.parentObject) {
// The property was not deleted, so update.
this.update();
} else {
// The property was deleted, so remove this tree element.
this.parent.removeChild(this);
}
}
return;
}
try {
// Surround the expression in parenthesis so the result of the eval is the result
// of the whole expression not the last potential sub-expression.
var result = this.evaluateExpression("(" + expression + ")");
// Store the result in the property.
this.parentObject[this.propertyName] = result;
} catch(e) {
try {
// Try to update as a string
var result = this.evaluateExpression("\"" + expression.escapeCharacters("\"") + "\"");
// Store the result in the property.
this.parentObject[this.propertyName] = result;
} catch(e) {
// The expression failed so don't change the value. So just update and return.
if (updateInterface)
this.update();
return;
}
}
if (updateInterface) {
// Call updateSiblings since their value might be based on the value that just changed.
this.updateSiblings();
}
}
}
WebInspector.ObjectPropertyTreeElement.prototype.__proto__ = TreeElement.prototype;
/* BreakpointsSidebarPane.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
WebInspector.BreakpointsSidebarPane = function()
{
WebInspector.SidebarPane.call(this, WebInspector.UIString("Breakpoints"));
this.breakpoints = [];
this.emptyElement = document.createElement("div");
this.emptyElement.className = "info";
this.emptyElement.textContent = WebInspector.UIString("No Breakpoints");
this.bodyElement.appendChild(this.emptyElement);
}
WebInspector.BreakpointsSidebarPane.prototype = {
addBreakpoint: function(breakpoint)
{
this.breakpoints.push(breakpoint);
breakpoint.addEventListener("enabled", this._breakpointEnableChanged, this);
breakpoint.addEventListener("disabled", this._breakpointEnableChanged, this);
// FIXME: add to the breakpoints UI.
if (!InspectorController.debuggerAttached() || !breakpoint.sourceID)
return;
if (breakpoint.enabled)
InspectorController.addBreakpoint(breakpoint.sourceID, breakpoint.line);
},
removeBreakpoint: function(breakpoint)
{
this.breakpoints.remove(breakpoint);
breakpoint.removeEventListener("enabled", null, this);
breakpoint.removeEventListener("disabled", null, this);
// FIXME: remove from the breakpoints UI.
if (!InspectorController.debuggerAttached() || !breakpoint.sourceID)
return;
InspectorController.removeBreakpoint(breakpoint.sourceID, breakpoint.line);
},
_breakpointEnableChanged: function(event)
{
var breakpoint = event.target;
// FIXME: change the breakpoint checkbox state in the UI.
if (!InspectorController.debuggerAttached() || !breakpoint.sourceID)
return;
if (breakpoint.enabled)
InspectorController.addBreakpoint(breakpoint.sourceID, breakpoint.line);
else
InspectorController.removeBreakpoint(breakpoint.sourceID, breakpoint.line);
}
}
WebInspector.BreakpointsSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
/* CallStackSidebarPane.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
WebInspector.CallStackSidebarPane = function()
{
WebInspector.SidebarPane.call(this, WebInspector.UIString("Call Stack"));
}
WebInspector.CallStackSidebarPane.prototype = {
update: function(callFrame, sourceIDMap)
{
this.bodyElement.removeChildren();
this.placards = [];
delete this._selectedCallFrame;
if (!callFrame) {
var infoElement = document.createElement("div");
infoElement.className = "info";
infoElement.textContent = WebInspector.UIString("Not Paused");
this.bodyElement.appendChild(infoElement);
return;
}
var title;
var subtitle;
var scriptOrResource;
do {
switch (callFrame.type) {
case "function":
title = callFrame.functionName || WebInspector.UIString("(anonymous function)");
break;
case "program":
title = WebInspector.UIString("(program)");
break;
}
scriptOrResource = sourceIDMap[callFrame.sourceID];
subtitle = WebInspector.displayNameForURL(scriptOrResource.sourceURL || scriptOrResource.url);
if (callFrame.line > 0) {
if (subtitle)
subtitle += ":" + callFrame.line;
else
subtitle = WebInspector.UIString("line %d", callFrame.line);
}
var placard = new WebInspector.Placard(title, subtitle);
placard.callFrame = callFrame;
placard.element.addEventListener("click", this._placardSelected.bind(this), false);
this.placards.push(placard);
this.bodyElement.appendChild(placard.element);
callFrame = callFrame.caller;
} while (callFrame);
},
get selectedCallFrame()
{
return this._selectedCallFrame;
},
set selectedCallFrame(x)
{
if (this._selectedCallFrame === x)
return;
this._selectedCallFrame = x;
for (var i = 0; i < this.placards.length; ++i) {
var placard = this.placards[i];
placard.selected = (placard.callFrame === this._selectedCallFrame);
}
this.dispatchEventToListeners("call frame selected");
},
_placardSelected: function(event)
{
var placardElement = event.target.enclosingNodeOrSelfWithClass("placard");
this.selectedCallFrame = placardElement.placard.callFrame;
}
}
WebInspector.CallStackSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
/* ScopeChainSidebarPane.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
WebInspector.ScopeChainSidebarPane = function()
{
WebInspector.SidebarPane.call(this, WebInspector.UIString("Scope Variables"));
}
WebInspector.ScopeChainSidebarPane.prototype = {
update: function(callFrame)
{
this.bodyElement.removeChildren();
this.sections = [];
this.callFrame = callFrame;
if (!callFrame) {
var infoElement = document.createElement("div");
infoElement.className = "info";
infoElement.textContent = WebInspector.UIString("Not Paused");
this.bodyElement.appendChild(infoElement);
return;
}
if (!callFrame._expandedProperties) {
// FIXME: fix this when https://bugs.webkit.org/show_bug.cgi?id=19410 is fixed.
// The callFrame is a JSInspectedObjectWrapper, so we are not allowed to assign
// an object created in the Inspector's context to that object. So create an
// Object from the inspectedWindow.
var inspectedWindow = InspectorController.inspectedWindow();
callFrame._expandedProperties = new inspectedWindow.Object;
}
var foundLocalScope = false;
var scopeChain = callFrame.scopeChain;
for (var i = 0; i < scopeChain.length; ++i) {
var scopeObject = scopeChain[i];
var title = null;
var subtitle = Object.describe(scopeObject, true);
var emptyPlaceholder = null;
var localScope = false;
var extraProperties = null;
if (Object.prototype.toString.call(scopeObject) === "[object JSActivation]") {
if (!foundLocalScope) {
extraProperties = { "this": callFrame.thisObject };
title = WebInspector.UIString("Local");
} else
title = WebInspector.UIString("Closure");
emptyPlaceholder = WebInspector.UIString("No Variables");
subtitle = null;
foundLocalScope = true;
localScope = true;
} else if (i === (scopeChain.length - 1))
title = WebInspector.UIString("Global");
else if (foundLocalScope && scopeObject instanceof InspectorController.inspectedWindow().Element)
title = WebInspector.UIString("Event Target");
else if (foundLocalScope && scopeObject instanceof InspectorController.inspectedWindow().Document)
title = WebInspector.UIString("Event Document");
else if (!foundLocalScope && !localScope)
title = WebInspector.UIString("With Block");
if (!title || title === subtitle)
subtitle = null;
var section = new WebInspector.ObjectPropertiesSection(scopeObject, title, subtitle, emptyPlaceholder, true, extraProperties, WebInspector.ScopeVariableTreeElement);
section.editInSelectedCallFrameWhenPaused = true;
section.pane = this;
if (!foundLocalScope || localScope)
section.expanded = true;
this.sections.push(section);
this.bodyElement.appendChild(section.element);
}
}
}
WebInspector.ScopeChainSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
WebInspector.ScopeVariableTreeElement = function(parentObject, propertyName)
{
WebInspector.ObjectPropertyTreeElement.call(this, parentObject, propertyName);
}
WebInspector.ScopeVariableTreeElement.prototype = {
onattach: function()
{
WebInspector.ObjectPropertyTreeElement.prototype.onattach.call(this);
if (this.hasChildren && this.propertyIdentifier in this.treeOutline.section.pane.callFrame._expandedProperties)
this.expand();
},
onexpand: function()
{
this.treeOutline.section.pane.callFrame._expandedProperties[this.propertyIdentifier] = true;
},
oncollapse: function()
{
delete this.treeOutline.section.pane.callFrame._expandedProperties[this.propertyIdentifier];
},
get propertyIdentifier()
{
if ("_propertyIdentifier" in this)
return this._propertyIdentifier;
var section = this.treeOutline.section;
this._propertyIdentifier = section.title + ":" + (section.subtitle ? section.subtitle + ":" : "") + this.propertyPath;
return this._propertyIdentifier;
},
get propertyPath()
{
if ("_propertyPath" in this)
return this._propertyPath;
var current = this;
var result;
do {
if (result)
result = current.propertyName + "." + result;
else
result = current.propertyName;
current = current.parent;
} while (current && !current.root);
this._propertyPath = result;
return result;
}
}
WebInspector.ScopeVariableTreeElement.prototype.__proto__ = WebInspector.ObjectPropertyTreeElement.prototype;
/* MetricsSidebarPane.js */
/*
* Copyright (C) 2007 Apple Inc. All rights reserved.
*
* 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.
*/
WebInspector.MetricsSidebarPane = function()
{
WebInspector.SidebarPane.call(this, WebInspector.UIString("Metrics"));
}
WebInspector.MetricsSidebarPane.prototype = {
update: function(node)
{
var body = this.bodyElement;
body.removeChildren();
if (node)
this.node = node;
else
node = this.node;
if (!node || !node.ownerDocument.defaultView)
return;
var style;
if (node.nodeType === Node.ELEMENT_NODE)
style = node.ownerDocument.defaultView.getComputedStyle(node);
if (!style)
return;
var metricsElement = document.createElement("div");
metricsElement.className = "metrics";
function createBoxPartElement(style, name, side, suffix)
{
var propertyName = (name !== "position" ? name + "-" : "") + side + suffix;
var value = style.getPropertyValue(propertyName);
if (value === "" || (name !== "position" && value === "0px"))
value = "\u2012";
else if (name === "position" && value === "auto")
value = "\u2012";
value = value.replace(/px$/, "");
var element = document.createElement("div");
element.className = side;
element.textContent = value;
element.addEventListener("dblclick", this.startEditing.bind(this, element, name, propertyName), false);
return element;
}
// Display types for which margin is ignored.
var noMarginDisplayType = {
"table-cell": true,
"table-column": true,
"table-column-group": true,
"table-footer-group": true,
"table-header-group": true,
"table-row": true,
"table-row-group": true
};
// Display types for which padding is ignored.
var noPaddingDisplayType = {
"table-column": true,
"table-column-group": true,
"table-footer-group": true,
"table-header-group": true,
"table-row": true,
"table-row-group": true
};
// Position types for which top, left, bottom and right are ignored.
var noPositionType = {
"static": true
};
var boxes = ["content", "padding", "border", "margin", "position"];
var boxLabels = [WebInspector.UIString("content"), WebInspector.UIString("padding"), WebInspector.UIString("border"), WebInspector.UIString("margin"), WebInspector.UIString("position")];
var previousBox;
for (var i = 0; i < boxes.length; ++i) {
var name = boxes[i];
if (name === "margin" && noMarginDisplayType[style.display])
continue;
if (name === "padding" && noPaddingDisplayType[style.display])
continue;
if (name === "position" && noPositionType[style.position])
continue;
var boxElement = document.createElement("div");
boxElement.className = name;
if (name === "content") {
var width = style.width.replace(/px$/, "");
var widthElement = document.createElement("span");
widthElement.textContent = width;
widthElement.addEventListener("dblclick", this.startEditing.bind(this, widthElement, "width", "width"), false);
var height = style.height.replace(/px$/, "");
var heightElement = document.createElement("span");
heightElement.textContent = height;
heightElement.addEventListener("dblclick", this.startEditing.bind(this, heightElement, "height", "height"), false);
boxElement.appendChild(widthElement);
boxElement.appendChild(document.createTextNode(" \u00D7 "));
boxElement.appendChild(heightElement);
} else {
var suffix = (name === "border" ? "-width" : "");
var labelElement = document.createElement("div");
labelElement.className = "label";
labelElement.textContent = boxLabels[i];
boxElement.appendChild(labelElement);
boxElement.appendChild(createBoxPartElement.call(this, style, name, "top", suffix));
boxElement.appendChild(document.createElement("br"));
boxElement.appendChild(createBoxPartElement.call(this, style, name, "left", suffix));
if (previousBox)
boxElement.appendChild(previousBox);
boxElement.appendChild(createBoxPartElement.call(this, style, name, "right", suffix));
boxElement.appendChild(document.createElement("br"));
boxElement.appendChild(createBoxPartElement.call(this, style, name, "bottom", suffix));
}
previousBox = boxElement;
}
metricsElement.appendChild(previousBox);
body.appendChild(metricsElement);
},
startEditing: function(targetElement, box, styleProperty)
{
if (WebInspector.isBeingEdited(targetElement))
return;
var context = { box: box, styleProperty: styleProperty };
WebInspector.startEditing(targetElement, this.editingCommitted.bind(this), this.editingCancelled.bind(this), context);
},
editingCancelled: function(element, context)
{
this.update();
},
editingCommitted: function(element, userInput, previousContent, context)
{
if (userInput === previousContent)
return this.editingCancelled(element, context); // nothing changed, so cancel
if (context.box !== "position" && (!userInput || userInput === "\u2012"))
userInput = "0px";
else if (context.box === "position" && (!userInput || userInput === "\u2012"))
userInput = "auto";
// Append a "px" unit if the user input was just a number.
if (/^\d+$/.test(userInput))
userInput += "px";
this.node.style.setProperty(context.styleProperty, userInput, "");
this.dispatchEventToListeners("metrics edited");
this.update();
}
}
WebInspector.MetricsSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
/* PropertiesSidebarPane.js */
/*
* Copyright (C) 2007 Apple Inc. All rights reserved.
*
* 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.
*/
WebInspector.PropertiesSidebarPane = function()
{
WebInspector.SidebarPane.call(this, WebInspector.UIString("Properties"));
}
WebInspector.PropertiesSidebarPane.prototype = {
update: function(object)
{
var body = this.bodyElement;
body.removeChildren();
this.sections = [];
if (!object)
return;
for (var prototype = object; prototype; prototype = prototype.__proto__) {
var section = new WebInspector.ObjectPropertiesSection(prototype);
this.sections.push(section);
body.appendChild(section.element);
}
}
}
WebInspector.PropertiesSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
/* StylesSidebarPane.js */
/*
* Copyright (C) 2007 Apple Inc. All rights reserved.
*
* 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.
*/
WebInspector.StylesSidebarPane = function()
{
WebInspector.SidebarPane.call(this, WebInspector.UIString("Styles"));
}
WebInspector.StylesSidebarPane.prototype = {
update: function(node, editedSection, forceUpdate)
{
var refresh = false;
if (forceUpdate)
delete this.node;
if (!forceUpdate && (!node || node === this.node))
refresh = true;
if (node && node.nodeType === Node.TEXT_NODE && node.parentNode)
node = node.parentNode;
if (node && node.nodeType !== Node.ELEMENT_NODE)
node = null;
if (node)
this.node = node;
else
node = this.node;
var body = this.bodyElement;
if (!refresh || !node) {
body.removeChildren();
this.sections = [];
}
if (!node)
return;
var styleRules = [];
if (refresh) {
for (var i = 0; i < this.sections.length; ++i) {
var section = this.sections[i];
if (section.computedStyle)
section.styleRule.style = node.ownerDocument.defaultView.getComputedStyle(node);
var styleRule = { section: section, style: section.styleRule.style, computedStyle: section.computedStyle };
styleRules.push(styleRule);
}
} else {
var computedStyle = node.ownerDocument.defaultView.getComputedStyle(node);
styleRules.push({ computedStyle: true, selectorText: WebInspector.UIString("Computed Style"), style: computedStyle, editable: false });
var nodeName = node.nodeName.toLowerCase();
for (var i = 0; i < node.attributes.length; ++i) {
var attr = node.attributes[i];
if (attr.style) {
var attrStyle = { style: attr.style, editable: false };
attrStyle.subtitle = WebInspector.UIString("element’s “%s” attribute", attr.name);
attrStyle.selectorText = nodeName + "[" + attr.name;
if (attr.value.length)
attrStyle.selectorText += "=" + attr.value;
attrStyle.selectorText += "]";
styleRules.push(attrStyle);
}
}
if (node.style && (node.style.length || Object.hasProperties(node.style.__disabledProperties))) {
var inlineStyle = { selectorText: WebInspector.UIString("Inline Style Attribute"), style: node.style };
inlineStyle.subtitle = WebInspector.UIString("element’s “%s” attribute", "style");
styleRules.push(inlineStyle);
}
var matchedStyleRules = node.ownerDocument.defaultView.getMatchedCSSRules(node, "", !Preferences.showUserAgentStyles);
if (matchedStyleRules) {
// Add rules in reverse order to match the cascade order.
for (var i = (matchedStyleRules.length - 1); i >= 0; --i) {
var rule = matchedStyleRules[i];
styleRules.push({ style: rule.style, selectorText: rule.selectorText, parentStyleSheet: rule.parentStyleSheet });
}
}
}
function deleteDisabledProperty(style, name)
{
if (!style || !name)
return;
if (style.__disabledPropertyValues)
delete style.__disabledPropertyValues[name];
if (style.__disabledPropertyPriorities)
delete style.__disabledPropertyPriorities[name];
if (style.__disabledProperties)
delete style.__disabledProperties[name];
}
var usedProperties = {};
var disabledComputedProperties = {};
var priorityUsed = false;
// Walk the style rules and make a list of all used and overloaded properties.
for (var i = 0; i < styleRules.length; ++i) {
var styleRule = styleRules[i];
if (styleRule.computedStyle)
continue;
styleRule.usedProperties = {};
var style = styleRule.style;
for (var j = 0; j < style.length; ++j) {
var name = style[j];
if (!priorityUsed && style.getPropertyPriority(name).length)
priorityUsed = true;
// If the property name is already used by another rule then this rule's
// property is overloaded, so don't add it to the rule's usedProperties.
if (!(name in usedProperties))
styleRule.usedProperties[name] = true;
if (name === "font") {
// The font property is not reported as a shorthand. Report finding the individual
// properties so they are visible in computed style.
// FIXME: remove this when http://bugs.webkit.org/show_bug.cgi?id=15598 is fixed.
styleRule.usedProperties["font-family"] = true;
styleRule.usedProperties["font-size"] = true;
styleRule.usedProperties["font-style"] = true;
styleRule.usedProperties["font-variant"] = true;
styleRule.usedProperties["font-weight"] = true;
styleRule.usedProperties["line-height"] = true;
}
// Delete any disabled properties, since the property does exist.
// This prevents it from showing twice.
deleteDisabledProperty(style, name);
deleteDisabledProperty(style, style.getPropertyShorthand(name));
}
// Add all the properties found in this style to the used properties list.
// Do this here so only future rules are affect by properties used in this rule.
for (var name in styleRules[i].usedProperties)
usedProperties[name] = true;
// Remember all disabled properties so they show up in computed style.
if (style.__disabledProperties)
for (var name in style.__disabledProperties)
disabledComputedProperties[name] = true;
}
if (priorityUsed) {
// Walk the properties again and account for !important.
var foundPriorityProperties = [];
// Walk in reverse to match the order !important overrides.
for (var i = (styleRules.length - 1); i >= 0; --i) {
if (styleRules[i].computedStyle)
continue;
var style = styleRules[i].style;
var uniqueProperties = getUniqueStyleProperties(style);
for (var j = 0; j < uniqueProperties.length; ++j) {
var name = uniqueProperties[j];
if (style.getPropertyPriority(name).length) {
if (!(name in foundPriorityProperties))
styleRules[i].usedProperties[name] = true;
else
delete styleRules[i].usedProperties[name];
foundPriorityProperties[name] = true;
} else if (name in foundPriorityProperties)
delete styleRules[i].usedProperties[name];
}
}
}
if (refresh) {
// Walk the style rules and update the sections with new overloaded and used properties.
for (var i = 0; i < styleRules.length; ++i) {
var styleRule = styleRules[i];
var section = styleRule.section;
if (styleRule.computedStyle)
section.disabledComputedProperties = disabledComputedProperties;
section._usedProperties = (styleRule.usedProperties || usedProperties);
section.update((section === editedSection) || styleRule.computedStyle);
}
} else {
// Make a property section for each style rule.
for (var i = 0; i < styleRules.length; ++i) {
var styleRule = styleRules[i];
var subtitle = styleRule.subtitle;
delete styleRule.subtitle;
var computedStyle = styleRule.computedStyle;
delete styleRule.computedStyle;
var ruleUsedProperties = styleRule.usedProperties;
delete styleRule.usedProperties;
var editable = styleRule.editable;
delete styleRule.editable;
// Default editable to true if it was omitted.
if (typeof editable === "undefined")
editable = true;
var section = new WebInspector.StylePropertiesSection(styleRule, subtitle, computedStyle, (ruleUsedProperties || usedProperties), editable);
if (computedStyle)
section.disabledComputedProperties = disabledComputedProperties;
section.expanded = true;
section.pane = this;
body.appendChild(section.element);
this.sections.push(section);
}
}
}
}
WebInspector.StylesSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
WebInspector.StylePropertiesSection = function(styleRule, subtitle, computedStyle, usedProperties, editable)
{
WebInspector.PropertiesSection.call(this, styleRule.selectorText);
this.styleRule = styleRule;
this.computedStyle = computedStyle;
this.editable = (editable && !computedStyle);
// Prevent editing the user agent and user rules.
var isUserAgent = this.styleRule.parentStyleSheet && !this.styleRule.parentStyleSheet.ownerNode && !this.styleRule.parentStyleSheet.href;
var isUser = this.styleRule.parentStyleSheet && this.styleRule.parentStyleSheet.ownerNode && this.styleRule.parentStyleSheet.ownerNode.nodeName == '#document';
if (isUserAgent || isUser)
this.editable = false;
this._usedProperties = usedProperties;
if (computedStyle) {
this.element.addStyleClass("computed-style");
if (Preferences.showInheritedComputedStyleProperties)
this.element.addStyleClass("show-inherited");
var showInheritedLabel = document.createElement("label");
var showInheritedInput = document.createElement("input");
showInheritedInput.type = "checkbox";
showInheritedInput.checked = Preferences.showInheritedComputedStyleProperties;
var computedStyleSection = this;
var showInheritedToggleFunction = function(event) {
Preferences.showInheritedComputedStyleProperties = showInheritedInput.checked;
if (Preferences.showInheritedComputedStyleProperties)
computedStyleSection.element.addStyleClass("show-inherited");
else
computedStyleSection.element.removeStyleClass("show-inherited");
event.stopPropagation();
};
showInheritedLabel.addEventListener("click", showInheritedToggleFunction, false);
showInheritedLabel.appendChild(showInheritedInput);
showInheritedLabel.appendChild(document.createTextNode(WebInspector.UIString("Show inherited")));
this.subtitleElement.appendChild(showInheritedLabel);
} else {
if (!subtitle) {
if (this.styleRule.parentStyleSheet && this.styleRule.parentStyleSheet.href) {
var url = this.styleRule.parentStyleSheet.href;
subtitle = WebInspector.linkifyURL(url, WebInspector.displayNameForURL(url));
this.subtitleElement.addStyleClass("file");
} else if (isUserAgent)
subtitle = WebInspector.UIString("user agent stylesheet");
else if (isUser)
subtitle = WebInspector.UIString("user stylesheet");
else
subtitle = WebInspector.UIString("inline stylesheet");
}
this.subtitle = subtitle;
}
}
WebInspector.StylePropertiesSection.prototype = {
get usedProperties()
{
return this._usedProperties || {};
},
set usedProperties(x)
{
this._usedProperties = x;
this.update();
},
isPropertyInherited: function(property)
{
if (!this.computedStyle || !this._usedProperties)
return false;
// These properties should always show for Computed Style.
var alwaysShowComputedProperties = { "display": true, "height": true, "width": true };
return !(property in this.usedProperties) && !(property in alwaysShowComputedProperties) && !(property in this.disabledComputedProperties);
},
isPropertyOverloaded: function(property, shorthand)
{
if (this.computedStyle || !this._usedProperties)
return false;
var used = (property in this.usedProperties);
if (used || !shorthand)
return !used;
// Find out if any of the individual longhand properties of the shorthand
// are used, if none are then the shorthand is overloaded too.
var longhandProperties = getLonghandProperties(this.styleRule.style, property);
for (var j = 0; j < longhandProperties.length; ++j) {
var individualProperty = longhandProperties[j];
if (individualProperty in this.usedProperties)
return false;
}
return true;
},
update: function(full)
{
if (full || this.computedStyle) {
this.propertiesTreeOutline.removeChildren();
this.populated = false;
} else {
var child = this.propertiesTreeOutline.children[0];
while (child) {
child.overloaded = this.isPropertyOverloaded(child.name, child.shorthand);
child = child.traverseNextTreeElement(false, null, true);
}
}
},
onpopulate: function()
{
var style = this.styleRule.style;
var foundShorthands = {};
var uniqueProperties = getUniqueStyleProperties(style);
var disabledProperties = style.__disabledPropertyValues || {};
for (var name in disabledProperties)
uniqueProperties.push(name);
uniqueProperties.sort();
for (var i = 0; i < uniqueProperties.length; ++i) {
var name = uniqueProperties[i];
var disabled = name in disabledProperties;
if (!disabled && this.disabledComputedProperties && !(name in this.usedProperties) && name in this.disabledComputedProperties)
disabled = true;
var shorthand = !disabled ? style.getPropertyShorthand(name) : null;
if (shorthand && shorthand in foundShorthands)
continue;
if (shorthand) {
foundShorthands[shorthand] = true;
name = shorthand;
}
var isShorthand = (shorthand ? true : false);
var inherited = this.isPropertyInherited(name);
var overloaded = this.isPropertyOverloaded(name, isShorthand);
var item = new WebInspector.StylePropertyTreeElement(style, name, isShorthand, inherited, overloaded, disabled);
this.propertiesTreeOutline.appendChild(item);
}
}
}
WebInspector.StylePropertiesSection.prototype.__proto__ = WebInspector.PropertiesSection.prototype;
WebInspector.StylePropertyTreeElement = function(style, name, shorthand, inherited, overloaded, disabled)
{
this.style = style;
this.name = name;
this.shorthand = shorthand;
this._inherited = inherited;
this._overloaded = overloaded;
this._disabled = disabled;
// Pass an empty title, the title gets made later in onattach.
TreeElement.call(this, "", null, shorthand);
}
WebInspector.StylePropertyTreeElement.prototype = {
get inherited()
{
return this._inherited;
},
set inherited(x)
{
if (x === this._inherited)
return;
this._inherited = x;
this.updateState();
},
get overloaded()
{
return this._overloaded;
},
set overloaded(x)
{
if (x === this._overloaded)
return;
this._overloaded = x;
this.updateState();
},
get disabled()
{
return this._disabled;
},
set disabled(x)
{
if (x === this._disabled)
return;
this._disabled = x;
this.updateState();
},
get priority()
{
if (this.disabled && this.style.__disabledPropertyPriorities && this.name in this.style.__disabledPropertyPriorities)
return this.style.__disabledPropertyPriorities[this.name];
return (this.shorthand ? getShorthandPriority(this.style, this.name) : this.style.getPropertyPriority(this.name));
},
get value()
{
if (this.disabled && this.style.__disabledPropertyValues && this.name in this.style.__disabledPropertyValues)
return this.style.__disabledPropertyValues[this.name];
return (this.shorthand ? getShorthandValue(this.style, this.name) : this.style.getPropertyValue(this.name));
},
onattach: function()
{
this.updateTitle();
},
updateTitle: function()
{
// "Nicknames" for some common values that are easier to read.
var valueNicknames = {
"rgb(0, 0, 0)": "black",
"#000": "black",
"#000000": "black",
"rgb(255, 255, 255)": "white",
"#fff": "white",
"#ffffff": "white",
"#FFF": "white",
"#FFFFFF": "white",
"rgba(0, 0, 0, 0)": "transparent",
"rgb(255, 0, 0)": "red",
"rgb(0, 255, 0)": "lime",
"rgb(0, 0, 255)": "blue",
"rgb(255, 255, 0)": "yellow",
"rgb(255, 0, 255)": "magenta",
"rgb(0, 255, 255)": "cyan"
};
var priority = this.priority;
var value = this.value;
var htmlValue = value;
if (priority && !priority.length)
delete priority;
if (priority)
priority = "!" + priority;
if (value) {
var urls = value.match(/url\([^)]+\)/);
if (urls) {
for (var i = 0; i < urls.length; ++i) {
var url = urls[i].substring(4, urls[i].length - 1);
htmlValue = htmlValue.replace(urls[i], "url(" + WebInspector.linkifyURL(url) + ")");
}
} else {
if (value in valueNicknames)
htmlValue = valueNicknames[value];
htmlValue = htmlValue.escapeHTML();
}
} else
htmlValue = value = "";
this.updateState();
var enabledCheckboxElement = document.createElement("input");
enabledCheckboxElement.className = "enabled-button";
enabledCheckboxElement.type = "checkbox";
enabledCheckboxElement.checked = !this.disabled;
enabledCheckboxElement.addEventListener("change", this.toggleEnabled.bind(this), false);
var nameElement = document.createElement("span");
nameElement.className = "name";
nameElement.textContent = this.name;
var valueElement = document.createElement("span");
valueElement.className = "value";
valueElement.innerHTML = htmlValue;
if (priority) {
var priorityElement = document.createElement("span");
priorityElement.className = "priority";
priorityElement.textContent = priority;
}
this.listItemElement.removeChildren();
// Append the checkbox for root elements of an editable section.
if (this.treeOutline.section && this.treeOutline.section.editable && this.parent.root)
this.listItemElement.appendChild(enabledCheckboxElement);
this.listItemElement.appendChild(nameElement);
this.listItemElement.appendChild(document.createTextNode(": "));
this.listItemElement.appendChild(valueElement);
if (priorityElement) {
this.listItemElement.appendChild(document.createTextNode(" "));
this.listItemElement.appendChild(priorityElement);
}
this.listItemElement.appendChild(document.createTextNode(";"));
if (value) {
// FIXME: this dosen't catch keyword based colors like black and white
var colors = value.match(/((rgb|hsl)a?\([^)]+\))|(#[0-9a-fA-F]{6})|(#[0-9a-fA-F]{3})/g);
if (colors) {
var colorsLength = colors.length;
for (var i = 0; i < colorsLength; ++i) {
var swatchElement = document.createElement("span");
swatchElement.className = "swatch";
swatchElement.style.setProperty("background-color", colors[i]);
this.listItemElement.appendChild(swatchElement);
}
}
}
this.tooltip = this.name + ": " + (valueNicknames[value] || value) + (priority ? " " + priority : "");
},
updateAll: function(updateAllRules)
{
if (updateAllRules && this.treeOutline.section && this.treeOutline.section.pane)
this.treeOutline.section.pane.update(null, this.treeOutline.section);
else if (this.treeOutline.section)
this.treeOutline.section.update(true);
else
this.updateTitle(); // FIXME: this will not show new properties. But we don't hit his case yet.
},
toggleEnabled: function(event)
{
var disabled = !event.target.checked;
if (disabled) {
if (!this.style.__disabledPropertyValues || !this.style.__disabledPropertyPriorities) {
var inspectedWindow = InspectorController.inspectedWindow();
this.style.__disabledProperties = new inspectedWindow.Object;
this.style.__disabledPropertyValues = new inspectedWindow.Object;
this.style.__disabledPropertyPriorities = new inspectedWindow.Object;
}
this.style.__disabledPropertyValues[this.name] = this.value;
this.style.__disabledPropertyPriorities[this.name] = this.priority;
if (this.shorthand) {
var longhandProperties = getLonghandProperties(this.style, this.name);
for (var i = 0; i < longhandProperties.length; ++i) {
this.style.__disabledProperties[longhandProperties[i]] = true;
this.style.removeProperty(longhandProperties[i]);
}
} else {
this.style.__disabledProperties[this.name] = true;
this.style.removeProperty(this.name);
}
} else {
this.style.setProperty(this.name, this.value, this.priority);
delete this.style.__disabledProperties[this.name];
delete this.style.__disabledPropertyValues[this.name];
delete this.style.__disabledPropertyPriorities[this.name];
}
// Set the disabled property here, since the code above replies on it not changing
// until after the value and priority are retrieved.
this.disabled = disabled;
if (this.treeOutline.section && this.treeOutline.section.pane)
this.treeOutline.section.pane.dispatchEventToListeners("style property toggled");
this.updateAll(true);
},
updateState: function()
{
if (!this.listItemElement)
return;
if (this.style.isPropertyImplicit(this.name) || this.value === "initial")
this.listItemElement.addStyleClass("implicit");
else
this.listItemElement.removeStyleClass("implicit");
if (this.inherited)
this.listItemElement.addStyleClass("inherited");
else
this.listItemElement.removeStyleClass("inherited");
if (this.overloaded)
this.listItemElement.addStyleClass("overloaded");
else
this.listItemElement.removeStyleClass("overloaded");
if (this.disabled)
this.listItemElement.addStyleClass("disabled");
else
this.listItemElement.removeStyleClass("disabled");
},
onpopulate: function()
{
// Only populate once and if this property is a shorthand.
if (this.children.length || !this.shorthand)
return;
var longhandProperties = getLonghandProperties(this.style, this.name);
for (var i = 0; i < longhandProperties.length; ++i) {
var name = longhandProperties[i];
if (this.treeOutline.section) {
var inherited = this.treeOutline.section.isPropertyInherited(name);
var overloaded = this.treeOutline.section.isPropertyOverloaded(name);
}
var item = new WebInspector.StylePropertyTreeElement(this.style, name, false, inherited, overloaded);
this.appendChild(item);
}
},
ondblclick: function(element, event)
{
this.startEditing(event.target);
},
startEditing: function(selectElement)
{
// FIXME: we don't allow editing of longhand properties under a shorthand right now.
if (this.parent.shorthand)
return;
if (WebInspector.isBeingEdited(this.listItemElement) || (this.treeOutline.section && !this.treeOutline.section.editable))
return;
var context = { expanded: this.expanded, hasChildren: this.hasChildren };
// Lie about our children to prevent expanding on double click and to collapse shorthands.
this.hasChildren = false;
if (!selectElement)
selectElement = this.listItemElement;
this.listItemElement.handleKeyEvent = this.editingKeyDown.bind(this);
WebInspector.startEditing(this.listItemElement, this.editingCommitted.bind(this), this.editingCancelled.bind(this), context);
window.getSelection().setBaseAndExtent(selectElement, 0, selectElement, 1);
},
editingKeyDown: function(event)
{
var arrowKeyPressed = (event.keyIdentifier === "Up" || event.keyIdentifier === "Down");
var pageKeyPressed = (event.keyIdentifier === "PageUp" || event.keyIdentifier === "PageDown");
if (!arrowKeyPressed && !pageKeyPressed)
return;
var selection = window.getSelection();
if (!selection.rangeCount)
return;
var selectionRange = selection.getRangeAt(0);
if (selectionRange.commonAncestorContainer !== this.listItemElement && !selectionRange.commonAncestorContainer.isDescendant(this.listItemElement))
return;
const styleValueDelimeters = " \t\n\"':;,/()";
var wordRange = selectionRange.startContainer.rangeOfWord(selectionRange.startOffset, styleValueDelimeters, this.listItemElement);
var wordString = wordRange.toString();
var replacementString = wordString;
var matches = /(.*?)(-?\d+(?:\.\d+)?)(.*)/.exec(wordString);
if (matches && matches.length) {
var prefix = matches[1];
var number = parseFloat(matches[2]);
var suffix = matches[3];
// If the number is near zero or the number is one and the direction will take it near zero.
var numberNearZero = (number < 1 && number > -1);
if (number === 1 && event.keyIdentifier === "Down")
numberNearZero = true;
else if (number === -1 && event.keyIdentifier === "Up")
numberNearZero = true;
if (numberNearZero && event.altKey && arrowKeyPressed) {
if (event.keyIdentifier === "Down")
number = Math.ceil(number - 1);
else
number = Math.floor(number + 1);
} else {
// Jump by 10 when shift is down or jump by 0.1 when near zero or Alt/Option is down.
// Also jump by 10 for page up and down, or by 100 if shift is held with a page key.
var changeAmount = 1;
if (event.shiftKey && pageKeyPressed)
changeAmount = 100;
else if (event.shiftKey || pageKeyPressed)
changeAmount = 10;
else if (event.altKey || numberNearZero)
changeAmount = 0.1;
if (event.keyIdentifier === "Down" || event.keyIdentifier === "PageDown")
changeAmount *= -1;
// Make the new number and constrain it to a precision of 6, this matches numbers the engine returns.
// Use the Number constructor to forget the fixed precision, so 1.100000 will print as 1.1.
number = Number((number + changeAmount).toFixed(6));
}
replacementString = prefix + number + suffix;
} else {
// FIXME: this should cycle through known keywords for the current property name.
return;
}
var replacementTextNode = document.createTextNode(replacementString);
wordRange.deleteContents();
wordRange.insertNode(replacementTextNode);
var finalSelectionRange = document.createRange();
finalSelectionRange.setStart(replacementTextNode, 0);
finalSelectionRange.setEnd(replacementTextNode, replacementString.length);
selection.removeAllRanges();
selection.addRange(finalSelectionRange);
event.preventDefault();
event.handled = true;
if (!this.originalCSSText) {
// Remember the rule's original CSS text, so it can be restored
// if the editing is canceled and before each apply.
this.originalCSSText = getStyleTextWithShorthands(this.style);
} else {
// Restore the original CSS text before applying user changes. This is needed to prevent
// new properties from sticking around if the user adds one, then removes it.
this.style.cssText = this.originalCSSText;
}
this.applyStyleText(this.listItemElement.textContent);
},
editingEnded: function(context)
{
this.hasChildren = context.hasChildren;
if (context.expanded)
this.expand();
delete this.listItemElement.handleKeyEvent;
delete this.originalCSSText;
},
editingCancelled: function(element, context)
{
if (this.originalCSSText) {
this.style.cssText = this.originalCSSText;
if (this.treeOutline.section && this.treeOutline.section.pane)
this.treeOutline.section.pane.dispatchEventToListeners("style edited");
this.updateAll();
} else
this.updateTitle();
this.editingEnded(context);
},
editingCommitted: function(element, userInput, previousContent, context)
{
this.editingEnded(context);
if (userInput === previousContent)
return; // nothing changed, so do nothing else
this.applyStyleText(userInput, true);
},
applyStyleText: function(styleText, updateInterface)
{
var styleTextLength = styleText.trimWhitespace().length;
// Create a new element to parse the user input CSS.
var parseElement = document.createElement("span");
parseElement.setAttribute("style", styleText);
var tempStyle = parseElement.style;
if (tempStyle.length || !styleTextLength) {
// The input was parsable or the user deleted everything, so remove the
// original property from the real style declaration. If this represents
// a shorthand remove all the longhand properties.
if (this.shorthand) {
var longhandProperties = getLonghandProperties(this.style, this.name);
for (var i = 0; i < longhandProperties.length; ++i)
this.style.removeProperty(longhandProperties[i]);
} else
this.style.removeProperty(this.name);
}
if (!styleTextLength) {
if (updateInterface) {
// The user deleted the everything, so remove the tree element and update.
if (this.treeOutline.section && this.treeOutline.section.pane)
this.treeOutline.section.pane.update();
this.parent.removeChild(this);
}
return;
}
if (!tempStyle.length) {
// The user typed something, but it didn't parse. Just abort and restore
// the original title for this property.
if (updateInterface)
this.updateTitle();
return;
}
// Iterate of the properties on the test element's style declaration and
// add them to the real style declaration. We take care to move shorthands.
var foundShorthands = {};
var uniqueProperties = getUniqueStyleProperties(tempStyle);
for (var i = 0; i < uniqueProperties.length; ++i) {
var name = uniqueProperties[i];
var shorthand = tempStyle.getPropertyShorthand(name);
if (shorthand && shorthand in foundShorthands)
continue;
if (shorthand) {
var value = getShorthandValue(tempStyle, shorthand);
var priority = getShorthandPriority(tempStyle, shorthand);
foundShorthands[shorthand] = true;
} else {
var value = tempStyle.getPropertyValue(name);
var priority = tempStyle.getPropertyPriority(name);
}
// Set the property on the real style declaration.
this.style.setProperty((shorthand || name), value, priority);
}
if (this.treeOutline.section && this.treeOutline.section.pane)
this.treeOutline.section.pane.dispatchEventToListeners("style edited");
if (updateInterface)
this.updateAll(true);
}
}
WebInspector.StylePropertyTreeElement.prototype.__proto__ = TreeElement.prototype;
/* Panel.js */
/*
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
*
* 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.
*/
WebInspector.Panel = function()
{
WebInspector.View.call(this);
this.element.addStyleClass("panel");
}
WebInspector.Panel.prototype = {
get toolbarItem()
{
if (this._toolbarItem)
return this._toolbarItem;
// Sample toolbar item as markup:
// <button class="toolbar-item resources toggleable">
// <div class="toolbar-icon"></div>
// <div class="toolbar-label">Resources</div>
// </button>
this._toolbarItem = document.createElement("button");
this._toolbarItem.className = "toolbar-item toggleable";
this._toolbarItem.panel = this;
if ("toolbarItemClass" in this)
this._toolbarItem.addStyleClass(this.toolbarItemClass);
var iconElement = document.createElement("div");
iconElement.className = "toolbar-icon";
this._toolbarItem.appendChild(iconElement);
if ("toolbarItemLabel" in this) {
var labelElement = document.createElement("div");
labelElement.className = "toolbar-label";
labelElement.textContent = this.toolbarItemLabel;
this._toolbarItem.appendChild(labelElement);
}
return this._toolbarItem;
},
show: function()
{
WebInspector.View.prototype.show.call(this);
var statusBarItems = this.statusBarItems;
if (statusBarItems) {
this._statusBarItemContainer = document.createElement("div");
for (var i = 0; i < statusBarItems.length; ++i)
this._statusBarItemContainer.appendChild(statusBarItems[i]);
document.getElementById("main-status-bar").appendChild(this._statusBarItemContainer);
}
if ("_toolbarItem" in this)
this._toolbarItem.addStyleClass("toggled-on");
WebInspector.currentFocusElement = document.getElementById("main-panels");
},
hide: function()
{
WebInspector.View.prototype.hide.call(this);
if (this._statusBarItemContainer && this._statusBarItemContainer.parentNode)
this._statusBarItemContainer.parentNode.removeChild(this._statusBarItemContainer);
delete this._statusBarItemContainer;
if ("_toolbarItem" in this)
this._toolbarItem.removeStyleClass("toggled-on");
},
attach: function()
{
if (!this.element.parentNode)
document.getElementById("main-panels").appendChild(this.element);
},
searchCanceled: function(startingNewSearch)
{
if (this._searchResults) {
for (var i = 0; i < this._searchResults.length; ++i) {
var view = this._searchResults[i];
if (view.searchCanceled)
view.searchCanceled();
delete view.currentQuery;
}
}
WebInspector.updateSearchMatchesCount(0, this);
if (this._currentSearchChunkIntervalIdentifier) {
clearInterval(this._currentSearchChunkIntervalIdentifier);
delete this._currentSearchChunkIntervalIdentifier;
}
this._totalSearchMatches = 0;
this._currentSearchResultIndex = 0;
this._searchResults = [];
},
performSearch: function(query)
{
// Call searchCanceled since it will reset everything we need before doing a new search.
this.searchCanceled(true);
var searchableViews = this.searchableViews;
if (!searchableViews || !searchableViews.length)
return;
var parentElement = this.viewsContainerElement;
var visibleView = this.visibleView;
var sortFuction = this.searchResultsSortFunction;
var matchesCountUpdateTimeout = null;
function updateMatchesCount()
{
WebInspector.updateSearchMatchesCount(this._totalSearchMatches, this);
matchesCountUpdateTimeout = null;
}
function updateMatchesCountSoon()
{
if (matchesCountUpdateTimeout)
return;
// Update the matches count every half-second so it doesn't feel twitchy.
matchesCountUpdateTimeout = setTimeout(updateMatchesCount.bind(this), 500);
}
function finishedCallback(view, searchMatches)
{
if (!searchMatches)
return;
this._totalSearchMatches += searchMatches;
this._searchResults.push(view);
if (sortFuction)
this._searchResults.sort(sortFuction);
if (this.searchMatchFound)
this.searchMatchFound(view, searchMatches);
updateMatchesCountSoon.call(this);
if (view === visibleView)
view.jumpToFirstSearchResult();
}
var i = 0;
var panel = this;
var boundFinishedCallback = finishedCallback.bind(this);
var chunkIntervalIdentifier = null;
// Split up the work into chunks so we don't block the
// UI thread while processing.
function processChunk()
{
var view = searchableViews[i];
if (++i >= searchableViews.length) {
if (panel._currentSearchChunkIntervalIdentifier === chunkIntervalIdentifier)
delete panel._currentSearchChunkIntervalIdentifier;
clearInterval(chunkIntervalIdentifier);
}
if (!view)
return;
if (view.element.parentNode !== parentElement && view.element.parentNode && parentElement)
view.detach();
view.currentQuery = query;
view.performSearch(query, boundFinishedCallback);
}
processChunk();
chunkIntervalIdentifier = setInterval(processChunk, 25);
this._currentSearchChunkIntervalIdentifier = chunkIntervalIdentifier;
},
jumpToNextSearchResult: function()
{
if (!this.showView || !this._searchResults || !this._searchResults.length)
return;
var showFirstResult = false;
this._currentSearchResultIndex = this._searchResults.indexOf(this.visibleView);
if (this._currentSearchResultIndex === -1) {
this._currentSearchResultIndex = 0;
showFirstResult = true;
}
var currentView = this._searchResults[this._currentSearchResultIndex];
if (currentView.showingLastSearchResult()) {
if (++this._currentSearchResultIndex >= this._searchResults.length)
this._currentSearchResultIndex = 0;
currentView = this._searchResults[this._currentSearchResultIndex];
showFirstResult = true;
}
if (currentView !== this.visibleView)
this.showView(currentView);
if (showFirstResult)
currentView.jumpToFirstSearchResult();
else
currentView.jumpToNextSearchResult();
},
jumpToPreviousSearchResult: function()
{
if (!this.showView || !this._searchResults || !this._searchResults.length)
return;
var showLastResult = false;
this._currentSearchResultIndex = this._searchResults.indexOf(this.visibleView);
if (this._currentSearchResultIndex === -1) {
this._currentSearchResultIndex = 0;
showLastResult = true;
}
var currentView = this._searchResults[this._currentSearchResultIndex];
if (currentView.showingFirstSearchResult()) {
if (--this._currentSearchResultIndex < 0)
this._currentSearchResultIndex = (this._searchResults.length - 1);
currentView = this._searchResults[this._currentSearchResultIndex];
showLastResult = true;
}
if (currentView !== this.visibleView)
this.showView(currentView);
if (showLastResult)
currentView.jumpToLastSearchResult();
else
currentView.jumpToPreviousSearchResult();
}
}
WebInspector.Panel.prototype.__proto__ = WebInspector.View.prototype;
/* ElementsPanel.js */
/*
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
* Copyright (C) 2008 Matt Lilek <webkit@mattlilek.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.
*/
WebInspector.ElementsPanel = function()
{
WebInspector.Panel.call(this);
this.element.addStyleClass("elements");
this.contentElement = document.createElement("div");
this.contentElement.id = "elements-content";
this.contentElement.className = "outline-disclosure";
this.treeOutline = new WebInspector.ElementsTreeOutline();
this.treeOutline.panel = this;
this.treeOutline.includeRootDOMNode = false;
this.treeOutline.selectEnabled = true;
this.treeOutline.focusedNodeChanged = function(forceUpdate)
{
if (this.panel.visible && WebInspector.currentFocusElement !== document.getElementById("search"))
WebInspector.currentFocusElement = document.getElementById("main-panels");
this.panel.updateBreadcrumb(forceUpdate);
for (var pane in this.panel.sidebarPanes)
this.panel.sidebarPanes[pane].needsUpdate = true;
this.panel.updateStyles(true);
this.panel.updateMetrics();
this.panel.updateProperties();
if (InspectorController.searchingForNode()) {
InspectorController.toggleNodeSearch();
this.panel.nodeSearchButton.removeStyleClass("toggled-on");
}
};
this.contentElement.appendChild(this.treeOutline.element);
this.crumbsElement = document.createElement("div");
this.crumbsElement.className = "crumbs";
this.crumbsElement.addEventListener("mousemove", this._mouseMovedInCrumbs.bind(this), false);
this.crumbsElement.addEventListener("mouseout", this._mouseMovedOutOfCrumbs.bind(this), false);
this.sidebarPanes = {};
this.sidebarPanes.styles = new WebInspector.StylesSidebarPane();
this.sidebarPanes.metrics = new WebInspector.MetricsSidebarPane();
this.sidebarPanes.properties = new WebInspector.PropertiesSidebarPane();
this.sidebarPanes.styles.onexpand = this.updateStyles.bind(this);
this.sidebarPanes.metrics.onexpand = this.updateMetrics.bind(this);
this.sidebarPanes.properties.onexpand = this.updateProperties.bind(this);
this.sidebarPanes.styles.expanded = true;
this.sidebarPanes.styles.addEventListener("style edited", this._stylesPaneEdited, this);
this.sidebarPanes.styles.addEventListener("style property toggled", this._stylesPaneEdited, this);
this.sidebarPanes.metrics.addEventListener("metrics edited", this._metricsPaneEdited, this);
this.sidebarElement = document.createElement("div");
this.sidebarElement.id = "elements-sidebar";
this.sidebarElement.appendChild(this.sidebarPanes.styles.element);
this.sidebarElement.appendChild(this.sidebarPanes.metrics.element);
this.sidebarElement.appendChild(this.sidebarPanes.properties.element);
this.sidebarResizeElement = document.createElement("div");
this.sidebarResizeElement.className = "sidebar-resizer-vertical";
this.sidebarResizeElement.addEventListener("mousedown", this.rightSidebarResizerDragStart.bind(this), false);
this.nodeSearchButton = document.createElement("button");
this.nodeSearchButton.title = WebInspector.UIString("Select an element in the page to inspect it.");
this.nodeSearchButton.id = "node-search-status-bar-item";
this.nodeSearchButton.className = "status-bar-item";
this.nodeSearchButton.addEventListener("click", this._nodeSearchButtonClicked.bind(this), false);
this.searchingForNode = false;
this.element.appendChild(this.contentElement);
this.element.appendChild(this.sidebarElement);
this.element.appendChild(this.sidebarResizeElement);
this._mutationMonitoredWindows = [];
this._nodeInsertedEventListener = InspectorController.wrapCallback(this._nodeInserted.bind(this));
this._nodeRemovedEventListener = InspectorController.wrapCallback(this._nodeRemoved.bind(this));
this._contentLoadedEventListener = InspectorController.wrapCallback(this._contentLoaded.bind(this));
this.reset();
}
WebInspector.ElementsPanel.prototype = {
toolbarItemClass: "elements",
get toolbarItemLabel()
{
return WebInspector.UIString("Elements");
},
get statusBarItems()
{
return [this.nodeSearchButton, this.crumbsElement];
},
updateStatusBarItems: function()
{
this.updateBreadcrumbSizes();
},
show: function()
{
WebInspector.Panel.prototype.show.call(this);
this.sidebarResizeElement.style.right = (this.sidebarElement.offsetWidth - 3) + "px";
this.updateBreadcrumb();
this.treeOutline.updateSelection();
if (this.recentlyModifiedNodes.length)
this._updateModifiedNodes();
},
hide: function()
{
WebInspector.Panel.prototype.hide.call(this);
WebInspector.hoveredDOMNode = null;
if (InspectorController.searchingForNode()) {
InspectorController.toggleNodeSearch();
this.nodeSearchButton.removeStyleClass("toggled-on");
}
},
resize: function()
{
this.treeOutline.updateSelection();
this.updateBreadcrumbSizes();
},
reset: function()
{
this.rootDOMNode = null;
this.focusedDOMNode = null;
WebInspector.hoveredDOMNode = null;
if (InspectorController.searchingForNode()) {
InspectorController.toggleNodeSearch();
this.nodeSearchButton.removeStyleClass("toggled-on");
}
this.recentlyModifiedNodes = [];
this.unregisterAllMutationEventListeners();
delete this.currentQuery;
this.searchCanceled();
var inspectedWindow = InspectorController.inspectedWindow();
if (!inspectedWindow || !inspectedWindow.document)
return;
if (!inspectedWindow.document.firstChild) {
function contentLoaded()
{
inspectedWindow.document.removeEventListener("DOMContentLoaded", contentLoadedCallback, false);
this.reset();
}
var contentLoadedCallback = InspectorController.wrapCallback(contentLoaded.bind(this));
inspectedWindow.document.addEventListener("DOMContentLoaded", contentLoadedCallback, false);
return;
}
// If the window isn't visible, return early so the DOM tree isn't built
// and mutation event listeners are not added.
if (!InspectorController.isWindowVisible())
return;
this.registerMutationEventListeners(inspectedWindow);
var inspectedRootDocument = inspectedWindow.document;
this.rootDOMNode = inspectedRootDocument;
var canidateFocusNode = inspectedRootDocument.body || inspectedRootDocument.documentElement;
if (canidateFocusNode) {
this.treeOutline.suppressSelectHighlight = true;
this.focusedDOMNode = canidateFocusNode;
this.treeOutline.suppressSelectHighlight = false;
if (this.treeOutline.selectedTreeElement)
this.treeOutline.selectedTreeElement.expand();
}
},
includedInSearchResultsPropertyName: "__includedInInspectorSearchResults",
searchCanceled: function()
{
if (this._searchResults) {
const searchResultsProperty = this.includedInSearchResultsPropertyName;
for (var i = 0; i < this._searchResults.length; ++i) {
var node = this._searchResults[i];
// Remove the searchResultsProperty since there might be an unfinished search.
delete node[searchResultsProperty];
var treeElement = this.treeOutline.findTreeElement(node);
if (treeElement)
treeElement.highlighted = false;
}
}
WebInspector.updateSearchMatchesCount(0, this);
if (this._currentSearchChunkIntervalIdentifier) {
clearInterval(this._currentSearchChunkIntervalIdentifier);
delete this._currentSearchChunkIntervalIdentifier;
}
this._currentSearchResultIndex = 0;
this._searchResults = [];
},
performSearch: function(query)
{
// Call searchCanceled since it will reset everything we need before doing a new search.
this.searchCanceled();
const whitespaceTrimmedQuery = query.trimWhitespace();
if (!whitespaceTrimmedQuery.length)
return;
var tagNameQuery = whitespaceTrimmedQuery;
var attributeNameQuery = whitespaceTrimmedQuery;
var startTagFound = (tagNameQuery.indexOf("<") === 0);
var endTagFound = (tagNameQuery.lastIndexOf(">") === (tagNameQuery.length - 1));
if (startTagFound || endTagFound) {
var tagNameQueryLength = tagNameQuery.length;
tagNameQuery = tagNameQuery.substring((startTagFound ? 1 : 0), (endTagFound ? (tagNameQueryLength - 1) : tagNameQueryLength));
}
// Check the tagNameQuery is it is a possibly valid tag name.
if (!/^[a-zA-Z0-9\-_:]+$/.test(tagNameQuery))
tagNameQuery = null;
// Check the attributeNameQuery is it is a possibly valid tag name.
if (!/^[a-zA-Z0-9\-_:]+$/.test(attributeNameQuery))
attributeNameQuery = null;
const escapedQuery = query.escapeCharacters("'");
const escapedTagNameQuery = (tagNameQuery ? tagNameQuery.escapeCharacters("'") : null);
const escapedWhitespaceTrimmedQuery = whitespaceTrimmedQuery.escapeCharacters("'");
const searchResultsProperty = this.includedInSearchResultsPropertyName;
var updatedMatchCountOnce = false;
var matchesCountUpdateTimeout = null;
function updateMatchesCount()
{
WebInspector.updateSearchMatchesCount(this._searchResults.length, this);
matchesCountUpdateTimeout = null;
updatedMatchCountOnce = true;
}
function updateMatchesCountSoon()
{
if (!updatedMatchCountOnce)
return updateMatchesCount.call(this);
if (matchesCountUpdateTimeout)
return;
// Update the matches count every half-second so it doesn't feel twitchy.
matchesCountUpdateTimeout = setTimeout(updateMatchesCount.bind(this), 500);
}
function addNodesToResults(nodes, length, getItem)
{
if (!length)
return;
for (var i = 0; i < length; ++i) {
var node = getItem.call(nodes, i);
// Skip this node if it already has the property.
if (searchResultsProperty in node)
continue;
if (!this._searchResults.length) {
this._currentSearchResultIndex = 0;
this.focusedDOMNode = node;
}
node[searchResultsProperty] = true;
this._searchResults.push(node);
// Highlight the tree element to show it matched the search.
// FIXME: highlight the substrings in text nodes and attributes.
var treeElement = this.treeOutline.findTreeElement(node);
if (treeElement)
treeElement.highlighted = true;
}
updateMatchesCountSoon.call(this);
}
function matchExactItems(doc)
{
matchExactId.call(this, doc);
matchExactClassNames.call(this, doc);
matchExactTagNames.call(this, doc);
matchExactAttributeNames.call(this, doc);
}
function matchExactId(doc)
{
const result = doc.__proto__.getElementById.call(doc, whitespaceTrimmedQuery);
addNodesToResults.call(this, result, (result ? 1 : 0), function() { return this });
}
function matchExactClassNames(doc)
{
const result = doc.__proto__.getElementsByClassName.call(doc, whitespaceTrimmedQuery);
addNodesToResults.call(this, result, result.length, result.item);
}
function matchExactTagNames(doc)
{
if (!tagNameQuery)
return;
const result = doc.__proto__.getElementsByTagName.call(doc, tagNameQuery);
addNodesToResults.call(this, result, result.length, result.item);
}
function matchExactAttributeNames(doc)
{
if (!attributeNameQuery)
return;
const result = doc.__proto__.querySelectorAll.call(doc, "[" + attributeNameQuery + "]");
addNodesToResults.call(this, result, result.length, result.item);
}
function matchPartialTagNames(doc)
{
if (!tagNameQuery)
return;
const result = doc.__proto__.evaluate.call(doc, "//*[contains(name(), '" + escapedTagNameQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
}
function matchStartOfTagNames(doc)
{
if (!tagNameQuery)
return;
const result = doc.__proto__.evaluate.call(doc, "//*[starts-with(name(), '" + escapedTagNameQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
}
function matchPartialTagNamesAndAttributeValues(doc)
{
if (!tagNameQuery) {
matchPartialAttributeValues.call(this, doc);
return;
}
const result = doc.__proto__.evaluate.call(doc, "//*[contains(name(), '" + escapedTagNameQuery + "') or contains(@*, '" + escapedQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
}
function matchPartialAttributeValues(doc)
{
const result = doc.__proto__.evaluate.call(doc, "//*[contains(@*, '" + escapedQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
}
function matchStyleSelector(doc)
{
const result = doc.__proto__.querySelectorAll.call(doc, whitespaceTrimmedQuery);
addNodesToResults.call(this, result, result.length, result.item);
}
function matchPlainText(doc)
{
const result = doc.__proto__.evaluate.call(doc, "//text()[contains(., '" + escapedQuery + "')] | //comment()[contains(., '" + escapedQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
}
function matchXPathQuery(doc)
{
const result = doc.__proto__.evaluate.call(doc, whitespaceTrimmedQuery, doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
}
function finishedSearching()
{
// Remove the searchResultsProperty now that the search is finished.
for (var i = 0; i < this._searchResults.length; ++i)
delete this._searchResults[i][searchResultsProperty];
}
const mainFrameDocument = InspectorController.inspectedWindow().document;
const searchDocuments = [mainFrameDocument];
if (tagNameQuery && startTagFound && endTagFound)
const searchFunctions = [matchExactTagNames, matchPlainText];
else if (tagNameQuery && startTagFound)
const searchFunctions = [matchStartOfTagNames, matchPlainText];
else if (tagNameQuery && endTagFound) {
// FIXME: we should have a matchEndOfTagNames search function if endTagFound is true but not startTagFound.
// This requires ends-with() support in XPath, WebKit only supports starts-with() and contains().
const searchFunctions = [matchPartialTagNames, matchPlainText];
} else if (whitespaceTrimmedQuery === "//*" || whitespaceTrimmedQuery === "*") {
// These queries will match every node. Matching everything isn't useful and can be slow for large pages,
// so limit the search functions list to plain text and attribute matching.
const searchFunctions = [matchPartialAttributeValues, matchPlainText];
} else
const searchFunctions = [matchExactItems, matchStyleSelector, matchPartialTagNamesAndAttributeValues, matchPlainText, matchXPathQuery];
// Find all frames, iframes and object elements to search their documents.
const querySelectorAllFunction = InspectorController.inspectedWindow().Document.prototype.querySelectorAll;
const subdocumentResult = querySelectorAllFunction.call(mainFrameDocument, "iframe, frame, object");
for (var i = 0; i < subdocumentResult.length; ++i) {
var element = subdocumentResult.item(i);
if (element.contentDocument)
searchDocuments.push(element.contentDocument);
}
const panel = this;
var documentIndex = 0;
var searchFunctionIndex = 0;
var chunkIntervalIdentifier = null;
// Split up the work into chunks so we don't block the UI thread while processing.
function processChunk()
{
var searchDocument = searchDocuments[documentIndex];
var searchFunction = searchFunctions[searchFunctionIndex];
if (++searchFunctionIndex > searchFunctions.length) {
searchFunction = searchFunctions[0];
searchFunctionIndex = 0;
if (++documentIndex > searchDocuments.length) {
if (panel._currentSearchChunkIntervalIdentifier === chunkIntervalIdentifier)
delete panel._currentSearchChunkIntervalIdentifier;
clearInterval(chunkIntervalIdentifier);
finishedSearching.call(panel);
return;
}
searchDocument = searchDocuments[documentIndex];
}
if (!searchDocument || !searchFunction)
return;
try {
searchFunction.call(panel, searchDocument);
} catch(err) {
// ignore any exceptions. the query might be malformed, but we allow that.
}
}
processChunk();
chunkIntervalIdentifier = setInterval(processChunk, 25);
this._currentSearchChunkIntervalIdentifier = chunkIntervalIdentifier;
},
jumpToNextSearchResult: function()
{
if (!this._searchResults || !this._searchResults.length)
return;
if (++this._currentSearchResultIndex >= this._searchResults.length)
this._currentSearchResultIndex = 0;
this.focusedDOMNode = this._searchResults[this._currentSearchResultIndex];
},
jumpToPreviousSearchResult: function()
{
if (!this._searchResults || !this._searchResults.length)
return;
if (--this._currentSearchResultIndex < 0)
this._currentSearchResultIndex = (this._searchResults.length - 1);
this.focusedDOMNode = this._searchResults[this._currentSearchResultIndex];
},
inspectedWindowCleared: function(window)
{
if (InspectorController.isWindowVisible())
this.updateMutationEventListeners(window);
},
_addMutationEventListeners: function(monitoredWindow)
{
monitoredWindow.document.addEventListener("DOMNodeInserted", this._nodeInsertedEventListener, true);
monitoredWindow.document.addEventListener("DOMNodeRemoved", this._nodeRemovedEventListener, true);
if (monitoredWindow.frameElement)
monitoredWindow.addEventListener("DOMContentLoaded", this._contentLoadedEventListener, true);
},
_removeMutationEventListeners: function(monitoredWindow)
{
if (monitoredWindow.frameElement)
monitoredWindow.removeEventListener("DOMContentLoaded", this._contentLoadedEventListener, true);
if (!monitoredWindow.document)
return;
monitoredWindow.document.removeEventListener("DOMNodeInserted", this._nodeInsertedEventListener, true);
monitoredWindow.document.removeEventListener("DOMNodeRemoved", this._nodeRemovedEventListener, true);
},
updateMutationEventListeners: function(monitoredWindow)
{
this._addMutationEventListeners(monitoredWindow);
},
registerMutationEventListeners: function(monitoredWindow)
{
if (!monitoredWindow || this._mutationMonitoredWindows.indexOf(monitoredWindow) !== -1)
return;
this._mutationMonitoredWindows.push(monitoredWindow);
if (InspectorController.isWindowVisible())
this._addMutationEventListeners(monitoredWindow);
},
unregisterMutationEventListeners: function(monitoredWindow)
{
if (!monitoredWindow || this._mutationMonitoredWindows.indexOf(monitoredWindow) === -1)
return;
this._mutationMonitoredWindows.remove(monitoredWindow);
this._removeMutationEventListeners(monitoredWindow);
},
unregisterAllMutationEventListeners: function()
{
for (var i = 0; i < this._mutationMonitoredWindows.length; ++i)
this._removeMutationEventListeners(this._mutationMonitoredWindows[i]);
this._mutationMonitoredWindows = [];
},
get rootDOMNode()
{
return this.treeOutline.rootDOMNode;
},
set rootDOMNode(x)
{
this.treeOutline.rootDOMNode = x;
},
get focusedDOMNode()
{
return this.treeOutline.focusedDOMNode;
},
set focusedDOMNode(x)
{
this.treeOutline.focusedDOMNode = x;
},
_contentLoaded: function(event)
{
this.recentlyModifiedNodes.push({node: event.target, parent: event.target.defaultView.frameElement, replaced: true});
if (this.visible)
this._updateModifiedNodesSoon();
},
_nodeInserted: function(event)
{
this.recentlyModifiedNodes.push({node: event.target, parent: event.relatedNode, inserted: true});
if (this.visible)
this._updateModifiedNodesSoon();
},
_nodeRemoved: function(event)
{
this.recentlyModifiedNodes.push({node: event.target, parent: event.relatedNode, removed: true});
if (this.visible)
this._updateModifiedNodesSoon();
},
_updateModifiedNodesSoon: function()
{
if ("_updateModifiedNodesTimeout" in this)
return;
this._updateModifiedNodesTimeout = setTimeout(this._updateModifiedNodes.bind(this), 0);
},
_updateModifiedNodes: function()
{
if ("_updateModifiedNodesTimeout" in this) {
clearTimeout(this._updateModifiedNodesTimeout);
delete this._updateModifiedNodesTimeout;
}
var updatedParentTreeElements = [];
var updateBreadcrumbs = false;
for (var i = 0; i < this.recentlyModifiedNodes.length; ++i) {
var replaced = this.recentlyModifiedNodes[i].replaced;
var parent = this.recentlyModifiedNodes[i].parent;
if (!parent)
continue;
var parentNodeItem = this.treeOutline.findTreeElement(parent, null, null, objectsAreSame);
if (parentNodeItem && !parentNodeItem.alreadyUpdatedChildren) {
parentNodeItem.updateChildren(replaced);
parentNodeItem.alreadyUpdatedChildren = true;
updatedParentTreeElements.push(parentNodeItem);
}
if (!updateBreadcrumbs && (objectsAreSame(this.focusedDOMNode, parent) || isAncestorIncludingParentFrames(this.focusedDOMNode, parent)))
updateBreadcrumbs = true;
}
for (var i = 0; i < updatedParentTreeElements.length; ++i)
delete updatedParentTreeElements[i].alreadyUpdatedChildren;
this.recentlyModifiedNodes = [];
if (updateBreadcrumbs)
this.updateBreadcrumb(true);
},
_stylesPaneEdited: function()
{
this.sidebarPanes.metrics.needsUpdate = true;
this.updateMetrics();
},
_metricsPaneEdited: function()
{
this.sidebarPanes.styles.needsUpdate = true;
this.updateStyles(true);
},
_mouseMovedInCrumbs: function(event)
{
var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
var crumbElement = nodeUnderMouse.enclosingNodeOrSelfWithClass("crumb");
WebInspector.hoveredDOMNode = (crumbElement ? crumbElement.representedObject : null);
if ("_mouseOutOfCrumbsTimeout" in this) {
clearTimeout(this._mouseOutOfCrumbsTimeout);
delete this._mouseOutOfCrumbsTimeout;
}
},
_mouseMovedOutOfCrumbs: function(event)
{
var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
if (nodeUnderMouse.isDescendant(this.crumbsElement))
return;
WebInspector.hoveredDOMNode = null;
this._mouseOutOfCrumbsTimeout = setTimeout(this.updateBreadcrumbSizes.bind(this), 1000);
},
updateBreadcrumb: function(forceUpdate)
{
if (!this.visible)
return;
var crumbs = this.crumbsElement;
var handled = false;
var foundRoot = false;
var crumb = crumbs.firstChild;
while (crumb) {
if (objectsAreSame(crumb.representedObject, this.rootDOMNode))
foundRoot = true;
if (foundRoot)
crumb.addStyleClass("dimmed");
else
crumb.removeStyleClass("dimmed");
if (objectsAreSame(crumb.representedObject, this.focusedDOMNode)) {
crumb.addStyleClass("selected");
handled = true;
} else {
crumb.removeStyleClass("selected");
}
crumb = crumb.nextSibling;
}
if (handled && !forceUpdate) {
// We don't need to rebuild the crumbs, but we need to adjust sizes
// to reflect the new focused or root node.
this.updateBreadcrumbSizes();
return;
}
crumbs.removeChildren();
var panel = this;
function selectCrumbFunction(event)
{
var crumb = event.currentTarget;
if (crumb.hasStyleClass("collapsed")) {
// Clicking a collapsed crumb will expose the hidden crumbs.
if (crumb === panel.crumbsElement.firstChild) {
// If the focused crumb is the first child, pick the farthest crumb
// that is still hidden. This allows the user to expose every crumb.
var currentCrumb = crumb;
while (currentCrumb) {
var hidden = currentCrumb.hasStyleClass("hidden");
var collapsed = currentCrumb.hasStyleClass("collapsed");
if (!hidden && !collapsed)
break;
crumb = currentCrumb;
currentCrumb = currentCrumb.nextSibling;
}
}
panel.updateBreadcrumbSizes(crumb);
} else {
// Clicking a dimmed crumb or double clicking (event.detail >= 2)
// will change the root node in addition to the focused node.
if (event.detail >= 2 || crumb.hasStyleClass("dimmed"))
panel.rootDOMNode = crumb.representedObject.parentNode;
panel.focusedDOMNode = crumb.representedObject;
}
event.preventDefault();
}
foundRoot = false;
for (var current = this.focusedDOMNode; current; current = parentNodeOrFrameElement(current)) {
if (current.nodeType === Node.DOCUMENT_NODE)
continue;
if (objectsAreSame(current, this.rootDOMNode))
foundRoot = true;
var crumb = document.createElement("span");
crumb.className = "crumb";
crumb.representedObject = current;
crumb.addEventListener("mousedown", selectCrumbFunction, false);
var crumbTitle;
switch (current.nodeType) {
case Node.ELEMENT_NODE:
crumbTitle = current.nodeName.toLowerCase();
var nameElement = document.createElement("span");
nameElement.textContent = crumbTitle;
crumb.appendChild(nameElement);
var idAttribute = current.getAttribute("id");
if (idAttribute) {
var idElement = document.createElement("span");
crumb.appendChild(idElement);
var part = "#" + idAttribute;
crumbTitle += part;
idElement.appendChild(document.createTextNode(part));
// Mark the name as extra, since the ID is more important.
nameElement.className = "extra";
}
var classAttribute = current.getAttribute("class");
if (classAttribute) {
var classes = classAttribute.split(/\s+/);
var foundClasses = {};
if (classes.length) {
var classesElement = document.createElement("span");
classesElement.className = "extra";
crumb.appendChild(classesElement);
for (var i = 0; i < classes.length; ++i) {
var className = classes[i];
if (className && !(className in foundClasses)) {
var part = "." + className;
crumbTitle += part;
classesElement.appendChild(document.createTextNode(part));
foundClasses[className] = true;
}
}
}
}
break;
case Node.TEXT_NODE:
if (isNodeWhitespace.call(current))
crumbTitle = WebInspector.UIString("(whitespace)");
else
crumbTitle = WebInspector.UIString("(text)");
break
case Node.COMMENT_NODE:
crumbTitle = "<!-->";
break;
case Node.DOCUMENT_TYPE_NODE:
crumbTitle = "<!DOCTYPE>";
break;
default:
crumbTitle = current.nodeName.toLowerCase();
}
if (!crumb.childNodes.length) {
var nameElement = document.createElement("span");
nameElement.textContent = crumbTitle;
crumb.appendChild(nameElement);
}
crumb.title = crumbTitle;
if (foundRoot)
crumb.addStyleClass("dimmed");
if (objectsAreSame(current, this.focusedDOMNode))
crumb.addStyleClass("selected");
if (!crumbs.childNodes.length)
crumb.addStyleClass("end");
crumbs.appendChild(crumb);
}
if (crumbs.hasChildNodes())
crumbs.lastChild.addStyleClass("start");
this.updateBreadcrumbSizes();
},
updateBreadcrumbSizes: function(focusedCrumb)
{
if (!this.visible)
return;
if (document.body.offsetWidth <= 0) {
// The stylesheet hasn't loaded yet or the window is closed,
// so we can't calculate what is need. Return early.
return;
}
var crumbs = this.crumbsElement;
if (!crumbs.childNodes.length || crumbs.offsetWidth <= 0)
return; // No crumbs, do nothing.
// A Zero index is the right most child crumb in the breadcrumb.
var selectedIndex = 0;
var focusedIndex = 0;
var selectedCrumb;
var i = 0;
var crumb = crumbs.firstChild;
while (crumb) {
// Find the selected crumb and index.
if (!selectedCrumb && crumb.hasStyleClass("selected")) {
selectedCrumb = crumb;
selectedIndex = i;
}
// Find the focused crumb index.
if (crumb === focusedCrumb)
focusedIndex = i;
// Remove any styles that affect size before
// deciding to shorten any crumbs.
if (crumb !== crumbs.lastChild)
crumb.removeStyleClass("start");
if (crumb !== crumbs.firstChild)
crumb.removeStyleClass("end");
crumb.removeStyleClass("compact");
crumb.removeStyleClass("collapsed");
crumb.removeStyleClass("hidden");
crumb = crumb.nextSibling;
++i;
}
// Restore the start and end crumb classes in case they got removed in coalesceCollapsedCrumbs().
// The order of the crumbs in the document is opposite of the visual order.
crumbs.firstChild.addStyleClass("end");
crumbs.lastChild.addStyleClass("start");
function crumbsAreSmallerThanContainer()
{
var rightPadding = 20;
var errorWarningElement = document.getElementById("error-warning-count");
if (!WebInspector.console.visible && errorWarningElement)
rightPadding += errorWarningElement.offsetWidth;
return ((crumbs.totalOffsetLeft + crumbs.offsetWidth + rightPadding) < window.innerWidth);
}
if (crumbsAreSmallerThanContainer())
return; // No need to compact the crumbs, they all fit at full size.
var BothSides = 0;
var AncestorSide = -1;
var ChildSide = 1;
function makeCrumbsSmaller(shrinkingFunction, direction, significantCrumb)
{
if (!significantCrumb)
significantCrumb = (focusedCrumb || selectedCrumb);
if (significantCrumb === selectedCrumb)
var significantIndex = selectedIndex;
else if (significantCrumb === focusedCrumb)
var significantIndex = focusedIndex;
else {
var significantIndex = 0;
for (var i = 0; i < crumbs.childNodes.length; ++i) {
if (crumbs.childNodes[i] === significantCrumb) {
significantIndex = i;
break;
}
}
}
function shrinkCrumbAtIndex(index)
{
var shrinkCrumb = crumbs.childNodes[index];
if (shrinkCrumb && shrinkCrumb !== significantCrumb)
shrinkingFunction(shrinkCrumb);
if (crumbsAreSmallerThanContainer())
return true; // No need to compact the crumbs more.
return false;
}
// Shrink crumbs one at a time by applying the shrinkingFunction until the crumbs
// fit in the container or we run out of crumbs to shrink.
if (direction) {
// Crumbs are shrunk on only one side (based on direction) of the signifcant crumb.
var index = (direction > 0 ? 0 : crumbs.childNodes.length - 1);
while (index !== significantIndex) {
if (shrinkCrumbAtIndex(index))
return true;
index += (direction > 0 ? 1 : -1);
}
} else {
// Crumbs are shrunk in order of descending distance from the signifcant crumb,
// with a tie going to child crumbs.
var startIndex = 0;
var endIndex = crumbs.childNodes.length - 1;
while (startIndex != significantIndex || endIndex != significantIndex) {
var startDistance = significantIndex - startIndex;
var endDistance = endIndex - significantIndex;
if (startDistance >= endDistance)
var index = startIndex++;
else
var index = endIndex--;
if (shrinkCrumbAtIndex(index))
return true;
}
}
// We are not small enough yet, return false so the caller knows.
return false;
}
function coalesceCollapsedCrumbs()
{
var crumb = crumbs.firstChild;
var collapsedRun = false;
var newStartNeeded = false;
var newEndNeeded = false;
while (crumb) {
var hidden = crumb.hasStyleClass("hidden");
if (!hidden) {
var collapsed = crumb.hasStyleClass("collapsed");
if (collapsedRun && collapsed) {
crumb.addStyleClass("hidden");
crumb.removeStyleClass("compact");
crumb.removeStyleClass("collapsed");
if (crumb.hasStyleClass("start")) {
crumb.removeStyleClass("start");
newStartNeeded = true;
}
if (crumb.hasStyleClass("end")) {
crumb.removeStyleClass("end");
newEndNeeded = true;
}
continue;
}
collapsedRun = collapsed;
if (newEndNeeded) {
newEndNeeded = false;
crumb.addStyleClass("end");
}
} else
collapsedRun = true;
crumb = crumb.nextSibling;
}
if (newStartNeeded) {
crumb = crumbs.lastChild;
while (crumb) {
if (!crumb.hasStyleClass("hidden")) {
crumb.addStyleClass("start");
break;
}
crumb = crumb.previousSibling;
}
}
}
function compact(crumb)
{
if (crumb.hasStyleClass("hidden"))
return;
crumb.addStyleClass("compact");
}
function collapse(crumb, dontCoalesce)
{
if (crumb.hasStyleClass("hidden"))
return;
crumb.addStyleClass("collapsed");
crumb.removeStyleClass("compact");
if (!dontCoalesce)
coalesceCollapsedCrumbs();
}
function compactDimmed(crumb)
{
if (crumb.hasStyleClass("dimmed"))
compact(crumb);
}
function collapseDimmed(crumb)
{
if (crumb.hasStyleClass("dimmed"))
collapse(crumb);
}
if (!focusedCrumb) {
// When not focused on a crumb we can be biased and collapse less important
// crumbs that the user might not care much about.
// Compact child crumbs.
if (makeCrumbsSmaller(compact, ChildSide))
return;
// Collapse child crumbs.
if (makeCrumbsSmaller(collapse, ChildSide))
return;
// Compact dimmed ancestor crumbs.
if (makeCrumbsSmaller(compactDimmed, AncestorSide))
return;
// Collapse dimmed ancestor crumbs.
if (makeCrumbsSmaller(collapseDimmed, AncestorSide))
return;
}
// Compact ancestor crumbs, or from both sides if focused.
if (makeCrumbsSmaller(compact, (focusedCrumb ? BothSides : AncestorSide)))
return;
// Collapse ancestor crumbs, or from both sides if focused.
if (makeCrumbsSmaller(collapse, (focusedCrumb ? BothSides : AncestorSide)))
return;
if (!selectedCrumb)
return;
// Compact the selected crumb.
compact(selectedCrumb);
if (crumbsAreSmallerThanContainer())
return;
// Collapse the selected crumb as a last resort. Pass true to prevent coalescing.
collapse(selectedCrumb, true);
},
updateStyles: function(forceUpdate)
{
var stylesSidebarPane = this.sidebarPanes.styles;
if (!stylesSidebarPane.expanded || !stylesSidebarPane.needsUpdate)
return;
stylesSidebarPane.update(this.focusedDOMNode, null, forceUpdate);
stylesSidebarPane.needsUpdate = false;
},
updateMetrics: function()
{
var metricsSidebarPane = this.sidebarPanes.metrics;
if (!metricsSidebarPane.expanded || !metricsSidebarPane.needsUpdate)
return;
metricsSidebarPane.update(this.focusedDOMNode);
metricsSidebarPane.needsUpdate = false;
},
updateProperties: function()
{
var propertiesSidebarPane = this.sidebarPanes.properties;
if (!propertiesSidebarPane.expanded || !propertiesSidebarPane.needsUpdate)
return;
propertiesSidebarPane.update(this.focusedDOMNode);
propertiesSidebarPane.needsUpdate = false;
},
handleKeyEvent: function(event)
{
this.treeOutline.handleKeyEvent(event);
},
handleCopyEvent: function(event)
{
// Don't prevent the normal copy if the user has a selection.
if (!window.getSelection().isCollapsed)
return;
switch (this.focusedDOMNode.nodeType) {
case Node.ELEMENT_NODE:
var data = this.focusedDOMNode.outerHTML;
break;
case Node.COMMENT_NODE:
var data = "<!--" + this.focusedDOMNode.nodeValue + "-->";
break;
default:
case Node.TEXT_NODE:
var data = this.focusedDOMNode.nodeValue;
}
event.clipboardData.clearData();
event.preventDefault();
if (data)
event.clipboardData.setData("text/plain", data);
},
rightSidebarResizerDragStart: function(event)
{
WebInspector.elementDragStart(this.sidebarElement, this.rightSidebarResizerDrag.bind(this), this.rightSidebarResizerDragEnd.bind(this), event, "col-resize");
},
rightSidebarResizerDragEnd: function(event)
{
WebInspector.elementDragEnd(event);
},
rightSidebarResizerDrag: function(event)
{
var x = event.pageX;
var newWidth = Number.constrain(window.innerWidth - x, Preferences.minElementsSidebarWidth, window.innerWidth * 0.66);
this.sidebarElement.style.width = newWidth + "px";
this.contentElement.style.right = newWidth + "px";
this.sidebarResizeElement.style.right = (newWidth - 3) + "px";
this.treeOutline.updateSelection();
event.preventDefault();
},
_nodeSearchButtonClicked: function(event)
{
InspectorController.toggleNodeSearch();
if (InspectorController.searchingForNode())
this.nodeSearchButton.addStyleClass("toggled-on");
else
this.nodeSearchButton.removeStyleClass("toggled-on");
}
}
WebInspector.ElementsPanel.prototype.__proto__ = WebInspector.Panel.prototype;
/* ResourcesPanel.js */
/*
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
* Copyright (C) 2008 Anthony Ricaud (rik24d@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.
*/
WebInspector.ResourcesPanel = function()
{
WebInspector.Panel.call(this);
this.element.addStyleClass("resources");
this.viewsContainerElement = document.createElement("div");
this.viewsContainerElement.id = "resource-views";
this.element.appendChild(this.viewsContainerElement);
this.containerElement = document.createElement("div");
this.containerElement.id = "resources-container";
this.containerElement.addEventListener("scroll", this._updateDividersLabelBarPosition.bind(this), false);
this.element.appendChild(this.containerElement);
this.sidebarElement = document.createElement("div");
this.sidebarElement.id = "resources-sidebar";
this.sidebarElement.className = "sidebar";
this.containerElement.appendChild(this.sidebarElement);
this.sidebarResizeElement = document.createElement("div");
this.sidebarResizeElement.className = "sidebar-resizer-vertical";
this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarDragging.bind(this), false);
this.element.appendChild(this.sidebarResizeElement);
this.containerContentElement = document.createElement("div");
this.containerContentElement.id = "resources-container-content";
this.containerElement.appendChild(this.containerContentElement);
this.summaryElement = document.createElement("div");
this.summaryElement.id = "resources-summary";
this.containerContentElement.appendChild(this.summaryElement);
this.resourcesGraphsElement = document.createElement("div");
this.resourcesGraphsElement.id = "resources-graphs";
this.containerContentElement.appendChild(this.resourcesGraphsElement);
this.dividersElement = document.createElement("div");
this.dividersElement.id = "resources-dividers";
this.containerContentElement.appendChild(this.dividersElement);
this.dividersLabelBarElement = document.createElement("div");
this.dividersLabelBarElement.id = "resources-dividers-label-bar";
this.containerContentElement.appendChild(this.dividersLabelBarElement);
this.summaryGraphElement = document.createElement("canvas");
this.summaryGraphElement.setAttribute("width", "450");
this.summaryGraphElement.setAttribute("height", "38");
this.summaryGraphElement.id = "resources-summary-graph";
this.summaryElement.appendChild(this.summaryGraphElement);
this.legendElement = document.createElement("div");
this.legendElement.id = "resources-graph-legend";
this.summaryElement.appendChild(this.legendElement);
this.sidebarTreeElement = document.createElement("ol");
this.sidebarTreeElement.className = "sidebar-tree";
this.sidebarElement.appendChild(this.sidebarTreeElement);
this.sidebarTree = new TreeOutline(this.sidebarTreeElement);
var timeGraphItem = new WebInspector.SidebarTreeElement("resources-time-graph-sidebar-item", WebInspector.UIString("Time"));
timeGraphItem.onselect = this._graphSelected.bind(this);
var transferTimeCalculator = new WebInspector.ResourceTransferTimeCalculator();
var transferDurationCalculator = new WebInspector.ResourceTransferDurationCalculator();
timeGraphItem.sortingOptions = [
{ name: WebInspector.UIString("Sort by Start Time"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByAscendingStartTime, calculator: transferTimeCalculator },
{ name: WebInspector.UIString("Sort by Response Time"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByAscendingResponseReceivedTime, calculator: transferTimeCalculator },
{ name: WebInspector.UIString("Sort by End Time"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByAscendingEndTime, calculator: transferTimeCalculator },
{ name: WebInspector.UIString("Sort by Duration"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingDuration, calculator: transferDurationCalculator },
{ name: WebInspector.UIString("Sort by Latency"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingLatency, calculator: transferDurationCalculator },
];
timeGraphItem.selectedSortingOptionIndex = 1;
var sizeGraphItem = new WebInspector.SidebarTreeElement("resources-size-graph-sidebar-item", WebInspector.UIString("Size"));
sizeGraphItem.onselect = this._graphSelected.bind(this);
var transferSizeCalculator = new WebInspector.ResourceTransferSizeCalculator();
sizeGraphItem.sortingOptions = [
{ name: WebInspector.UIString("Sort by Size"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingSize, calculator: transferSizeCalculator },
];
sizeGraphItem.selectedSortingOptionIndex = 0;
this.graphsTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("GRAPHS"), {}, true);
this.sidebarTree.appendChild(this.graphsTreeElement);
this.graphsTreeElement.appendChild(timeGraphItem);
this.graphsTreeElement.appendChild(sizeGraphItem);
this.graphsTreeElement.expand();
this.resourcesTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("RESOURCES"), {}, true);
this.sidebarTree.appendChild(this.resourcesTreeElement);
this.resourcesTreeElement.expand();
this.largerResourcesButton = document.createElement("button");
this.largerResourcesButton.id = "resources-larger-resources-status-bar-item";
this.largerResourcesButton.className = "status-bar-item toggled-on";
this.largerResourcesButton.title = WebInspector.UIString("Use small resource rows.");
this.largerResourcesButton.addEventListener("click", this._toggleLargerResources.bind(this), false);
this.sortingSelectElement = document.createElement("select");
this.sortingSelectElement.className = "status-bar-item";
this.sortingSelectElement.addEventListener("change", this._changeSortingFunction.bind(this), false);
this.reset();
timeGraphItem.select();
}
WebInspector.ResourcesPanel.prototype = {
toolbarItemClass: "resources",
get toolbarItemLabel()
{
return WebInspector.UIString("Resources");
},
get statusBarItems()
{
return [this.largerResourcesButton, this.sortingSelectElement];
},
show: function()
{
WebInspector.Panel.prototype.show.call(this);
this._updateDividersLabelBarPosition();
this._updateSidebarWidth();
this.refreshIfNeeded();
var visibleView = this.visibleView;
if (visibleView) {
visibleView.headersVisible = true;
visibleView.show(this.viewsContainerElement);
}
// Hide any views that are visible that are not this panel's current visible view.
// This can happen when a ResourceView is visible in the Scripts panel then switched
// to the this panel.
var resourcesLength = this._resources.length;
for (var i = 0; i < resourcesLength; ++i) {
var resource = this._resources[i];
var view = resource._resourcesView;
if (!view || view === visibleView)
continue;
view.visible = false;
}
},
resize: function()
{
this._updateGraphDividersIfNeeded();
var visibleView = this.visibleView;
if (visibleView && "resize" in visibleView)
visibleView.resize();
},
get searchableViews()
{
var views = [];
const visibleView = this.visibleView;
if (visibleView && visibleView.performSearch)
views.push(visibleView);
var resourcesLength = this._resources.length;
for (var i = 0; i < resourcesLength; ++i) {
var resource = this._resources[i];
var resourceView = this.resourceViewForResource(resource);
if (!resourceView.performSearch || resourceView === visibleView)
continue;
views.push(resourceView);
}
return views;
},
get searchResultsSortFunction()
{
const resourceTreeElementSortFunction = this.sortingFunction;
function sortFuction(a, b)
{
return resourceTreeElementSortFunction(a.resource._resourcesTreeElement, b.resource._resourcesTreeElement);
}
return sortFuction;
},
searchMatchFound: function(view, matches)
{
view.resource._resourcesTreeElement.searchMatches = matches;
},
searchCanceled: function(startingNewSearch)
{
WebInspector.Panel.prototype.searchCanceled.call(this, startingNewSearch);
if (startingNewSearch || !this._resources)
return;
for (var i = 0; i < this._resources.length; ++i) {
var resource = this._resources[i];
resource._resourcesTreeElement.updateErrorsAndWarnings();
}
},
performSearch: function(query)
{
for (var i = 0; i < this._resources.length; ++i) {
var resource = this._resources[i];
resource._resourcesTreeElement.resetBubble();
}
WebInspector.Panel.prototype.performSearch.call(this, query);
},
get visibleView()
{
if (this.visibleResource)
return this.visibleResource._resourcesView;
return null;
},
get calculator()
{
return this._calculator;
},
set calculator(x)
{
if (this._calculator === x)
return;
this._calculator = x;
if (this._calculator)
this._calculator.reset();
this._refreshAllResources(false, true, true);
this._updateGraphDividersIfNeeded(true);
this._updateSummaryGraph();
},
get sortingFunction()
{
return this._sortingFunction;
},
set sortingFunction(x)
{
this._sortingFunction = x;
this._sortResourcesIfNeeded();
},
get needsRefresh()
{
return this._needsRefresh;
},
set needsRefresh(x)
{
if (this._needsRefresh === x)
return;
this._needsRefresh = x;
if (x && this.visible)
this.refresh();
},
refreshIfNeeded: function()
{
if (this.needsRefresh)
this.refresh();
},
refresh: function()
{
this.needsRefresh = false;
var staleResourcesLength = this._staleResources.length;
for (var i = 0; i < staleResourcesLength; ++i)
this.refreshResource(this._staleResources[i], false, true, true);
this._staleResources = [];
this._updateGraphDividersIfNeeded();
this._sortResourcesIfNeeded();
this._updateSummaryGraph();
},
reset: function()
{
this.closeVisibleResource();
delete this.currentQuery;
this.searchCanceled();
if (this._calculator)
this._calculator.reset();
if (this._resources) {
var resourcesLength = this._resources.length;
for (var i = 0; i < resourcesLength; ++i) {
var resource = this._resources[i];
resource.warnings = 0;
resource.errors = 0;
delete resource._resourcesTreeElement;
delete resource._resourcesView;
}
}
this._resources = [];
this._staleResources = [];
this.resourcesTreeElement.removeChildren();
this.viewsContainerElement.removeChildren();
this.resourcesGraphsElement.removeChildren();
this._updateGraphDividersIfNeeded(true);
this._drawSummaryGraph(); // draws an empty graph
},
addResource: function(resource)
{
this._resources.push(resource);
var resourceTreeElement = new WebInspector.ResourceSidebarTreeElement(resource);
resource._resourcesTreeElement = resourceTreeElement;
var resourceGraph = new WebInspector.ResourceGraph(resource);
resource._resourcesTreeElement._resourceGraph = resourceGraph;
this.resourcesGraphsElement.appendChild(resourceGraph.graphElement);
this.resourcesTreeElement.appendChild(resourceTreeElement);
this.refreshResource(resource);
},
removeResource: function(resource)
{
if (this.visibleView === resource._resourcesView)
this.closeVisibleResource();
this._resources.remove(resource, true);
this.resourcesTreeElement.removeChild(resource._resourcesTreeElement);
this.resourcesGraphsElement.removeChild(resource._resourcesTreeElement._resourceGraph.graphElement);
resource.warnings = 0;
resource.errors = 0;
delete resource._resourcesTreeElement;
delete resource._resourcesView;
},
addMessageToResource: function(resource, msg)
{
if (!resource)
return;
switch (msg.level) {
case WebInspector.ConsoleMessage.MessageLevel.Warning:
resource.warnings += msg.repeatDelta;
break;
case WebInspector.ConsoleMessage.MessageLevel.Error:
resource.errors += msg.repeatDelta;
break;
}
if (!this.currentQuery)
resource._resourcesTreeElement.updateErrorsAndWarnings();
var view = this.resourceViewForResource(resource);
if (view.addMessage)
view.addMessage(msg);
},
clearMessages: function()
{
var resourcesLength = this._resources.length;
for (var i = 0; i < resourcesLength; ++i) {
var resource = this._resources[i];
resource.warnings = 0;
resource.errors = 0;
if (!this.currentQuery)
resource._resourcesTreeElement.updateErrorsAndWarnings();
var view = resource._resourcesView;
if (!view || !view.clearMessages)
continue;
view.clearMessages();
}
},
refreshResource: function(resource, skipBoundaryUpdate, skipSort, immediate)
{
if (!this.visible) {
this._staleResources.push(resource);
this.needsRefresh = true;
return;
}
if (!skipBoundaryUpdate) {
if (this._updateGraphBoundriesIfNeeded(resource, immediate))
return; // _updateGraphBoundriesIfNeeded refreshes all resources if it returns true, so just return.
}
if (!skipSort) {
if (immediate)
this._sortResourcesIfNeeded();
else
this._sortResourcesSoonIfNeeded();
}
this._updateSummaryGraphSoon();
if (!resource._resourcesTreeElement)
return;
resource._resourcesTreeElement.refresh();
var percentages = this.calculator.computeBarGraphPercentages(resource);
resource._resourcesTreeElement._resourceGraph.refresh(percentages);
},
recreateViewForResourceIfNeeded: function(resource)
{
if (!resource || !resource._resourcesView)
return;
var newView = this._createResourceView(resource);
if (newView.prototype === resource._resourcesView.prototype)
return;
resource.warnings = 0;
resource.errors = 0;
if (!this.currentQuery)
resource._resourcesTreeElement.updateErrorsAndWarnings();
var oldView = resource._resourcesView;
resource._resourcesView.detach();
delete resource._resourcesView;
resource._resourcesView = newView;
newView.headersVisible = oldView.headersVisible;
if (oldView.visible && oldView.element.parentNode)
newView.show(oldView.element.parentNode);
},
showResource: function(resource, line)
{
if (!resource)
return;
this.containerElement.addStyleClass("viewing-resource");
if (this.visibleResource && this.visibleResource._resourcesView)
this.visibleResource._resourcesView.hide();
var view = this.resourceViewForResource(resource);
view.headersVisible = true;
view.show(this.viewsContainerElement);
if (line) {
if (view.revealLine)
view.revealLine(line);
if (view.highlightLine)
view.highlightLine(line);
}
if (resource._resourcesTreeElement) {
resource._resourcesTreeElement.reveal();
resource._resourcesTreeElement.select(true);
}
this.visibleResource = resource;
this._updateSidebarWidth();
},
showView: function(view)
{
if (!view)
return;
this.showResource(view.resource);
},
closeVisibleResource: function()
{
this.containerElement.removeStyleClass("viewing-resource");
this._updateDividersLabelBarPosition();
if (this.visibleResource && this.visibleResource._resourcesView)
this.visibleResource._resourcesView.hide();
delete this.visibleResource;
if (this._lastSelectedGraphTreeElement)
this._lastSelectedGraphTreeElement.select(true);
this._updateSidebarWidth();
},
resourceViewForResource: function(resource)
{
if (!resource)
return null;
if (!resource._resourcesView)
resource._resourcesView = this._createResourceView(resource);
return resource._resourcesView;
},
sourceFrameForResource: function(resource)
{
var view = this.resourceViewForResource(resource);
if (!view)
return null;
if (!view.setupSourceFrameIfNeeded)
return null;
// Setting up the source frame requires that we be attached.
if (!this.element.parentNode)
this.attach();
view.setupSourceFrameIfNeeded();
return view.sourceFrame;
},
handleKeyEvent: function(event)
{
this.sidebarTree.handleKeyEvent(event);
},
_makeLegendElement: function(label, value, color)
{
var legendElement = document.createElement("label");
legendElement.className = "resources-graph-legend-item";
if (color) {
var swatch = document.createElement("canvas");
swatch.className = "resources-graph-legend-swatch";
swatch.setAttribute("width", "13");
swatch.setAttribute("height", "24");
legendElement.appendChild(swatch);
this._drawSwatch(swatch, color);
}
var labelElement = document.createElement("div");
labelElement.className = "resources-graph-legend-label";
legendElement.appendChild(labelElement);
var headerElement = document.createElement("div");
var headerElement = document.createElement("div");
headerElement.className = "resources-graph-legend-header";
headerElement.textContent = label;
labelElement.appendChild(headerElement);
var valueElement = document.createElement("div");
valueElement.className = "resources-graph-legend-value";
valueElement.textContent = value;
labelElement.appendChild(valueElement);
return legendElement;
},
_sortResourcesSoonIfNeeded: function()
{
if ("_sortResourcesTimeout" in this)
return;
this._sortResourcesTimeout = setTimeout(this._sortResourcesIfNeeded.bind(this), 500);
},
_sortResourcesIfNeeded: function()
{
if ("_sortResourcesTimeout" in this) {
clearTimeout(this._sortResourcesTimeout);
delete this._sortResourcesTimeout;
}
var sortedElements = [].concat(this.resourcesTreeElement.children);
sortedElements.sort(this.sortingFunction);
var sortedElementsLength = sortedElements.length;
for (var i = 0; i < sortedElementsLength; ++i) {
var treeElement = sortedElements[i];
if (treeElement === this.resourcesTreeElement.children[i])
continue;
var wasSelected = treeElement.selected;
this.resourcesTreeElement.removeChild(treeElement);
this.resourcesTreeElement.insertChild(treeElement, i);
if (wasSelected)
treeElement.select(true);
var graphElement = treeElement._resourceGraph.graphElement;
this.resourcesGraphsElement.removeChild(graphElement);
this.resourcesGraphsElement.insertBefore(graphElement, this.resourcesGraphsElement.children[i]);
}
},
_updateGraphBoundriesIfNeeded: function(resource, immediate)
{
var didChange = this.calculator.updateBoundries(resource);
if (didChange) {
if (immediate) {
this._refreshAllResources(true, true, immediate);
this._updateGraphDividersIfNeeded();
} else {
this._refreshAllResourcesSoon(true, true, immediate);
this._updateGraphDividersSoonIfNeeded();
}
}
return didChange;
},
_updateGraphDividersSoonIfNeeded: function()
{
if ("_updateGraphDividersTimeout" in this)
return;
this._updateGraphDividersTimeout = setTimeout(this._updateGraphDividersIfNeeded.bind(this), 500);
},
_updateGraphDividersIfNeeded: function(force)
{
if ("_updateGraphDividersTimeout" in this) {
clearTimeout(this._updateGraphDividersTimeout);
delete this._updateGraphDividersTimeout;
}
if (!this.visible) {
this.needsRefresh = true;
return;
}
if (document.body.offsetWidth <= 0) {
// The stylesheet hasn't loaded yet or the window is closed,
// so we can't calculate what is need. Return early.
return;
}
var dividerCount = Math.round(this.dividersElement.offsetWidth / 64);
var slice = this.calculator.boundarySpan / dividerCount;
if (!force && this._currentDividerSlice === slice)
return;
this._currentDividerSlice = slice;
this.dividersElement.removeChildren();
this.dividersLabelBarElement.removeChildren();
for (var i = 1; i <= dividerCount; ++i) {
var divider = document.createElement("div");
divider.className = "resources-divider";
if (i === dividerCount)
divider.addStyleClass("last");
divider.style.left = ((i / dividerCount) * 100) + "%";
this.dividersElement.appendChild(divider.cloneNode());
var label = document.createElement("div");
label.className = "resources-divider-label";
if (!isNaN(slice))
label.textContent = this.calculator.formatValue(slice * i);
divider.appendChild(label);
this.dividersLabelBarElement.appendChild(divider);
}
},
_refreshAllResourcesSoon: function(skipBoundaryUpdate, skipSort, immediate)
{
if ("_refreshAllResourcesTimeout" in this)
return;
this._refreshAllResourcesTimeout = setTimeout(this._refreshAllResources.bind(this), 500, skipBoundaryUpdate, skipSort, immediate);
},
_refreshAllResources: function(skipBoundaryUpdate, skipSort, immediate)
{
if ("_refreshAllResourcesTimeout" in this) {
clearTimeout(this._refreshAllResourcesTimeout);
delete this._refreshAllResourcesTimeout;
}
var resourcesLength = this._resources.length;
for (var i = 0; i < resourcesLength; ++i)
this.refreshResource(this._resources[i], skipBoundaryUpdate, skipSort, immediate);
},
_fadeOutRect: function(ctx, x, y, w, h, a1, a2)
{
ctx.save();
var gradient = ctx.createLinearGradient(x, y, x, y + h);
gradient.addColorStop(0.0, "rgba(0, 0, 0, " + (1.0 - a1) + ")");
gradient.addColorStop(0.8, "rgba(0, 0, 0, " + (1.0 - a2) + ")");
gradient.addColorStop(1.0, "rgba(0, 0, 0, 1.0)");
ctx.globalCompositeOperation = "destination-out";
ctx.fillStyle = gradient;
ctx.fillRect(x, y, w, h);
ctx.restore();
},
_drawSwatch: function(canvas, color)
{
var ctx = canvas.getContext("2d");
function drawSwatchSquare() {
ctx.fillStyle = color;
ctx.fillRect(0, 0, 13, 13);
var gradient = ctx.createLinearGradient(0, 0, 13, 13);
gradient.addColorStop(0.0, "rgba(255, 255, 255, 0.2)");
gradient.addColorStop(1.0, "rgba(255, 255, 255, 0.0)");
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 13, 13);
gradient = ctx.createLinearGradient(13, 13, 0, 0);
gradient.addColorStop(0.0, "rgba(0, 0, 0, 0.2)");
gradient.addColorStop(1.0, "rgba(0, 0, 0, 0.0)");
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 13, 13);
ctx.strokeStyle = "rgba(0, 0, 0, 0.6)";
ctx.strokeRect(0.5, 0.5, 12, 12);
}
ctx.clearRect(0, 0, 13, 24);
drawSwatchSquare();
ctx.save();
ctx.translate(0, 25);
ctx.scale(1, -1);
drawSwatchSquare();
ctx.restore();
this._fadeOutRect(ctx, 0, 13, 13, 13, 0.5, 0.0);
},
_drawSummaryGraph: function(segments)
{
if (!this.summaryGraphElement)
return;
if (!segments || !segments.length) {
segments = [{color: "white", value: 1}];
this._showingEmptySummaryGraph = true;
} else
delete this._showingEmptySummaryGraph;
// Calculate the total of all segments.
var total = 0;
for (var i = 0; i < segments.length; ++i)
total += segments[i].value;
// Calculate the percentage of each segment, rounded to the nearest percent.
var percents = segments.map(function(s) { return Math.max(Math.round(100 * s.value / total), 1) });
// Calculate the total percentage.
var percentTotal = 0;
for (var i = 0; i < percents.length; ++i)
percentTotal += percents[i];
// Make sure our percentage total is not greater-than 100, it can be greater
// if we rounded up for a few segments.
while (percentTotal > 100) {
for (var i = 0; i < percents.length && percentTotal > 100; ++i) {
if (percents[i] > 1) {
--percents[i];
--percentTotal;
}
}
}
// Make sure our percentage total is not less-than 100, it can be less
// if we rounded down for a few segments.
while (percentTotal < 100) {
for (var i = 0; i < percents.length && percentTotal < 100; ++i) {
++percents[i];
++percentTotal;
}
}
var ctx = this.summaryGraphElement.getContext("2d");
var x = 0;
var y = 0;
var w = 450;
var h = 19;
var r = (h / 2);
function drawPillShadow()
{
// This draws a line with a shadow that is offset away from the line. The line is stroked
// twice with different X shadow offsets to give more feathered edges. Later we erase the
// line with destination-out 100% transparent black, leaving only the shadow. This only
// works if nothing has been drawn into the canvas yet.
ctx.beginPath();
ctx.moveTo(x + 4, y + h - 3 - 0.5);
ctx.lineTo(x + w - 4, y + h - 3 - 0.5);
ctx.closePath();
ctx.save();
ctx.shadowBlur = 2;
ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
ctx.shadowOffsetX = 3;
ctx.shadowOffsetY = 5;
ctx.strokeStyle = "white";
ctx.lineWidth = 1;
ctx.stroke();
ctx.shadowOffsetX = -3;
ctx.stroke();
ctx.restore();
ctx.save();
ctx.globalCompositeOperation = "destination-out";
ctx.strokeStyle = "rgba(0, 0, 0, 1)";
ctx.lineWidth = 1;
ctx.stroke();
ctx.restore();
}
function drawPill()
{
// Make a rounded rect path.
ctx.beginPath();
ctx.moveTo(x, y + r);
ctx.lineTo(x, y + h - r);
ctx.quadraticCurveTo(x, y + h, x + r, y + h);
ctx.lineTo(x + w - r, y + h);
ctx.quadraticCurveTo(x + w, y + h, x + w, y + h - r);
ctx.lineTo(x + w, y + r);
ctx.quadraticCurveTo(x + w, y, x + w - r, y);
ctx.lineTo(x + r, y);
ctx.quadraticCurveTo(x, y, x, y + r);
ctx.closePath();
// Clip to the rounded rect path.
ctx.save();
ctx.clip();
// Fill the segments with the associated color.
var previousSegmentsWidth = 0;
for (var i = 0; i < segments.length; ++i) {
var segmentWidth = Math.round(w * percents[i] / 100);
ctx.fillStyle = segments[i].color;
ctx.fillRect(x + previousSegmentsWidth, y, segmentWidth, h);
previousSegmentsWidth += segmentWidth;
}
// Draw the segment divider lines.
ctx.lineWidth = 1;
for (var i = 1; i < 20; ++i) {
ctx.beginPath();
ctx.moveTo(x + (i * Math.round(w / 20)) + 0.5, y);
ctx.lineTo(x + (i * Math.round(w / 20)) + 0.5, y + h);
ctx.closePath();
ctx.strokeStyle = "rgba(0, 0, 0, 0.2)";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x + (i * Math.round(w / 20)) + 1.5, y);
ctx.lineTo(x + (i * Math.round(w / 20)) + 1.5, y + h);
ctx.closePath();
ctx.strokeStyle = "rgba(255, 255, 255, 0.2)";
ctx.stroke();
}
// Draw the pill shading.
var lightGradient = ctx.createLinearGradient(x, y, x, y + (h / 1.5));
lightGradient.addColorStop(0.0, "rgba(220, 220, 220, 0.6)");
lightGradient.addColorStop(0.4, "rgba(220, 220, 220, 0.2)");
lightGradient.addColorStop(1.0, "rgba(255, 255, 255, 0.0)");
var darkGradient = ctx.createLinearGradient(x, y + (h / 3), x, y + h);
darkGradient.addColorStop(0.0, "rgba(0, 0, 0, 0.0)");
darkGradient.addColorStop(0.8, "rgba(0, 0, 0, 0.2)");
darkGradient.addColorStop(1.0, "rgba(0, 0, 0, 0.5)");
ctx.fillStyle = darkGradient;
ctx.fillRect(x, y, w, h);
ctx.fillStyle = lightGradient;
ctx.fillRect(x, y, w, h);
ctx.restore();
}
ctx.clearRect(x, y, w, (h * 2));
drawPillShadow();
drawPill();
ctx.save();
ctx.translate(0, (h * 2) + 1);
ctx.scale(1, -1);
drawPill();
ctx.restore();
this._fadeOutRect(ctx, x, y + h + 1, w, h, 0.5, 0.0);
},
_updateSummaryGraphSoon: function()
{
if ("_updateSummaryGraphTimeout" in this)
return;
this._updateSummaryGraphTimeout = setTimeout(this._updateSummaryGraph.bind(this), (this._showingEmptySummaryGraph ? 0 : 500));
},
_updateSummaryGraph: function()
{
if ("_updateSummaryGraphTimeout" in this) {
clearTimeout(this._updateSummaryGraphTimeout);
delete this._updateSummaryGraphTimeout;
}
var graphInfo = this.calculator.computeSummaryValues(this._resources);
var categoryOrder = ["documents", "stylesheets", "images", "scripts", "xhr", "fonts", "other"];
var categoryColors = {documents: {r: 47, g: 102, b: 236}, stylesheets: {r: 157, g: 231, b: 119}, images: {r: 164, g: 60, b: 255}, scripts: {r: 255, g: 121, b: 0}, xhr: {r: 231, g: 231, b: 10}, fonts: {r: 255, g: 82, b: 62}, other: {r: 186, g: 186, b: 186}};
var fillSegments = [];
this.legendElement.removeChildren();
if (this.totalLegendLabel)
this.totalLegendLabel.parentNode.removeChild(this.totalLegendLabel);
for (var i = 0; i < categoryOrder.length; ++i) {
var category = categoryOrder[i];
var size = graphInfo.categoryValues[category];
if (!size)
continue;
var color = categoryColors[category];
var colorString = "rgb(" + color.r + ", " + color.g + ", " + color.b + ")";
var fillSegment = {color: colorString, value: size};
fillSegments.push(fillSegment);
var legendLabel = this._makeLegendElement(WebInspector.resourceCategories[category].title, this.calculator.formatValue(size), colorString);
this.legendElement.appendChild(legendLabel);
}
if (graphInfo.total) {
var totalLegendLabel = this._makeLegendElement(WebInspector.UIString("Total"), this.calculator.formatValue(graphInfo.total));
totalLegendLabel.addStyleClass("total");
this.legendElement.appendChild(totalLegendLabel);
}
this._drawSummaryGraph(fillSegments);
},
_updateDividersLabelBarPosition: function()
{
var scrollTop = this.containerElement.scrollTop;
var dividersTop = (scrollTop < this.summaryElement.offsetHeight ? this.summaryElement.offsetHeight : scrollTop);
this.dividersElement.style.top = scrollTop + "px";
this.dividersLabelBarElement.style.top = dividersTop + "px";
},
_graphSelected: function(treeElement)
{
if (this._lastSelectedGraphTreeElement)
this._lastSelectedGraphTreeElement.selectedSortingOptionIndex = this.sortingSelectElement.selectedIndex;
this._lastSelectedGraphTreeElement = treeElement;
this.sortingSelectElement.removeChildren();
for (var i = 0; i < treeElement.sortingOptions.length; ++i) {
var sortingOption = treeElement.sortingOptions[i];
var option = document.createElement("option");
option.label = sortingOption.name;
option.sortingFunction = sortingOption.sortingFunction;
option.calculator = sortingOption.calculator;
this.sortingSelectElement.appendChild(option);
}
this.sortingSelectElement.selectedIndex = treeElement.selectedSortingOptionIndex;
this._changeSortingFunction();
this.closeVisibleResource();
this.containerElement.scrollTop = 0;
},
_toggleLargerResources: function()
{
if (!this.resourcesTreeElement._childrenListNode)
return;
this.resourcesTreeElement.smallChildren = !this.resourcesTreeElement.smallChildren;
if (this.resourcesTreeElement.smallChildren) {
this.resourcesGraphsElement.addStyleClass("small");
this.largerResourcesButton.title = WebInspector.UIString("Use large resource rows.");
this.largerResourcesButton.removeStyleClass("toggled-on");
} else {
this.resourcesGraphsElement.removeStyleClass("small");
this.largerResourcesButton.title = WebInspector.UIString("Use small resource rows.");
this.largerResourcesButton.addStyleClass("toggled-on");
}
},
_changeSortingFunction: function()
{
var selectedOption = this.sortingSelectElement[this.sortingSelectElement.selectedIndex];
this.calculator = selectedOption.calculator;
this.sortingFunction = selectedOption.sortingFunction;
},
_createResourceView: function(resource)
{
switch (resource.category) {
case WebInspector.resourceCategories.documents:
case WebInspector.resourceCategories.stylesheets:
case WebInspector.resourceCategories.scripts:
case WebInspector.resourceCategories.xhr:
return new WebInspector.SourceView(resource);
case WebInspector.resourceCategories.images:
return new WebInspector.ImageView(resource);
case WebInspector.resourceCategories.fonts:
return new WebInspector.FontView(resource);
default:
return new WebInspector.ResourceView(resource);
}
},
_startSidebarDragging: function(event)
{
WebInspector.elementDragStart(this.sidebarResizeElement, this._sidebarDragging.bind(this), this._endSidebarDragging.bind(this), event, "col-resize");
},
_sidebarDragging: function(event)
{
this._updateSidebarWidth(event.pageX);
event.preventDefault();
},
_endSidebarDragging: function(event)
{
WebInspector.elementDragEnd(event);
},
_updateSidebarWidth: function(width)
{
if (this.sidebarElement.offsetWidth <= 0) {
// The stylesheet hasn't loaded yet or the window is closed,
// so we can't calculate what is need. Return early.
return;
}
if (!("_currentSidebarWidth" in this))
this._currentSidebarWidth = this.sidebarElement.offsetWidth;
if (typeof width === "undefined")
width = this._currentSidebarWidth;
width = Number.constrain(width, Preferences.minSidebarWidth, window.innerWidth / 2);
this._currentSidebarWidth = width;
if (this.visibleResource) {
this.containerElement.style.width = width + "px";
this.sidebarElement.style.removeProperty("width");
} else {
this.sidebarElement.style.width = width + "px";
this.containerElement.style.removeProperty("width");
}
this.containerContentElement.style.left = width + "px";
this.viewsContainerElement.style.left = width + "px";
this.sidebarResizeElement.style.left = (width - 3) + "px";
this._updateGraphDividersIfNeeded();
var visibleView = this.visibleView;
if (visibleView && "resize" in visibleView)
visibleView.resize();
}
}
WebInspector.ResourcesPanel.prototype.__proto__ = WebInspector.Panel.prototype;
WebInspector.ResourceCalculator = function()
{
}
WebInspector.ResourceCalculator.prototype = {
computeSummaryValues: function(resources)
{
var total = 0;
var categoryValues = {};
var resourcesLength = resources.length;
for (var i = 0; i < resourcesLength; ++i) {
var resource = resources[i];
var value = this._value(resource);
if (typeof value === "undefined")
continue;
if (!(resource.category.name in categoryValues))
categoryValues[resource.category.name] = 0;
categoryValues[resource.category.name] += value;
total += value;
}
return {categoryValues: categoryValues, total: total};
},
computeBarGraphPercentages: function(resource)
{
return {start: 0, middle: 0, end: (this._value(resource) / this.boundarySpan) * 100};
},
get boundarySpan()
{
return this.maximumBoundary - this.minimumBoundary;
},
updateBoundries: function(resource)
{
this.minimumBoundary = 0;
var value = this._value(resource);
if (typeof this.maximumBoundary === "undefined" || value > this.maximumBoundary) {
this.maximumBoundary = value;
return true;
}
return false;
},
reset: function()
{
delete this.minimumBoundary;
delete this.maximumBoundary;
},
_value: function(resource)
{
return 0;
},
formatValue: function(value)
{
return value.toString();
}
}
WebInspector.ResourceTimeCalculator = function(startAtZero)
{
WebInspector.ResourceCalculator.call(this);
this.startAtZero = startAtZero;
}
WebInspector.ResourceTimeCalculator.prototype = {
computeSummaryValues: function(resources)
{
var resourcesByCategory = {};
var resourcesLength = resources.length;
for (var i = 0; i < resourcesLength; ++i) {
var resource = resources[i];
if (!(resource.category.name in resourcesByCategory))
resourcesByCategory[resource.category.name] = [];
resourcesByCategory[resource.category.name].push(resource);
}
var earliestStart;
var latestEnd;
var categoryValues = {};
for (var category in resourcesByCategory) {
resourcesByCategory[category].sort(WebInspector.Resource.CompareByTime);
categoryValues[category] = 0;
var segment = {start: -1, end: -1};
var categoryResources = resourcesByCategory[category];
var resourcesLength = categoryResources.length;
for (var i = 0; i < resourcesLength; ++i) {
var resource = categoryResources[i];
if (resource.startTime === -1 || resource.endTime === -1)
continue;
if (typeof earliestStart === "undefined")
earliestStart = resource.startTime;
else
earliestStart = Math.min(earliestStart, resource.startTime);
if (typeof latestEnd === "undefined")
latestEnd = resource.endTime;
else
latestEnd = Math.max(latestEnd, resource.endTime);
if (resource.startTime <= segment.end) {
segment.end = Math.max(segment.end, resource.endTime);
continue;
}
categoryValues[category] += segment.end - segment.start;
segment.start = resource.startTime;
segment.end = resource.endTime;
}
// Add the last segment
categoryValues[category] += segment.end - segment.start;
}
return {categoryValues: categoryValues, total: latestEnd - earliestStart};
},
computeBarGraphPercentages: function(resource)
{
if (resource.startTime !== -1)
var start = ((resource.startTime - this.minimumBoundary) / this.boundarySpan) * 100;
else
var start = 100;
if (resource.responseReceivedTime !== -1)
var middle = ((resource.responseReceivedTime - this.minimumBoundary) / this.boundarySpan) * 100;
else
var middle = 100;
if (resource.endTime !== -1)
var end = ((resource.endTime - this.minimumBoundary) / this.boundarySpan) * 100;
else
var end = 100;
if (this.startAtZero) {
end -= start;
middle -= start;
start = 0;
}
return {start: start, middle: middle, end: end};
},
updateBoundries: function(resource)
{
var didChange = false;
var lowerBound;
if (this.startAtZero)
lowerBound = 0;
else
lowerBound = this._lowerBound(resource);
if (lowerBound !== -1 && (typeof this.minimumBoundary === "undefined" || lowerBound < this.minimumBoundary)) {
this.minimumBoundary = lowerBound;
didChange = true;
}
var upperBound = this._upperBound(resource);
if (upperBound !== -1 && (typeof this.maximumBoundary === "undefined" || upperBound > this.maximumBoundary)) {
this.maximumBoundary = upperBound;
didChange = true;
}
return didChange;
},
formatValue: function(value)
{
return Number.secondsToString(value, WebInspector.UIString.bind(WebInspector));
},
_lowerBound: function(resource)
{
return 0;
},
_upperBound: function(resource)
{
return 0;
},
}
WebInspector.ResourceTimeCalculator.prototype.__proto__ = WebInspector.ResourceCalculator.prototype;
WebInspector.ResourceTransferTimeCalculator = function()
{
WebInspector.ResourceTimeCalculator.call(this, false);
}
WebInspector.ResourceTransferTimeCalculator.prototype = {
formatValue: function(value)
{
return Number.secondsToString(value, WebInspector.UIString.bind(WebInspector));
},
_lowerBound: function(resource)
{
return resource.startTime;
},
_upperBound: function(resource)
{
return resource.endTime;
},
}
WebInspector.ResourceTransferTimeCalculator.prototype.__proto__ = WebInspector.ResourceTimeCalculator.prototype;
WebInspector.ResourceTransferDurationCalculator = function()
{
WebInspector.ResourceTimeCalculator.call(this, true);
}
WebInspector.ResourceTransferDurationCalculator.prototype = {
formatValue: function(value)
{
return Number.secondsToString(value, WebInspector.UIString.bind(WebInspector));
},
_upperBound: function(resource)
{
return resource.duration;
},
}
WebInspector.ResourceTransferDurationCalculator.prototype.__proto__ = WebInspector.ResourceTimeCalculator.prototype;
WebInspector.ResourceTransferSizeCalculator = function()
{
WebInspector.ResourceCalculator.call(this);
}
WebInspector.ResourceTransferSizeCalculator.prototype = {
_value: function(resource)
{
return resource.contentLength;
},
formatValue: function(value)
{
return Number.bytesToString(value, WebInspector.UIString.bind(WebInspector));
}
}
WebInspector.ResourceTransferSizeCalculator.prototype.__proto__ = WebInspector.ResourceCalculator.prototype;
WebInspector.ResourceSidebarTreeElement = function(resource)
{
this.resource = resource;
this.createIconElement();
WebInspector.SidebarTreeElement.call(this, "resource-sidebar-tree-item", "", "", resource);
this.refreshTitles();
}
WebInspector.ResourceSidebarTreeElement.prototype = {
onattach: function()
{
WebInspector.SidebarTreeElement.prototype.onattach.call(this);
this._listItemNode.addStyleClass("resources-category-" + this.resource.category.name);
},
onselect: function()
{
WebInspector.panels.resources.showResource(this.resource);
},
get mainTitle()
{
return this.resource.displayName;
},
set mainTitle(x)
{
// Do nothing.
},
get subtitle()
{
var subtitle = this.resource.displayDomain;
if (this.resource.path && this.resource.lastPathComponent) {
var lastPathComponentIndex = this.resource.path.lastIndexOf("/" + this.resource.lastPathComponent);
if (lastPathComponentIndex != -1)
subtitle += this.resource.path.substring(0, lastPathComponentIndex);
}
return subtitle;
},
set subtitle(x)
{
// Do nothing.
},
createIconElement: function()
{
var previousIconElement = this.iconElement;
if (this.resource.category === WebInspector.resourceCategories.images) {
var previewImage = document.createElement("img");
previewImage.className = "image-resource-icon-preview";
previewImage.src = this.resource.url;
this.iconElement = document.createElement("div");
this.iconElement.className = "icon";
this.iconElement.appendChild(previewImage);
} else {
this.iconElement = document.createElement("img");
this.iconElement.className = "icon";
}
if (previousIconElement)
previousIconElement.parentNode.replaceChild(this.iconElement, previousIconElement);
},
refresh: function()
{
this.refreshTitles();
if (!this._listItemNode.hasStyleClass("resources-category-" + this.resource.category.name)) {
this._listItemNode.removeMatchingStyleClasses("resources-category-\\w+");
this._listItemNode.addStyleClass("resources-category-" + this.resource.category.name);
this.createIconElement();
}
},
resetBubble: function()
{
this.bubbleText = "";
this.bubbleElement.removeStyleClass("search-matches");
this.bubbleElement.removeStyleClass("warning");
this.bubbleElement.removeStyleClass("error");
},
set searchMatches(matches)
{
this.resetBubble();
if (!matches)
return;
this.bubbleText = matches;
this.bubbleElement.addStyleClass("search-matches");
},
updateErrorsAndWarnings: function()
{
this.resetBubble();
if (this.resource.warnings || this.resource.errors)
this.bubbleText = (this.resource.warnings + this.resource.errors);
if (this.resource.warnings)
this.bubbleElement.addStyleClass("warning");
if (this.resource.errors)
this.bubbleElement.addStyleClass("error");
}
}
WebInspector.ResourceSidebarTreeElement.CompareByAscendingStartTime = function(a, b)
{
return WebInspector.Resource.CompareByStartTime(a.resource, b.resource)
|| WebInspector.Resource.CompareByEndTime(a.resource, b.resource)
|| WebInspector.Resource.CompareByResponseReceivedTime(a.resource, b.resource);
}
WebInspector.ResourceSidebarTreeElement.CompareByAscendingResponseReceivedTime = function(a, b)
{
return WebInspector.Resource.CompareByResponseReceivedTime(a.resource, b.resource)
|| WebInspector.Resource.CompareByStartTime(a.resource, b.resource)
|| WebInspector.Resource.CompareByEndTime(a.resource, b.resource);
}
WebInspector.ResourceSidebarTreeElement.CompareByAscendingEndTime = function(a, b)
{
return WebInspector.Resource.CompareByEndTime(a.resource, b.resource)
|| WebInspector.Resource.CompareByStartTime(a.resource, b.resource)
|| WebInspector.Resource.CompareByResponseReceivedTime(a.resource, b.resource);
}
WebInspector.ResourceSidebarTreeElement.CompareByDescendingDuration = function(a, b)
{
return -1 * WebInspector.Resource.CompareByDuration(a.resource, b.resource);
}
WebInspector.ResourceSidebarTreeElement.CompareByDescendingLatency = function(a, b)
{
return -1 * WebInspector.Resource.CompareByLatency(a.resource, b.resource);
}
WebInspector.ResourceSidebarTreeElement.CompareByDescendingSize = function(a, b)
{
return -1 * WebInspector.Resource.CompareBySize(a.resource, b.resource);
}
WebInspector.ResourceSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype;
WebInspector.ResourceGraph = function(resource)
{
this.resource = resource;
this._graphElement = document.createElement("div");
this._graphElement.className = "resources-graph-side";
this._barAreaElement = document.createElement("div");
this._barAreaElement.className = "resources-graph-bar-area";
this._graphElement.appendChild(this._barAreaElement);
this._barLeftElement = document.createElement("div");
this._barLeftElement.className = "resources-graph-bar waiting";
this._barAreaElement.appendChild(this._barLeftElement);
this._barRightElement = document.createElement("div");
this._barRightElement.className = "resources-graph-bar";
this._barAreaElement.appendChild(this._barRightElement);
this._graphElement.addStyleClass("resources-category-" + resource.category.name);
}
WebInspector.ResourceGraph.prototype = {
get graphElement()
{
return this._graphElement;
},
refresh: function(percentages)
{
if (!this._graphElement.hasStyleClass("resources-category-" + this.resource.category.name)) {
this._graphElement.removeMatchingStyleClasses("resources-category-\\w+");
this._graphElement.addStyleClass("resources-category-" + this.resource.category.name);
}
this._barLeftElement.style.setProperty("left", percentages.start + "%");
this._barLeftElement.style.setProperty("right", (100 - percentages.end) + "%");
this._barRightElement.style.setProperty("left", percentages.middle + "%");
this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%");
}
}
/* ScriptsPanel.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
WebInspector.ScriptsPanel = function()
{
WebInspector.Panel.call(this);
this.element.addStyleClass("scripts");
this.topStatusBar = document.createElement("div");
this.topStatusBar.className = "status-bar";
this.topStatusBar.id = "scripts-status-bar";
this.element.appendChild(this.topStatusBar);
this.backButton = document.createElement("button");
this.backButton.className = "status-bar-item";
this.backButton.id = "scripts-back";
this.backButton.title = WebInspector.UIString("Show the previous script resource.");
this.backButton.disabled = true;
this.backButton.appendChild(document.createElement("img"));
this.backButton.addEventListener("click", this._goBack.bind(this), false);
this.topStatusBar.appendChild(this.backButton);
this.forwardButton = document.createElement("button");
this.forwardButton.className = "status-bar-item";
this.forwardButton.id = "scripts-forward";
this.forwardButton.title = WebInspector.UIString("Show the next script resource.");
this.forwardButton.disabled = true;
this.forwardButton.appendChild(document.createElement("img"));
this.forwardButton.addEventListener("click", this._goForward.bind(this), false);
this.topStatusBar.appendChild(this.forwardButton);
this.filesSelectElement = document.createElement("select");
this.filesSelectElement.className = "status-bar-item";
this.filesSelectElement.id = "scripts-files";
this.filesSelectElement.addEventListener("change", this._changeVisibleFile.bind(this), false);
this.topStatusBar.appendChild(this.filesSelectElement);
this.functionsSelectElement = document.createElement("select");
this.functionsSelectElement.className = "status-bar-item";
this.functionsSelectElement.id = "scripts-functions";
// FIXME: append the functions select element to the top status bar when it is implemented.
// this.topStatusBar.appendChild(this.functionsSelectElement);
this.sidebarButtonsElement = document.createElement("div");
this.sidebarButtonsElement.id = "scripts-sidebar-buttons";
this.topStatusBar.appendChild(this.sidebarButtonsElement);
this.pauseButton = document.createElement("button");
this.pauseButton.className = "status-bar-item";
this.pauseButton.id = "scripts-pause";
this.pauseButton.title = WebInspector.UIString("Pause script execution.");
this.pauseButton.disabled = true;
this.pauseButton.appendChild(document.createElement("img"));
this.pauseButton.addEventListener("click", this._togglePause.bind(this), false);
this.sidebarButtonsElement.appendChild(this.pauseButton);
this.stepOverButton = document.createElement("button");
this.stepOverButton.className = "status-bar-item";
this.stepOverButton.id = "scripts-step-over";
this.stepOverButton.title = WebInspector.UIString("Step over next function call.");
this.stepOverButton.disabled = true;
this.stepOverButton.addEventListener("click", this._stepOverClicked.bind(this), false);
this.stepOverButton.appendChild(document.createElement("img"));
this.sidebarButtonsElement.appendChild(this.stepOverButton);
this.stepIntoButton = document.createElement("button");
this.stepIntoButton.className = "status-bar-item";
this.stepIntoButton.id = "scripts-step-into";
this.stepIntoButton.title = WebInspector.UIString("Step into next function call.");
this.stepIntoButton.disabled = true;
this.stepIntoButton.addEventListener("click", this._stepIntoClicked.bind(this), false);
this.stepIntoButton.appendChild(document.createElement("img"));
this.sidebarButtonsElement.appendChild(this.stepIntoButton);
this.stepOutButton = document.createElement("button");
this.stepOutButton.className = "status-bar-item";
this.stepOutButton.id = "scripts-step-out";
this.stepOutButton.title = WebInspector.UIString("Step out of current function.");
this.stepOutButton.disabled = true;
this.stepOutButton.addEventListener("click", this._stepOutClicked.bind(this), false);
this.stepOutButton.appendChild(document.createElement("img"));
this.sidebarButtonsElement.appendChild(this.stepOutButton);
this.debuggerStatusElement = document.createElement("div");
this.debuggerStatusElement.id = "scripts-debugger-status";
this.sidebarButtonsElement.appendChild(this.debuggerStatusElement);
this.viewsContainerElement = document.createElement("div");
this.viewsContainerElement.id = "script-resource-views";
this.sidebarElement = document.createElement("div");
this.sidebarElement.id = "scripts-sidebar";
this.sidebarResizeElement = document.createElement("div");
this.sidebarResizeElement.className = "sidebar-resizer-vertical";
this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarResizeDrag.bind(this), false);
this.sidebarResizeWidgetElement = document.createElement("div");
this.sidebarResizeWidgetElement.id = "scripts-sidebar-resizer-widget";
this.sidebarResizeWidgetElement.addEventListener("mousedown", this._startSidebarResizeDrag.bind(this), false);
this.topStatusBar.appendChild(this.sidebarResizeWidgetElement);
this.sidebarPanes = {};
this.sidebarPanes.callstack = new WebInspector.CallStackSidebarPane();
this.sidebarPanes.scopechain = new WebInspector.ScopeChainSidebarPane();
this.sidebarPanes.breakpoints = new WebInspector.BreakpointsSidebarPane();
for (var pane in this.sidebarPanes)
this.sidebarElement.appendChild(this.sidebarPanes[pane].element);
// FIXME: remove the following line of code when the Breakpoints pane has content.
this.sidebarElement.removeChild(this.sidebarPanes.breakpoints.element);
this.sidebarPanes.callstack.expanded = true;
this.sidebarPanes.callstack.addEventListener("call frame selected", this._callFrameSelected, this);
this.sidebarPanes.scopechain.expanded = true;
this.attachOverlayElement = document.createElement("div");
this.attachOverlayElement.id = "scripts-attach-overlay";
var headerElement = document.createElement("h1");
headerElement.textContent = WebInspector.UIString("Starting debugging will reload the inspected page.");
this.attachOverlayElement.appendChild(headerElement);
this.attachOverlayElement.appendChild(document.createElement("br"));
var attachButton = document.createElement("button");
attachButton.textContent = WebInspector.UIString("Start Debugging");
attachButton.addEventListener("click", this._toggleDebugging.bind(this), false);
this.attachOverlayElement.appendChild(attachButton);
this.element.appendChild(this.attachOverlayElement);
this.element.appendChild(this.viewsContainerElement);
this.element.appendChild(this.sidebarElement);
this.element.appendChild(this.sidebarResizeElement);
this.debuggingButton = document.createElement("button");
this.debuggingButton.id = "scripts-debugging-status-bar-item";
this.debuggingButton.className = "status-bar-item";
this.debuggingButton.addEventListener("click", this._toggleDebugging.bind(this), false);
this.pauseOnExceptionButtons = document.createElement("button");
this.pauseOnExceptionButtons.id = "scripts-pause-on-exceptions-status-bar-item";
this.pauseOnExceptionButtons.className = "status-bar-item";
this.pauseOnExceptionButtons.addEventListener("click", this._togglePauseOnExceptions.bind(this), false);
this._breakpointsURLMap = {};
this.reset();
}
WebInspector.ScriptsPanel.prototype = {
toolbarItemClass: "scripts",
get toolbarItemLabel()
{
return WebInspector.UIString("Scripts");
},
get statusBarItems()
{
return [this.debuggingButton, this.pauseOnExceptionButtons];
},
get paused()
{
return this._paused;
},
show: function()
{
WebInspector.Panel.prototype.show.call(this);
this.sidebarResizeElement.style.right = (this.sidebarElement.offsetWidth - 3) + "px";
if (this.visibleView) {
if (this.visibleView instanceof WebInspector.ResourceView)
this.visibleView.headersVisible = false;
this.visibleView.show(this.viewsContainerElement);
}
// Hide any views that are visible that are not this panel's current visible view.
// This can happen when a ResourceView is visible in the Resources panel then switched
// to the this panel.
for (var sourceID in this._sourceIDMap) {
var scriptOrResource = this._sourceIDMap[sourceID];
var view = this._sourceViewForScriptOrResource(scriptOrResource);
if (!view || view === this.visibleView)
continue;
view.visible = false;
}
},
get searchableViews()
{
var views = [];
const visibleView = this.visibleView;
if (visibleView && visibleView.performSearch) {
visibleView.alreadySearching = true;
views.push(visibleView);
}
for (var sourceID in this._sourceIDMap) {
var scriptOrResource = this._sourceIDMap[sourceID];
var view = this._sourceViewForScriptOrResource(scriptOrResource);
if (!view.performSearch || view.alreadySearching)
continue;
view.alreadySearching = true;
views.push(view);
}
for (var i = 0; i < views.length; ++i)
delete views[i].alreadySearching;
return views;
},
addScript: function(sourceID, sourceURL, source, startingLine, errorLine, errorMessage)
{
var script = new WebInspector.Script(sourceID, sourceURL, source, startingLine, errorLine, errorMessage);
if (sourceURL in WebInspector.resourceURLMap) {
var resource = WebInspector.resourceURLMap[sourceURL];
resource.addScript(script);
}
if (sourceURL in this._breakpointsURLMap && sourceID) {
var breakpoints = this._breakpointsURLMap[sourceURL];
var breakpointsLength = breakpoints.length;
for (var i = 0; i < breakpointsLength; ++i) {
var breakpoint = breakpoints[i];
if (startingLine <= breakpoint.line) {
breakpoint.sourceID = sourceID;
if (breakpoint.enabled)
InspectorController.addBreakpoint(breakpoint.sourceID, breakpoint.line);
}
}
}
if (sourceID)
this._sourceIDMap[sourceID] = (resource || script);
this._addScriptToFilesMenu(script);
},
addBreakpoint: function(breakpoint)
{
this.sidebarPanes.breakpoints.addBreakpoint(breakpoint);
var sourceFrame;
if (breakpoint.url) {
if (!(breakpoint.url in this._breakpointsURLMap))
this._breakpointsURLMap[breakpoint.url] = [];
this._breakpointsURLMap[breakpoint.url].unshift(breakpoint);
if (breakpoint.url in WebInspector.resourceURLMap) {
var resource = WebInspector.resourceURLMap[breakpoint.url];
sourceFrame = this._sourceFrameForScriptOrResource(resource);
}
}
if (breakpoint.sourceID && !sourceFrame) {
var object = this._sourceIDMap[breakpoint.sourceID]
sourceFrame = this._sourceFrameForScriptOrResource(object);
}
if (sourceFrame)
sourceFrame.addBreakpoint(breakpoint);
},
removeBreakpoint: function(breakpoint)
{
this.sidebarPanes.breakpoints.removeBreakpoint(breakpoint);
var sourceFrame;
if (breakpoint.url && breakpoint.url in this._breakpointsURLMap) {
var breakpoints = this._breakpointsURLMap[breakpoint.url];
breakpoints.remove(breakpoint);
if (!breakpoints.length)
delete this._breakpointsURLMap[breakpoint.url];
if (breakpoint.url in WebInspector.resourceURLMap) {
var resource = WebInspector.resourceURLMap[breakpoint.url];
sourceFrame = this._sourceFrameForScriptOrResource(resource);
}
}
if (breakpoint.sourceID && !sourceFrame) {
var object = this._sourceIDMap[breakpoint.sourceID]
sourceFrame = this._sourceFrameForScriptOrResource(object);
}
if (sourceFrame)
sourceFrame.removeBreakpoint(breakpoint);
},
evaluateInSelectedCallFrame: function(code, updateInterface)
{
var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame;
if (!this._paused || !selectedCallFrame)
return;
if (typeof updateInterface === "undefined")
updateInterface = true;
var result = selectedCallFrame.evaluate(code);
if (updateInterface)
this.sidebarPanes.scopechain.update(selectedCallFrame);
return result;
},
variablesInScopeForSelectedCallFrame: function()
{
var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame;
if (!this._paused || !selectedCallFrame)
return {};
var result = {};
var scopeChain = selectedCallFrame.scopeChain;
for (var i = 0; i < scopeChain.length; ++i) {
var scopeObject = scopeChain[i];
for (var property in scopeObject)
result[property] = true;
}
return result;
},
debuggerPaused: function()
{
this._paused = true;
this._waitingToPause = false;
this._stepping = false;
this._updateDebuggerButtons();
var callStackPane = this.sidebarPanes.callstack;
var currentFrame = InspectorController.currentCallFrame();
callStackPane.update(currentFrame, this._sourceIDMap);
callStackPane.selectedCallFrame = currentFrame;
WebInspector.currentPanel = this;
window.focus();
},
debuggerAttached: function()
{
this.reset();
},
debuggerDetached: function()
{
this.reset();
},
reset: function()
{
this.visibleView = null;
delete this.currentQuery;
this.searchCanceled();
if (!InspectorController.debuggerAttached()) {
this._paused = false;
this._waitingToPause = false;
this._stepping = false;
}
this._clearInterface();
this._backForwardList = [];
this._currentBackForwardIndex = -1;
this._updateBackAndForwardButtons();
this._scriptsForURLsInFilesSelect = {};
this.filesSelectElement.removeChildren();
this.functionsSelectElement.removeChildren();
this.viewsContainerElement.removeChildren();
if (this._sourceIDMap) {
for (var sourceID in this._sourceIDMap) {
var object = this._sourceIDMap[sourceID];
if (object instanceof WebInspector.Resource)
object.removeAllScripts();
}
}
this._sourceIDMap = {};
},
get visibleView()
{
return this._visibleView;
},
set visibleView(x)
{
if (this._visibleView === x)
return;
if (this._visibleView)
this._visibleView.hide();
this._visibleView = x;
if (x)
x.show(this.viewsContainerElement);
},
canShowResource: function(resource)
{
return resource && resource.scripts.length && InspectorController.debuggerAttached();
},
showScript: function(script, line)
{
this._showScriptOrResource(script, line, true);
},
showResource: function(resource, line)
{
this._showScriptOrResource(resource, line, true);
},
showView: function(view)
{
if (!view)
return;
this._showScriptOrResource((view.resource || view.script));
},
scriptViewForScript: function(script)
{
if (!script)
return null;
if (!script._scriptView)
script._scriptView = new WebInspector.ScriptView(script);
return script._scriptView;
},
sourceFrameForScript: function(script)
{
var view = this.scriptViewForScript(script);
if (!view)
return null;
// Setting up the source frame requires that we be attached.
if (!this.element.parentNode)
this.attach();
view.setupSourceFrameIfNeeded();
return view.sourceFrame;
},
_sourceViewForScriptOrResource: function(scriptOrResource)
{
if (scriptOrResource instanceof WebInspector.Resource)
return WebInspector.panels.resources.resourceViewForResource(scriptOrResource);
if (scriptOrResource instanceof WebInspector.Script)
return this.scriptViewForScript(scriptOrResource);
},
_sourceFrameForScriptOrResource: function(scriptOrResource)
{
if (scriptOrResource instanceof WebInspector.Resource)
return WebInspector.panels.resources.sourceFrameForResource(scriptOrResource);
if (scriptOrResource instanceof WebInspector.Script)
return this.sourceFrameForScript(scriptOrResource);
},
_showScriptOrResource: function(scriptOrResource, line, shouldHighlightLine, fromBackForwardAction)
{
if (!scriptOrResource)
return;
var view;
if (scriptOrResource instanceof WebInspector.Resource) {
view = WebInspector.panels.resources.resourceViewForResource(scriptOrResource);
view.headersVisible = false;
if (scriptOrResource.url in this._breakpointsURLMap) {
var sourceFrame = this._sourceFrameForScriptOrResource(scriptOrResource);
if (sourceFrame && !sourceFrame.breakpoints.length) {
var breakpoints = this._breakpointsURLMap[scriptOrResource.url];
var breakpointsLength = breakpoints.length;
for (var i = 0; i < breakpointsLength; ++i)
sourceFrame.addBreakpoint(breakpoints[i]);
}
}
} else if (scriptOrResource instanceof WebInspector.Script)
view = this.scriptViewForScript(scriptOrResource);
if (!view)
return;
if (!fromBackForwardAction) {
var oldIndex = this._currentBackForwardIndex;
if (oldIndex >= 0)
this._backForwardList.splice(oldIndex + 1, this._backForwardList.length - oldIndex);
// Check for a previous entry of the same object in _backForwardList.
// If one is found, remove it and update _currentBackForwardIndex to match.
var previousEntryIndex = this._backForwardList.indexOf(scriptOrResource);
if (previousEntryIndex !== -1) {
this._backForwardList.splice(previousEntryIndex, 1);
--this._currentBackForwardIndex;
}
this._backForwardList.push(scriptOrResource);
++this._currentBackForwardIndex;
this._updateBackAndForwardButtons();
}
this.visibleView = view;
if (line) {
if (view.revealLine)
view.revealLine(line);
if (view.highlightLine && shouldHighlightLine)
view.highlightLine(line);
}
var option;
if (scriptOrResource instanceof WebInspector.Script) {
option = scriptOrResource.filesSelectOption;
console.assert(option);
} else {
var url = scriptOrResource.url;
var script = this._scriptsForURLsInFilesSelect[url];
if (script)
option = script.filesSelectOption;
}
if (option)
this.filesSelectElement.selectedIndex = option.index;
},
_addScriptToFilesMenu: function(script)
{
if (script.resource && this._scriptsForURLsInFilesSelect[script.sourceURL])
return;
this._scriptsForURLsInFilesSelect[script.sourceURL] = script;
var select = this.filesSelectElement;
// FIXME: Append in some meaningful order.
var option = document.createElement("option");
option.representedObject = (script.resource || script);
option.text = (script.sourceURL ? WebInspector.displayNameForURL(script.sourceURL) : WebInspector.UIString("(program)"));
select.appendChild(option);
script.filesSelectOption = option;
// Call _showScriptOrResource if the option we just appended ended up being selected.
// This will happen for the first item added to the menu.
if (select.options[select.selectedIndex] === option)
this._showScriptOrResource(option.representedObject);
},
_clearCurrentExecutionLine: function()
{
if (this._executionSourceFrame)
this._executionSourceFrame.executionLine = 0;
delete this._executionSourceFrame;
},
_callFrameSelected: function()
{
this._clearCurrentExecutionLine();
var callStackPane = this.sidebarPanes.callstack;
var currentFrame = callStackPane.selectedCallFrame;
if (!currentFrame)
return;
this.sidebarPanes.scopechain.update(currentFrame);
var scriptOrResource = this._sourceIDMap[currentFrame.sourceID];
this._showScriptOrResource(scriptOrResource, currentFrame.line);
this._executionSourceFrame = this._sourceFrameForScriptOrResource(scriptOrResource);
if (this._executionSourceFrame)
this._executionSourceFrame.executionLine = currentFrame.line;
},
_changeVisibleFile: function(event)
{
var select = this.filesSelectElement;
this._showScriptOrResource(select.options[select.selectedIndex].representedObject);
},
_startSidebarResizeDrag: function(event)
{
WebInspector.elementDragStart(this.sidebarElement, this._sidebarResizeDrag.bind(this), this._endSidebarResizeDrag.bind(this), event, "col-resize");
if (event.target === this.sidebarResizeWidgetElement)
this._dragOffset = (event.target.offsetWidth - (event.pageX - event.target.totalOffsetLeft));
else
this._dragOffset = 0;
},
_endSidebarResizeDrag: function(event)
{
WebInspector.elementDragEnd(event);
delete this._dragOffset;
},
_sidebarResizeDrag: function(event)
{
var x = event.pageX + this._dragOffset;
var newWidth = Number.constrain(window.innerWidth - x, Preferences.minScriptsSidebarWidth, window.innerWidth * 0.66);
this.sidebarElement.style.width = newWidth + "px";
this.sidebarButtonsElement.style.width = newWidth + "px";
this.viewsContainerElement.style.right = newWidth + "px";
this.sidebarResizeWidgetElement.style.right = newWidth + "px";
this.sidebarResizeElement.style.right = (newWidth - 3) + "px";
event.preventDefault();
},
_updatePauseOnExceptionsButton: function()
{
if (InspectorController.pauseOnExceptions()) {
this.pauseOnExceptionButtons.title = WebInspector.UIString("Don't pause on exceptions.");
this.pauseOnExceptionButtons.addStyleClass("toggled-on");
} else {
this.pauseOnExceptionButtons.title = WebInspector.UIString("Pause on exceptions.");
this.pauseOnExceptionButtons.removeStyleClass("toggled-on");
}
},
_updateDebuggerButtons: function()
{
if (InspectorController.debuggerAttached()) {
this.debuggingButton.title = WebInspector.UIString("Stop debugging.");
this.debuggingButton.addStyleClass("toggled-on");
this.pauseButton.disabled = false;
if (this.attachOverlayElement.parentNode)
this.attachOverlayElement.parentNode.removeChild(this.attachOverlayElement);
} else {
this.debuggingButton.title = WebInspector.UIString("Start debugging and reload inspected page.");
this.debuggingButton.removeStyleClass("toggled-on");
this.pauseButton.disabled = true;
this.element.appendChild(this.attachOverlayElement);
}
this._updatePauseOnExceptionsButton();
if (this._paused) {
this.pauseButton.addStyleClass("paused");
this.pauseButton.disabled = false;
this.stepOverButton.disabled = false;
this.stepIntoButton.disabled = false;
this.stepOutButton.disabled = false;
this.debuggerStatusElement.textContent = WebInspector.UIString("Paused");
} else {
this.pauseButton.removeStyleClass("paused");
this.pauseButton.disabled = this._waitingToPause;
this.stepOverButton.disabled = true;
this.stepIntoButton.disabled = true;
this.stepOutButton.disabled = true;
if (this._waitingToPause)
this.debuggerStatusElement.textContent = WebInspector.UIString("Pausing");
else if (this._stepping)
this.debuggerStatusElement.textContent = WebInspector.UIString("Stepping");
else
this.debuggerStatusElement.textContent = "";
}
},
_updateBackAndForwardButtons: function()
{
this.backButton.disabled = this._currentBackForwardIndex <= 0;
this.forwardButton.disabled = this._currentBackForwardIndex >= (this._backForwardList.length - 1);
},
_clearInterface: function()
{
this.sidebarPanes.callstack.update(null);
this.sidebarPanes.scopechain.update(null);
this._clearCurrentExecutionLine();
this._updateDebuggerButtons();
},
_goBack: function()
{
if (this._currentBackForwardIndex <= 0) {
console.error("Can't go back from index " + this._currentBackForwardIndex);
return;
}
this._showScriptOrResource(this._backForwardList[--this._currentBackForwardIndex], null, false, true);
this._updateBackAndForwardButtons();
},
_goForward: function()
{
if (this._currentBackForwardIndex >= this._backForwardList.length - 1) {
console.error("Can't go forward from index " + this._currentBackForwardIndex);
return;
}
this._showScriptOrResource(this._backForwardList[++this._currentBackForwardIndex], null, false, true);
this._updateBackAndForwardButtons();
},
_toggleDebugging: function()
{
this._paused = false;
this._waitingToPause = false;
this._stepping = false;
if (InspectorController.debuggerAttached())
InspectorController.stopDebugging();
else
InspectorController.startDebuggingAndReloadInspectedPage();
this._clearInterface();
},
_togglePauseOnExceptions: function()
{
InspectorController.setPauseOnExceptions(!InspectorController.pauseOnExceptions());
this._updatePauseOnExceptionsButton();
},
_togglePause: function()
{
if (this._paused) {
this._paused = false;
this._waitingToPause = false;
InspectorController.resumeDebugger();
} else {
this._stepping = false;
this._waitingToPause = true;
InspectorController.pauseInDebugger();
}
this._clearInterface();
},
_stepOverClicked: function()
{
this._paused = false;
this._stepping = true;
this._clearInterface();
InspectorController.stepOverStatementInDebugger();
},
_stepIntoClicked: function()
{
this._paused = false;
this._stepping = true;
this._clearInterface();
InspectorController.stepIntoStatementInDebugger();
},
_stepOutClicked: function()
{
this._paused = false;
this._stepping = true;
this._clearInterface();
InspectorController.stepOutOfFunctionInDebugger();
}
}
WebInspector.ScriptsPanel.prototype.__proto__ = WebInspector.Panel.prototype;
/* DatabasesPanel.js */
/*
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
*
* 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.
*/
WebInspector.DatabasesPanel = function(database)
{
WebInspector.Panel.call(this);
this.sidebarElement = document.createElement("div");
this.sidebarElement.id = "databases-sidebar";
this.sidebarElement.className = "sidebar";
this.element.appendChild(this.sidebarElement);
this.sidebarResizeElement = document.createElement("div");
this.sidebarResizeElement.className = "sidebar-resizer-vertical";
this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarDragging.bind(this), false);
this.element.appendChild(this.sidebarResizeElement);
this.sidebarTreeElement = document.createElement("ol");
this.sidebarTreeElement.className = "sidebar-tree";
this.sidebarElement.appendChild(this.sidebarTreeElement);
this.sidebarTree = new TreeOutline(this.sidebarTreeElement);
this.databaseViews = document.createElement("div");
this.databaseViews.id = "database-views";
this.element.appendChild(this.databaseViews);
this.reset();
}
WebInspector.DatabasesPanel.prototype = {
toolbarItemClass: "databases",
get toolbarItemLabel()
{
return WebInspector.UIString("Databases");
},
show: function()
{
WebInspector.Panel.prototype.show.call(this);
this._updateSidebarWidth();
},
reset: function()
{
if (this._databases) {
var databasesLength = this._databases.length;
for (var i = 0; i < databasesLength; ++i) {
var database = this._databases[i];
delete database._tableViews;
delete database._queryView;
}
}
this._databases = [];
this.sidebarTree.removeChildren();
this.databaseViews.removeChildren();
},
handleKeyEvent: function(event)
{
this.sidebarTree.handleKeyEvent(event);
},
addDatabase: function(database)
{
this._databases.push(database);
var databaseTreeElement = new WebInspector.DatabaseSidebarTreeElement(database);
database._databasesTreeElement = databaseTreeElement;
this.sidebarTree.appendChild(databaseTreeElement);
},
showDatabase: function(database, tableName)
{
if (!database)
return;
if (this.visibleDatabaseView)
this.visibleDatabaseView.hide();
var view;
if (tableName) {
if (!("_tableViews" in database))
database._tableViews = {};
view = database._tableViews[tableName];
if (!view) {
view = new WebInspector.DatabaseTableView(database, tableName);
database._tableViews[tableName] = view;
}
} else {
view = database._queryView;
if (!view) {
view = new WebInspector.DatabaseQueryView(database);
database._queryView = view;
}
}
view.show(this.databaseViews);
this.visibleDatabaseView = view;
},
closeVisibleView: function()
{
if (this.visibleDatabaseView)
this.visibleDatabaseView.hide();
delete this.visibleDatabaseView;
},
updateDatabaseTables: function(database)
{
if (!database || !database._databasesTreeElement)
return;
database._databasesTreeElement.shouldRefreshChildren = true;
if (!("_tableViews" in database))
return;
var tableNamesHash = {};
var tableNames = database.tableNames;
var tableNamesLength = tableNames.length;
for (var i = 0; i < tableNamesLength; ++i)
tableNamesHash[tableNames[i]] = true;
for (var tableName in database._tableViews) {
if (!(tableName in tableNamesHash)) {
if (this.visibleDatabaseView === database._tableViews[tableName])
this.closeVisibleView();
delete database._tableViews[tableName];
}
}
},
dataGridForResult: function(result)
{
if (!result.rows.length)
return null;
var columns = {};
var rows = result.rows;
for (var columnIdentifier in rows.item(0)) {
var column = {};
column.width = columnIdentifier.length;
column.title = columnIdentifier;
columns[columnIdentifier] = column;
}
var nodes = [];
var length = rows.length;
for (var i = 0; i < length; ++i) {
var data = {};
var row = rows.item(i);
for (var columnIdentifier in row) {
// FIXME: (Bug 19439) We should specially format SQL NULL here
// (which is represented by JavaScript null here, and turned
// into the string "null" by the String() function).
var text = String(row[columnIdentifier]);
data[columnIdentifier] = text;
if (text.length > columns[columnIdentifier].width)
columns[columnIdentifier].width = text.length;
}
var node = new WebInspector.DataGridNode(data, false);
node.selectable = false;
nodes.push(node);
}
var totalColumnWidths = 0;
for (var columnIdentifier in columns)
totalColumnWidths += columns[columnIdentifier].width;
// Calculate the percentage width for the columns.
const minimumPrecent = 5;
var recoupPercent = 0;
for (var columnIdentifier in columns) {
var width = columns[columnIdentifier].width;
width = Math.round((width / totalColumnWidths) * 100);
if (width < minimumPrecent) {
recoupPercent += (minimumPrecent - width);
width = minimumPrecent;
}
columns[columnIdentifier].width = width;
}
// Enforce the minimum percentage width.
while (recoupPercent > 0) {
for (var columnIdentifier in columns) {
if (columns[columnIdentifier].width > minimumPrecent) {
--columns[columnIdentifier].width;
--recoupPercent;
if (!recoupPercent)
break;
}
}
}
// Change the width property to a string suitable for a style width.
for (var columnIdentifier in columns)
columns[columnIdentifier].width += "%";
var dataGrid = new WebInspector.DataGrid(columns);
var length = nodes.length;
for (var i = 0; i < length; ++i)
dataGrid.appendChild(nodes[i]);
return dataGrid;
},
_startSidebarDragging: function(event)
{
WebInspector.elementDragStart(this.sidebarResizeElement, this._sidebarDragging.bind(this), this._endSidebarDragging.bind(this), event, "col-resize");
},
_sidebarDragging: function(event)
{
this._updateSidebarWidth(event.pageX);
event.preventDefault();
},
_endSidebarDragging: function(event)
{
WebInspector.elementDragEnd(event);
},
_updateSidebarWidth: function(width)
{
if (this.sidebarElement.offsetWidth <= 0) {
// The stylesheet hasn't loaded yet or the window is closed,
// so we can't calculate what is need. Return early.
return;
}
if (!("_currentSidebarWidth" in this))
this._currentSidebarWidth = this.sidebarElement.offsetWidth;
if (typeof width === "undefined")
width = this._currentSidebarWidth;
width = Number.constrain(width, Preferences.minSidebarWidth, window.innerWidth / 2);
this._currentSidebarWidth = width;
this.sidebarElement.style.width = width + "px";
this.databaseViews.style.left = width + "px";
this.sidebarResizeElement.style.left = (width - 3) + "px";
}
}
WebInspector.DatabasesPanel.prototype.__proto__ = WebInspector.Panel.prototype;
WebInspector.DatabaseSidebarTreeElement = function(database)
{
this.database = database;
WebInspector.SidebarTreeElement.call(this, "database-sidebar-tree-item", "", "", database, true);
this.refreshTitles();
}
WebInspector.DatabaseSidebarTreeElement.prototype = {
onselect: function()
{
WebInspector.panels.databases.showDatabase(this.database);
},
oncollapse: function()
{
// Request a refresh after every collapse so the next
// expand will have an updated table list.
this.shouldRefreshChildren = true;
},
onpopulate: function()
{
this.removeChildren();
var tableNames = this.database.tableNames;
var tableNamesLength = tableNames.length;
for (var i = 0; i < tableNamesLength; ++i)
this.appendChild(new WebInspector.SidebarDatabaseTableTreeElement(this.database, tableNames[i]));
},
get mainTitle()
{
return this.database.name;
},
set mainTitle(x)
{
// Do nothing.
},
get subtitle()
{
return this.database.displayDomain;
},
set subtitle(x)
{
// Do nothing.
}
}
WebInspector.DatabaseSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype;
WebInspector.SidebarDatabaseTableTreeElement = function(database, tableName)
{
this.database = database;
this.tableName = tableName;
WebInspector.SidebarTreeElement.call(this, "database-table-sidebar-tree-item small", tableName, "", null, false);
}
WebInspector.SidebarDatabaseTableTreeElement.prototype = {
onselect: function()
{
WebInspector.panels.databases.showDatabase(this.database, this.tableName);
}
}
WebInspector.SidebarDatabaseTableTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype;
/* ProfilesPanel.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
const UserInitiatedProfileName = "org.webkit.profiles.user-initiated";
WebInspector.ProfilesPanel = function()
{
WebInspector.Panel.call(this);
this.element.addStyleClass("profiles");
this.sidebarElement = document.createElement("div");
this.sidebarElement.id = "profiles-sidebar";
this.sidebarElement.className = "sidebar";
this.element.appendChild(this.sidebarElement);
this.sidebarResizeElement = document.createElement("div");
this.sidebarResizeElement.className = "sidebar-resizer-vertical";
this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarDragging.bind(this), false);
this.element.appendChild(this.sidebarResizeElement);
this.sidebarTreeElement = document.createElement("ol");
this.sidebarTreeElement.className = "sidebar-tree";
this.sidebarElement.appendChild(this.sidebarTreeElement);
this.sidebarTree = new TreeOutline(this.sidebarTreeElement);
this.profileViews = document.createElement("div");
this.profileViews.id = "profile-views";
this.element.appendChild(this.profileViews);
this.recordButton = document.createElement("button");
this.recordButton.title = WebInspector.UIString("Start profiling.");
this.recordButton.id = "record-profile-status-bar-item";
this.recordButton.className = "status-bar-item";
this.recordButton.addEventListener("click", this._recordClicked.bind(this), false);
this.recording = false;
this.profileViewStatusBarItemsContainer = document.createElement("div");
this.profileViewStatusBarItemsContainer.id = "profile-view-status-bar-items";
this.reset();
}
WebInspector.ProfilesPanel.prototype = {
toolbarItemClass: "profiles",
get toolbarItemLabel()
{
return WebInspector.UIString("Profiles");
},
get statusBarItems()
{
return [this.recordButton, this.profileViewStatusBarItemsContainer];
},
show: function()
{
WebInspector.Panel.prototype.show.call(this);
this._updateSidebarWidth();
if (this._shouldPopulateProfiles)
this._populateProfiles();
},
populateInterface: function()
{
if (this.visible)
this._populateProfiles();
else
this._shouldPopulateProfiles = true;
},
reset: function()
{
this.nextUserInitiatedProfileNumber = 1;
this.nextUserInitiatedProfileNumberForLinks = 1;
if (this._profiles) {
var profiledLength = this._profiles.length;
for (var i = 0; i < profiledLength; ++i) {
var profile = this._profiles[i];
delete profile._profileView;
}
}
delete this.currentQuery;
this.searchCanceled();
this._profiles = [];
this._profilesIdMap = {};
this._profileGroups = {};
this._profileGroupsForLinks = {}
this.sidebarTreeElement.removeStyleClass("some-expandable");
this.sidebarTree.removeChildren();
this.profileViews.removeChildren();
this.profileViewStatusBarItemsContainer.removeChildren();
},
handleKeyEvent: function(event)
{
this.sidebarTree.handleKeyEvent(event);
},
addProfile: function(profile)
{
this._profiles.push(profile);
this._profilesIdMap[profile.uid] = profile;
var sidebarParent = this.sidebarTree;
var small = false;
var alternateTitle;
if (profile.title !== UserInitiatedProfileName) {
if (!(profile.title in this._profileGroups))
this._profileGroups[profile.title] = [];
var group = this._profileGroups[profile.title];
group.push(profile);
if (group.length === 2) {
// Make a group TreeElement now that there are 2 profiles.
group._profilesTreeElement = new WebInspector.ProfileGroupSidebarTreeElement(profile.title);
// Insert at the same index for the first profile of the group.
var index = this.sidebarTree.children.indexOf(group[0]._profilesTreeElement);
this.sidebarTree.insertChild(group._profilesTreeElement, index);
// Move the first profile to the group.
var selected = group[0]._profilesTreeElement.selected;
this.sidebarTree.removeChild(group[0]._profilesTreeElement);
group._profilesTreeElement.appendChild(group[0]._profilesTreeElement);
if (selected) {
group[0]._profilesTreeElement.select();
group[0]._profilesTreeElement.reveal();
}
group[0]._profilesTreeElement.small = true;
group[0]._profilesTreeElement.mainTitle = WebInspector.UIString("Run %d", 1);
this.sidebarTreeElement.addStyleClass("some-expandable");
}
if (group.length >= 2) {
sidebarParent = group._profilesTreeElement;
alternateTitle = WebInspector.UIString("Run %d", group.length);
small = true;
}
}
var profileTreeElement = new WebInspector.ProfileSidebarTreeElement(profile);
profileTreeElement.small = small;
if (alternateTitle)
profileTreeElement.mainTitle = alternateTitle;
profile._profilesTreeElement = profileTreeElement;
sidebarParent.appendChild(profileTreeElement);
},
showProfile: function(profile)
{
if (!profile)
return;
if (this.visibleView)
this.visibleView.hide();
var view = this.profileViewForProfile(profile);
view.show(this.profileViews);
profile._profilesTreeElement.select(true);
profile._profilesTreeElement.reveal()
this.visibleView = view;
this.profileViewStatusBarItemsContainer.removeChildren();
var statusBarItems = view.statusBarItems;
for (var i = 0; i < statusBarItems.length; ++i)
this.profileViewStatusBarItemsContainer.appendChild(statusBarItems[i]);
},
showView: function(view)
{
// Always use the treeProfile, since the heavy profile might be showing.
this.showProfile(view.profile.treeProfile);
},
profileViewForProfile: function(profile)
{
if (!profile)
return null;
if (!profile._profileView)
profile._profileView = new WebInspector.ProfileView(profile);
return profile._profileView;
},
showProfileById: function(uid)
{
this.showProfile(this._profilesIdMap[uid]);
},
closeVisibleView: function()
{
if (this.visibleView)
this.visibleView.hide();
delete this.visibleView;
},
displayTitleForProfileLink: function(title)
{
title = unescape(title);
if (title === UserInitiatedProfileName)
title = WebInspector.UIString("Profile %d", this.nextUserInitiatedProfileNumberForLinks++);
else {
if (!(title in this._profileGroupsForLinks))
this._profileGroupsForLinks[title] = 0;
groupNumber = ++this._profileGroupsForLinks[title];
if (groupNumber >= 2)
title += " " + WebInspector.UIString("Run %d", groupNumber);
}
return title;
},
get searchableViews()
{
var views = [];
const visibleView = this.visibleView;
if (visibleView && visibleView.performSearch)
views.push(visibleView);
var profilesLength = this._profiles.length;
for (var i = 0; i < profilesLength; ++i) {
var view = this.profileViewForProfile(this._profiles[i]);
if (!view.performSearch || view === visibleView)
continue;
views.push(view);
}
return views;
},
searchMatchFound: function(view, matches)
{
// Always use the treeProfile, since the heavy profile might be showing.
view.profile.treeProfile._profilesTreeElement.searchMatches = matches;
},
searchCanceled: function(startingNewSearch)
{
WebInspector.Panel.prototype.searchCanceled.call(this, startingNewSearch);
if (!this._profiles)
return;
for (var i = 0; i < this._profiles.length; ++i) {
var profile = this._profiles[i];
profile._profilesTreeElement.searchMatches = 0;
}
},
_recordClicked: function()
{
this.recording = !this.recording;
if (this.recording)
InspectorController.startProfiling();
else
InspectorController.stopProfiling();
},
setRecordingProfile: function(isProfiling)
{
this.recording = isProfiling;
if (isProfiling) {
this.recordButton.addStyleClass("toggled-on");
this.recordButton.title = WebInspector.UIString("Stop profiling.");
} else {
this.recordButton.removeStyleClass("toggled-on");
this.recordButton.title = WebInspector.UIString("Start profiling.");
}
},
_populateProfiles: function()
{
if (this.sidebarTree.children.length)
return;
var profiles = InspectorController.profiles();
var profilesLength = profiles.length;
for (var i = 0; i < profilesLength; ++i) {
var profile = profiles[i];
this.addProfile(profile);
}
if (this.sidebarTree.children[0])
this.sidebarTree.children[0].select();
delete this._shouldPopulateProfiles;
},
_startSidebarDragging: function(event)
{
WebInspector.elementDragStart(this.sidebarResizeElement, this._sidebarDragging.bind(this), this._endSidebarDragging.bind(this), event, "col-resize");
},
_sidebarDragging: function(event)
{
this._updateSidebarWidth(event.pageX);
event.preventDefault();
},
_endSidebarDragging: function(event)
{
WebInspector.elementDragEnd(event);
},
_updateSidebarWidth: function(width)
{
if (this.sidebarElement.offsetWidth <= 0) {
// The stylesheet hasn't loaded yet or the window is closed,
// so we can't calculate what is need. Return early.
return;
}
if (!("_currentSidebarWidth" in this))
this._currentSidebarWidth = this.sidebarElement.offsetWidth;
if (typeof width === "undefined")
width = this._currentSidebarWidth;
width = Number.constrain(width, Preferences.minSidebarWidth, window.innerWidth / 2);
this._currentSidebarWidth = width;
this.sidebarElement.style.width = width + "px";
this.profileViews.style.left = width + "px";
this.profileViewStatusBarItemsContainer.style.left = width + "px";
this.sidebarResizeElement.style.left = (width - 3) + "px";
}
}
WebInspector.ProfilesPanel.prototype.__proto__ = WebInspector.Panel.prototype;
WebInspector.ProfileSidebarTreeElement = function(profile)
{
this.profile = profile;
if (this.profile.title === UserInitiatedProfileName)
this._profileNumber = WebInspector.panels.profiles.nextUserInitiatedProfileNumber++;
WebInspector.SidebarTreeElement.call(this, "profile-sidebar-tree-item", "", "", profile, false);
this.refreshTitles();
}
WebInspector.ProfileSidebarTreeElement.prototype = {
onselect: function()
{
WebInspector.panels.profiles.showProfile(this.profile);
},
get mainTitle()
{
if (this._mainTitle)
return this._mainTitle;
if (this.profile.title === UserInitiatedProfileName)
return WebInspector.UIString("Profile %d", this._profileNumber);
return this.profile.title;
},
set mainTitle(x)
{
this._mainTitle = x;
this.refreshTitles();
},
get subtitle()
{
// There is no subtitle.
},
set subtitle(x)
{
// Can't change subtitle.
},
set searchMatches(matches)
{
if (!matches) {
if (!this.bubbleElement)
return;
this.bubbleElement.removeStyleClass("search-matches");
this.bubbleText = "";
return;
}
this.bubbleText = matches;
this.bubbleElement.addStyleClass("search-matches");
}
}
WebInspector.ProfileSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype;
WebInspector.ProfileGroupSidebarTreeElement = function(title, subtitle)
{
WebInspector.SidebarTreeElement.call(this, "profile-group-sidebar-tree-item", title, subtitle, null, true);
}
WebInspector.ProfileGroupSidebarTreeElement.prototype = {
onselect: function()
{
WebInspector.panels.profiles.showProfile(this.children[this.children.length - 1].profile);
}
}
WebInspector.ProfileGroupSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype;
/* ResourceView.js */
/*
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
*
* 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.
*/
WebInspector.ResourceView = function(resource)
{
WebInspector.View.call(this);
this.element.addStyleClass("resource-view");
this.resource = resource;
this.headersElement = document.createElement("div");
this.headersElement.className = "resource-view-headers";
this.element.appendChild(this.headersElement);
this.contentElement = document.createElement("div");
this.contentElement.className = "resource-view-content";
this.element.appendChild(this.contentElement);
this.headersListElement = document.createElement("ol");
this.headersListElement.className = "outline-disclosure";
this.headersElement.appendChild(this.headersListElement);
this.headersTreeOutline = new TreeOutline(this.headersListElement);
this.headersTreeOutline.expandTreeElementsWhenArrowing = true;
this.urlTreeElement = new TreeElement("", null, false);
this.urlTreeElement.selectable = false;
this.headersTreeOutline.appendChild(this.urlTreeElement);
this.requestHeadersTreeElement = new TreeElement("", null, true);
this.requestHeadersTreeElement.expanded = false;
this.requestHeadersTreeElement.selectable = false;
this.headersTreeOutline.appendChild(this.requestHeadersTreeElement);
this.responseHeadersTreeElement = new TreeElement("", null, true);
this.responseHeadersTreeElement.expanded = false;
this.responseHeadersTreeElement.selectable = false;
this.headersTreeOutline.appendChild(this.responseHeadersTreeElement);
this.headersVisible = true;
resource.addEventListener("url changed", this._refreshURL, this);
resource.addEventListener("requestHeaders changed", this._refreshRequestHeaders, this);
resource.addEventListener("responseHeaders changed", this._refreshResponseHeaders, this);
this._refreshURL();
this._refreshRequestHeaders();
this._refreshResponseHeaders();
}
WebInspector.ResourceView.prototype = {
get headersVisible()
{
return this._headersVisible;
},
set headersVisible(x)
{
if (x === this._headersVisible)
return;
this._headersVisible = x;
if (x)
this.element.addStyleClass("headers-visible");
else
this.element.removeStyleClass("headers-visible");
},
attach: function()
{
if (!this.element.parentNode) {
var parentElement = (document.getElementById("resource-views") || document.getElementById("script-resource-views"));
if (parentElement)
parentElement.appendChild(this.element);
}
},
_refreshURL: function()
{
this.urlTreeElement.title = this.resource.url.escapeHTML();
},
_refreshRequestHeaders: function()
{
this._refreshHeaders(WebInspector.UIString("Request Headers"), this.resource.sortedRequestHeaders, this.requestHeadersTreeElement);
},
_refreshResponseHeaders: function()
{
this._refreshHeaders(WebInspector.UIString("Response Headers"), this.resource.sortedResponseHeaders, this.responseHeadersTreeElement);
},
_refreshHeaders: function(title, headers, headersTreeElement)
{
headersTreeElement.removeChildren();
var length = headers.length;
headersTreeElement.title = title.escapeHTML() + "<span class=\"header-count\">" + WebInspector.UIString(" (%d)", length) + "</span>";
headersTreeElement.hidden = !length;
var length = headers.length;
for (var i = 0; i < length; ++i) {
var title = "<div class=\"header-name\">" + headers[i].header.escapeHTML() + ":</div>";
title += "<div class=\"header-value\">" + headers[i].value.escapeHTML() + "</div>"
var headerTreeElement = new TreeElement(title, null, false);
headerTreeElement.selectable = false;
headersTreeElement.appendChild(headerTreeElement);
}
}
}
WebInspector.ResourceView.prototype.__proto__ = WebInspector.View.prototype;
/* SourceFrame.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
WebInspector.SourceFrame = function(element, addBreakpointDelegate)
{
this.messages = [];
this.breakpoints = [];
this.addBreakpointDelegate = addBreakpointDelegate;
this.element = element || document.createElement("iframe");
this.element.addStyleClass("source-view-frame");
this.element.setAttribute("viewsource", "true");
this.element.addEventListener("load", this._loaded.bind(this), false);
}
WebInspector.SourceFrame.prototype = {
get executionLine()
{
return this._executionLine;
},
set executionLine(x)
{
if (this._executionLine === x)
return;
var previousLine = this._executionLine;
this._executionLine = x;
this._updateExecutionLine(previousLine);
},
get autoSizesToFitContentHeight()
{
return this._autoSizesToFitContentHeight;
},
set autoSizesToFitContentHeight(x)
{
if (this._autoSizesToFitContentHeight === x)
return;
this._autoSizesToFitContentHeight = x;
if (this._autoSizesToFitContentHeight) {
this._windowResizeListener = this._windowResized.bind(this);
window.addEventListener("resize", this._windowResizeListener, false);
this.sizeToFitContentHeight();
} else {
this.element.style.removeProperty("height");
if (this.element.contentDocument)
this.element.contentDocument.body.removeStyleClass("webkit-height-sized-to-fit");
window.removeEventListener("resize", this._windowResizeListener, false);
delete this._windowResizeListener;
}
},
sourceRow: function(lineNumber)
{
if (!lineNumber || !this.element.contentDocument)
return;
var table = this.element.contentDocument.getElementsByTagName("table")[0];
if (!table)
return;
var rows = table.rows;
// Line numbers are a 1-based index, but the rows collection is 0-based.
--lineNumber;
if (lineNumber >= rows.length)
lineNumber = rows.length - 1;
return rows[lineNumber];
},
lineNumberForSourceRow: function(sourceRow)
{
// Line numbers are a 1-based index, but the rows collection is 0-based.
var lineNumber = 0;
while (sourceRow) {
++lineNumber;
sourceRow = sourceRow.previousSibling;
}
return lineNumber;
},
revealLine: function(lineNumber)
{
var row = this.sourceRow(lineNumber);
if (row)
row.scrollIntoViewIfNeeded(true);
},
addBreakpoint: function(breakpoint)
{
this.breakpoints.push(breakpoint);
breakpoint.addEventListener("enabled", this._breakpointEnableChanged, this);
breakpoint.addEventListener("disabled", this._breakpointEnableChanged, this);
this._addBreakpointToSource(breakpoint);
},
removeBreakpoint: function(breakpoint)
{
this.breakpoints.remove(breakpoint);
breakpoint.removeEventListener("enabled", null, this);
breakpoint.removeEventListener("disabled", null, this);
this._removeBreakpointFromSource(breakpoint);
},
addMessage: function(msg)
{
// Don't add the message if there is no message or valid line or if the msg isn't an error or warning.
if (!msg.message || msg.line <= 0 || !msg.isErrorOrWarning())
return;
this.messages.push(msg);
this._addMessageToSource(msg);
},
clearMessages: function()
{
this.messages = [];
if (!this.element.contentDocument)
return;
var bubbles = this.element.contentDocument.querySelectorAll(".webkit-html-message-bubble");
if (!bubbles)
return;
for (var i = 0; i < bubbles.length; ++i) {
var bubble = bubbles[i];
bubble.parentNode.removeChild(bubble);
}
},
sizeToFitContentHeight: function()
{
if (this.element.contentDocument) {
this.element.style.setProperty("height", this.element.contentDocument.body.offsetHeight + "px");
this.element.contentDocument.body.addStyleClass("webkit-height-sized-to-fit");
}
},
_highlightLineEnds: function(event)
{
event.target.parentNode.removeStyleClass("webkit-highlighted-line");
},
highlightLine: function(lineNumber)
{
var sourceRow = this.sourceRow(lineNumber);
if (!sourceRow)
return;
var line = sourceRow.getElementsByClassName('webkit-line-content')[0];
// Trick to reset the animation if the user clicks on the same link
// Using a timeout to avoid coalesced style updates
line.style.setProperty("-webkit-animation-name", "none");
setTimeout(function () {
line.style.removeProperty("-webkit-animation-name");
sourceRow.addStyleClass("webkit-highlighted-line");
}, 0);
},
_loaded: function()
{
WebInspector.addMainEventListeners(this.element.contentDocument);
this.element.contentDocument.addEventListener("mousedown", this._documentMouseDown.bind(this), true);
this.element.contentDocument.addEventListener("webkitAnimationEnd", this._highlightLineEnds.bind(this), false);
var headElement = this.element.contentDocument.getElementsByTagName("head")[0];
if (!headElement) {
headElement = this.element.contentDocument.createElement("head");
this.element.contentDocument.documentElement.insertBefore(headElement, this.element.contentDocument.documentElement.firstChild);
}
var styleElement = this.element.contentDocument.createElement("style");
headElement.appendChild(styleElement);
// Add these style rules here since they are specific to the Inspector. They also behave oddly and not
// all properties apply if added to view-source.css (becuase it is a user agent sheet.)
var styleText = ".webkit-line-number { background-repeat: no-repeat; background-position: right 1px; }\n";
styleText += ".webkit-breakpoint .webkit-line-number { color: white; background-image: -webkit-canvas(breakpoint); }\n";
styleText += ".webkit-breakpoint-disabled .webkit-line-number { color: white; background-image: -webkit-canvas(breakpoint-disabled); }\n";
styleText += ".webkit-execution-line .webkit-line-number { color: transparent; background-image: -webkit-canvas(program-counter); }\n";
styleText += ".webkit-breakpoint.webkit-execution-line .webkit-line-number { color: transparent; background-image: -webkit-canvas(breakpoint-program-counter); }\n";
styleText += ".webkit-breakpoint-disabled.webkit-execution-line .webkit-line-number { color: transparent; background-image: -webkit-canvas(breakpoint-disabled-program-counter); }\n";
styleText += ".webkit-execution-line .webkit-line-content { background-color: rgb(171, 191, 254); outline: 1px solid rgb(64, 115, 244); }\n";
styleText += ".webkit-height-sized-to-fit { overflow-y: hidden }\n";
styleText += ".webkit-line-content { background-color: white; }\n";
styleText += "@-webkit-keyframes fadeout {from {background-color: rgb(255, 255, 120);} to { background-color: white;}}\n";
styleText += ".webkit-highlighted-line .webkit-line-content { background-color: rgb(255, 255, 120); -webkit-animation: 'fadeout' 2s 500ms}\n";
styleText += ".webkit-javascript-comment { color: rgb(0, 116, 0); }\n";
styleText += ".webkit-javascript-keyword { color: rgb(170, 13, 145); }\n";
styleText += ".webkit-javascript-number { color: rgb(28, 0, 207); }\n";
styleText += ".webkit-javascript-string, .webkit-javascript-regexp { color: rgb(196, 26, 22); }\n";
styleElement.textContent = styleText;
this._needsProgramCounterImage = true;
this._needsBreakpointImages = true;
this.element.contentWindow.Element.prototype.addStyleClass = Element.prototype.addStyleClass;
this.element.contentWindow.Element.prototype.removeStyleClass = Element.prototype.removeStyleClass;
this.element.contentWindow.Element.prototype.hasStyleClass = Element.prototype.hasStyleClass;
this.element.contentWindow.Node.prototype.enclosingNodeOrSelfWithNodeName = Node.prototype.enclosingNodeOrSelfWithNodeName;
this._addExistingMessagesToSource();
this._addExistingBreakpointsToSource();
this._updateExecutionLine();
if (this.autoSizesToFitContentHeight)
this.sizeToFitContentHeight();
},
_windowResized: function(event)
{
if (!this._autoSizesToFitContentHeight)
return;
this.sizeToFitContentHeight();
},
_documentMouseDown: function(event)
{
if (!event.target.hasStyleClass("webkit-line-number"))
return;
var sourceRow = event.target.enclosingNodeOrSelfWithNodeName("tr");
if (sourceRow._breakpointObject)
sourceRow._breakpointObject.enabled = !sourceRow._breakpointObject.enabled;
else if (this.addBreakpointDelegate)
this.addBreakpointDelegate(this.lineNumberForSourceRow(sourceRow));
},
_breakpointEnableChanged: function(event)
{
var breakpoint = event.target;
var sourceRow = this.sourceRow(breakpoint.line);
if (!sourceRow)
return;
sourceRow.addStyleClass("webkit-breakpoint");
if (breakpoint.enabled)
sourceRow.removeStyleClass("webkit-breakpoint-disabled");
else
sourceRow.addStyleClass("webkit-breakpoint-disabled");
},
_updateExecutionLine: function(previousLine)
{
if (previousLine) {
var sourceRow = this.sourceRow(previousLine);
if (sourceRow)
sourceRow.removeStyleClass("webkit-execution-line");
}
if (!this._executionLine)
return;
this._drawProgramCounterImageIfNeeded();
var sourceRow = this.sourceRow(this._executionLine);
if (sourceRow)
sourceRow.addStyleClass("webkit-execution-line");
},
_addExistingBreakpointsToSource: function()
{
var length = this.breakpoints.length;
for (var i = 0; i < length; ++i)
this._addBreakpointToSource(this.breakpoints[i]);
},
_addBreakpointToSource: function(breakpoint)
{
var sourceRow = this.sourceRow(breakpoint.line);
if (!sourceRow)
return;
this._drawBreakpointImagesIfNeeded();
sourceRow._breakpointObject = breakpoint;
sourceRow.addStyleClass("webkit-breakpoint");
if (!breakpoint.enabled)
sourceRow.addStyleClass("webkit-breakpoint-disabled");
},
_removeBreakpointFromSource: function(breakpoint)
{
var sourceRow = this.sourceRow(breakpoint.line);
if (!sourceRow)
return;
delete sourceRow._breakpointObject;
sourceRow.removeStyleClass("webkit-breakpoint");
sourceRow.removeStyleClass("webkit-breakpoint-disabled");
},
_incrementMessageRepeatCount: function(msg, repeatDelta)
{
if (!msg._resourceMessageLineElement)
return;
if (!msg._resourceMessageRepeatCountElement) {
var repeatedElement = document.createElement("span");
msg._resourceMessageLineElement.appendChild(repeatedElement);
msg._resourceMessageRepeatCountElement = repeatedElement;
}
msg.repeatCount += repeatDelta;
msg._resourceMessageRepeatCountElement.textContent = WebInspector.UIString(" (repeated %d times)", msg.repeatCount);
},
_addExistingMessagesToSource: function()
{
var length = this.messages.length;
for (var i = 0; i < length; ++i)
this._addMessageToSource(this.messages[i]);
},
_addMessageToSource: function(msg)
{
var row = this.sourceRow(msg.line);
if (!row)
return;
var cell = row.cells[1];
if (!cell)
return;
var messageBubbleElement = cell.lastChild;
if (!messageBubbleElement || messageBubbleElement.nodeType !== Node.ELEMENT_NODE || !messageBubbleElement.hasStyleClass("webkit-html-message-bubble")) {
messageBubbleElement = this.element.contentDocument.createElement("div");
messageBubbleElement.className = "webkit-html-message-bubble";
cell.appendChild(messageBubbleElement);
}
if (!row.messages)
row.messages = [];
for (var i = 0; i < row.messages.length; ++i) {
if (row.messages[i].isEqual(msg, true)) {
this._incrementMessageRepeatCount(row.messages[i], msg.repeatDelta);
return;
}
}
row.messages.push(msg);
var imageURL;
switch (msg.level) {
case WebInspector.ConsoleMessage.MessageLevel.Error:
messageBubbleElement.addStyleClass("webkit-html-error-message");
imageURL = "Images/errorIcon.png";
break;
case WebInspector.ConsoleMessage.MessageLevel.Warning:
messageBubbleElement.addStyleClass("webkit-html-warning-message");
imageURL = "Images/warningIcon.png";
break;
}
var messageLineElement = this.element.contentDocument.createElement("div");
messageLineElement.className = "webkit-html-message-line";
messageBubbleElement.appendChild(messageLineElement);
// Create the image element in the Inspector's document so we can use relative image URLs.
var image = document.createElement("img");
image.src = imageURL;
image.className = "webkit-html-message-icon";
// Adopt the image element since it wasn't created in element's contentDocument.
image = this.element.contentDocument.adoptNode(image);
messageLineElement.appendChild(image);
messageLineElement.appendChild(this.element.contentDocument.createTextNode(msg.message));
msg._resourceMessageLineElement = messageLineElement;
},
_drawProgramCounterInContext: function(ctx, glow)
{
if (glow)
ctx.save();
ctx.beginPath();
ctx.moveTo(17, 2);
ctx.lineTo(19, 2);
ctx.lineTo(19, 0);
ctx.lineTo(21, 0);
ctx.lineTo(26, 5.5);
ctx.lineTo(21, 11);
ctx.lineTo(19, 11);
ctx.lineTo(19, 9);
ctx.lineTo(17, 9);
ctx.closePath();
ctx.fillStyle = "rgb(142, 5, 4)";
if (glow) {
ctx.shadowBlur = 4;
ctx.shadowColor = "rgb(255, 255, 255)";
ctx.shadowOffsetX = -1;
ctx.shadowOffsetY = 0;
}
ctx.fill();
ctx.fill(); // Fill twice to get a good shadow and darker anti-aliased pixels.
if (glow)
ctx.restore();
},
_drawProgramCounterImageIfNeeded: function()
{
if (!this._needsProgramCounterImage || !this.element.contentDocument)
return;
var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "program-counter", 26, 11);
ctx.clearRect(0, 0, 26, 11);
this._drawProgramCounterInContext(ctx, true);
delete this._needsProgramCounterImage;
},
_drawBreakpointImagesIfNeeded: function()
{
if (!this._needsBreakpointImages || !this.element.contentDocument)
return;
function drawBreakpoint(ctx, disabled)
{
ctx.beginPath();
ctx.moveTo(0, 2);
ctx.lineTo(2, 0);
ctx.lineTo(21, 0);
ctx.lineTo(26, 5.5);
ctx.lineTo(21, 11);
ctx.lineTo(2, 11);
ctx.lineTo(0, 9);
ctx.closePath();
ctx.fillStyle = "rgb(1, 142, 217)";
ctx.strokeStyle = "rgb(0, 103, 205)";
ctx.lineWidth = 3;
ctx.fill();
ctx.save();
ctx.clip();
ctx.stroke();
ctx.restore();
if (!disabled)
return;
ctx.save();
ctx.globalCompositeOperation = "destination-out";
ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
ctx.fillRect(0, 0, 26, 11);
ctx.restore();
}
var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "breakpoint", 26, 11);
ctx.clearRect(0, 0, 26, 11);
drawBreakpoint(ctx);
var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "breakpoint-program-counter", 26, 11);
ctx.clearRect(0, 0, 26, 11);
drawBreakpoint(ctx);
ctx.clearRect(20, 0, 6, 11);
this._drawProgramCounterInContext(ctx, true);
var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "breakpoint-disabled", 26, 11);
ctx.clearRect(0, 0, 26, 11);
drawBreakpoint(ctx, true);
var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "breakpoint-disabled-program-counter", 26, 11);
ctx.clearRect(0, 0, 26, 11);
drawBreakpoint(ctx, true);
ctx.clearRect(20, 0, 6, 11);
this._drawProgramCounterInContext(ctx, true);
delete this._needsBreakpointImages;
},
syntaxHighlightJavascript: function()
{
var table = this.element.contentDocument.getElementsByTagName("table")[0];
if (!table)
return;
function deleteContinueFlags(cell)
{
if (!cell)
return;
delete cell._commentContinues;
delete cell._singleQuoteStringContinues;
delete cell._doubleQuoteStringContinues;
delete cell._regexpContinues;
}
function createSpan(content, className)
{
var span = document.createElement("span");
span.className = className;
span.appendChild(document.createTextNode(content));
return span;
}
function generateFinder(regex, matchNumber, className)
{
return function(str) {
var match = regex.exec(str);
if (!match)
return null;
previousMatchLength = match[matchNumber].length;
return createSpan(match[matchNumber], className);
};
}
var findNumber = generateFinder(/^(-?(\d+\.?\d*([eE][+-]\d+)?|0[xX]\h+|Infinity)|NaN)(?:\W|$)/, 1, "webkit-javascript-number");
var findKeyword = generateFinder(/^(null|true|false|break|case|catch|const|default|finally|for|instanceof|new|var|continue|function|return|void|delete|if|this|do|while|else|in|switch|throw|try|typeof|with|debugger|class|enum|export|extends|import|super|get|set)(?:\W|$)/, 1, "webkit-javascript-keyword");
var findSingleLineString = generateFinder(/^"(?:[^"\\]|\\.)*"|^'([^'\\]|\\.)*'/, 0, "webkit-javascript-string"); // " this quote keeps Xcode happy
var findMultilineCommentStart = generateFinder(/^\/\*.*$/, 0, "webkit-javascript-comment");
var findMultilineCommentEnd = generateFinder(/^.*?\*\//, 0, "webkit-javascript-comment");
var findMultilineSingleQuoteStringStart = generateFinder(/^'(?:[^'\\]|\\.)*\\$/, 0, "webkit-javascript-string");
var findMultilineSingleQuoteStringEnd = generateFinder(/^(?:[^'\\]|\\.)*?'/, 0, "webkit-javascript-string");
var findMultilineDoubleQuoteStringStart = generateFinder(/^"(?:[^"\\]|\\.)*\\$/, 0, "webkit-javascript-string");
var findMultilineDoubleQuoteStringEnd = generateFinder(/^(?:[^"\\]|\\.)*?"/, 0, "webkit-javascript-string");
var findMultilineRegExpEnd = generateFinder(/^(?:[^\/\\]|\\.)*?\/([gim]{0,3})/, 0, "webkit-javascript-regexp");
var findSingleLineComment = generateFinder(/^\/\/.*|^\/\*.*?\*\//, 0, "webkit-javascript-comment");
function findMultilineRegExpStart(str)
{
var match = /^\/(?:[^\/\\]|\\.)*\\$/.exec(str);
if (!match || !/\\|\$|\.[\?\*\+]|[^\|]\|[^\|]/.test(match[0]))
return null;
var node = createSpan(match[0], "webkit-javascript-regexp");
previousMatchLength = match[0].length;
return node;
}
function findSingleLineRegExp(str)
{
var match = /^(\/(?:[^\/\\]|\\.)*\/([gim]{0,3}))(.?)/.exec(str);
if (!match || !(match[2].length > 0 || /\\|\$|\.[\?\*\+]|[^\|]\|[^\|]/.test(match[1]) || /\.|;|,/.test(match[3])))
return null;
var node = createSpan(match[1], "webkit-javascript-regexp");
previousMatchLength = match[1].length;
return node;
}
function syntaxHighlightJavascriptLine(line, prevLine)
{
var messageBubble = line.lastChild;
if (messageBubble && messageBubble.nodeType === Node.ELEMENT_NODE && messageBubble.hasStyleClass("webkit-html-message-bubble"))
line.removeChild(messageBubble);
else
messageBubble = null;
var code = line.textContent;
while (line.firstChild)
line.removeChild(line.firstChild);
var token;
var tmp = 0;
var i = 0;
previousMatchLength = 0;
if (prevLine) {
if (prevLine._commentContinues) {
if (!(token = findMultilineCommentEnd(code))) {
token = createSpan(code, "webkit-javascript-comment");
line._commentContinues = true;
}
} else if (prevLine._singleQuoteStringContinues) {
if (!(token = findMultilineSingleQuoteStringEnd(code))) {
token = createSpan(code, "webkit-javascript-string");
line._singleQuoteStringContinues = true;
}
} else if (prevLine._doubleQuoteStringContinues) {
if (!(token = findMultilineDoubleQuoteStringEnd(code))) {
token = createSpan(code, "webkit-javascript-string");
line._doubleQuoteStringContinues = true;
}
} else if (prevLine._regexpContinues) {
if (!(token = findMultilineRegExpEnd(code))) {
token = createSpan(code, "webkit-javascript-regexp");
line._regexpContinues = true;
}
}
if (token) {
i += previousMatchLength ? previousMatchLength : code.length;
tmp = i;
line.appendChild(token);
}
}
for ( ; i < code.length; ++i) {
var codeFragment = code.substr(i);
var prevChar = code[i - 1];
token = findSingleLineComment(codeFragment);
if (!token) {
if ((token = findMultilineCommentStart(codeFragment)))
line._commentContinues = true;
else if (!prevChar || /^\W/.test(prevChar)) {
token = findNumber(codeFragment, code[i - 1]) ||
findKeyword(codeFragment, code[i - 1]) ||
findSingleLineString(codeFragment) ||
findSingleLineRegExp(codeFragment);
if (!token) {
if (token = findMultilineSingleQuoteStringStart(codeFragment))
line._singleQuoteStringContinues = true;
else if (token = findMultilineDoubleQuoteStringStart(codeFragment))
line._doubleQuoteStringContinues = true;
else if (token = findMultilineRegExpStart(codeFragment))
line._regexpContinues = true;
}
}
}
if (token) {
if (tmp !== i)
line.appendChild(document.createTextNode(code.substring(tmp, i)));
line.appendChild(token);
i += previousMatchLength - 1;
tmp = i + 1;
}
}
if (tmp < code.length)
line.appendChild(document.createTextNode(code.substring(tmp, i)));
if (messageBubble)
line.appendChild(messageBubble);
}
var i = 0;
var rows = table.rows;
var rowsLength = rows.length;
var previousCell = null;
var previousMatchLength = 0;
var sourceFrame = this;
// Split up the work into chunks so we don't block the
// UI thread while processing.
function processChunk()
{
for (var end = Math.min(i + 10, rowsLength); i < end; ++i) {
var row = rows[i];
if (!row)
continue;
var cell = row.cells[1];
if (!cell)
continue;
syntaxHighlightJavascriptLine(cell, previousCell);
if (i < (end - 1))
deleteContinueFlags(previousCell);
previousCell = cell;
}
if (i >= rowsLength && processChunkInterval) {
deleteContinueFlags(previousCell);
clearInterval(processChunkInterval);
sourceFrame.dispatchEventToListeners("syntax highlighting complete");
}
}
processChunk();
var processChunkInterval = setInterval(processChunk, 25);
}
}
WebInspector.SourceFrame.prototype.__proto__ = WebInspector.Object.prototype;
/* SourceView.js */
/*
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
*
* 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.
*/
WebInspector.SourceView = function(resource)
{
// Set the sourceFrame first since WebInspector.ResourceView will set headersVisible
// and our override of headersVisible needs the sourceFrame.
this.sourceFrame = new WebInspector.SourceFrame(null, this._addBreakpoint.bind(this));
WebInspector.ResourceView.call(this, resource);
resource.addEventListener("finished", this._resourceLoadingFinished, this);
this.element.addStyleClass("source");
this._frameNeedsSetup = true;
this.contentElement.appendChild(this.sourceFrame.element);
var gutterElement = document.createElement("div");
gutterElement.className = "webkit-line-gutter-backdrop";
this.element.appendChild(gutterElement);
}
WebInspector.SourceView.prototype = {
set headersVisible(x)
{
if (x === this._headersVisible)
return;
var superSetter = WebInspector.ResourceView.prototype.__lookupSetter__("headersVisible");
if (superSetter)
superSetter.call(this, x);
this.sourceFrame.autoSizesToFitContentHeight = x;
},
show: function(parentElement)
{
WebInspector.ResourceView.prototype.show.call(this, parentElement);
this.setupSourceFrameIfNeeded();
},
hide: function()
{
WebInspector.View.prototype.hide.call(this);
this._currentSearchResultIndex = -1;
},
resize: function()
{
if (this.sourceFrame.autoSizesToFitContentHeight)
this.sourceFrame.sizeToFitContentHeight();
},
detach: function()
{
// FIXME: We need to mark the frame for setup on detach because the frame DOM is cleared
// when it is removed from the document. Is this a bug?
WebInspector.ResourceView.prototype.detach.call(this);
this._frameNeedsSetup = true;
this._sourceFrameSetup = false;
},
setupSourceFrameIfNeeded: function()
{
if (this.resource.finished && !this.resource.failed && this._frameNeedsSetup) {
delete this._frameNeedsSetup;
this.attach();
InspectorController.addResourceSourceToFrame(this.resource.identifier, this.sourceFrame.element);
if (this.resource.type === WebInspector.Resource.Type.Script) {
this.sourceFrame.addEventListener("syntax highlighting complete", this._syntaxHighlightingComplete, this);
this.sourceFrame.syntaxHighlightJavascript();
} else
this._sourceFrameSetupFinished();
}
},
_resourceLoadingFinished: function(event)
{
if (this.visible)
this.setupSourceFrameIfNeeded();
this.resource.removeEventListener("finished", this._resourceLoadingFinished, this);
},
_addBreakpoint: function(line)
{
var sourceID = null;
var closestStartingLine = 0;
var scripts = this.resource.scripts;
for (var i = 0; i < scripts.length; ++i) {
var script = scripts[i];
if (script.startingLine <= line && script.startingLine >= closestStartingLine) {
closestStartingLine = script.startingLine;
sourceID = script.sourceID;
}
}
var breakpoint = new WebInspector.Breakpoint(this.resource.url, line, sourceID);
WebInspector.panels.scripts.addBreakpoint(breakpoint);
},
// The rest of the methods in this prototype need to be generic enough to work with a ScriptView.
// The ScriptView prototype pulls these methods into it's prototype to avoid duplicate code.
searchCanceled: function()
{
this._currentSearchResultIndex = -1;
this._searchResults = [];
delete this._delayedFindSearchMatches;
},
performSearch: function(query, finishedCallback)
{
// Call searchCanceled since it will reset everything we need before doing a new search.
this.searchCanceled();
this._searchFinishedCallback = finishedCallback;
function findSearchMatches(query, finishedCallback)
{
this._searchResults = InspectorController.search(this.sourceFrame.element.contentDocument, query);
if (this._searchResults)
finishedCallback(this, this._searchResults.length);
}
if (!this._sourceFrameSetup) {
// The search is performed in _sourceFrameSetupFinished by calling _delayedFindSearchMatches.
this._delayedFindSearchMatches = findSearchMatches.bind(this, query, finishedCallback);
this.setupSourceFrameIfNeeded();
return;
}
findSearchMatches.call(this, query, finishedCallback);
},
jumpToFirstSearchResult: function()
{
if (!this._searchResults || !this._searchResults.length)
return;
this._currentSearchResultIndex = 0;
this._jumpToSearchResult(this._currentSearchResultIndex);
},
jumpToLastSearchResult: function()
{
if (!this._searchResults || !this._searchResults.length)
return;
this._currentSearchResultIndex = (this._searchResults.length - 1);
this._jumpToSearchResult(this._currentSearchResultIndex);
},
jumpToNextSearchResult: function()
{
if (!this._searchResults || !this._searchResults.length)
return;
if (++this._currentSearchResultIndex >= this._searchResults.length)
this._currentSearchResultIndex = 0;
this._jumpToSearchResult(this._currentSearchResultIndex);
},
jumpToPreviousSearchResult: function()
{
if (!this._searchResults || !this._searchResults.length)
return;
if (--this._currentSearchResultIndex < 0)
this._currentSearchResultIndex = (this._searchResults.length - 1);
this._jumpToSearchResult(this._currentSearchResultIndex);
},
showingFirstSearchResult: function()
{
return (this._currentSearchResultIndex === 0);
},
showingLastSearchResult: function()
{
return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1));
},
revealLine: function(lineNumber)
{
this.setupSourceFrameIfNeeded();
this.sourceFrame.revealLine(lineNumber);
},
highlightLine: function(lineNumber)
{
this.setupSourceFrameIfNeeded();
this.sourceFrame.highlightLine(lineNumber);
},
addMessage: function(msg)
{
this.sourceFrame.addMessage(msg);
},
clearMessages: function()
{
this.sourceFrame.clearMessages();
},
_jumpToSearchResult: function(index)
{
var foundRange = this._searchResults[index];
if (!foundRange)
return;
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(foundRange);
if (foundRange.startContainer.scrollIntoViewIfNeeded)
foundRange.startContainer.scrollIntoViewIfNeeded(true);
else if (foundRange.startContainer.parentNode)
foundRange.startContainer.parentNode.scrollIntoViewIfNeeded(true);
},
_sourceFrameSetupFinished: function()
{
this._sourceFrameSetup = true;
if (this._delayedFindSearchMatches) {
this._delayedFindSearchMatches();
delete this._delayedFindSearchMatches;
}
},
_syntaxHighlightingComplete: function(event)
{
this._sourceFrameSetupFinished();
this.sourceFrame.removeEventListener("syntax highlighting complete", null, this);
}
}
WebInspector.SourceView.prototype.__proto__ = WebInspector.ResourceView.prototype;
/* FontView.js */
/*
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
*
* 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.
*/
WebInspector.FontView = function(resource)
{
WebInspector.ResourceView.call(this, resource);
this.element.addStyleClass("font");
var uniqueFontName = "WebInspectorFontPreview" + this.resource.identifier;
this.fontStyleElement = document.createElement("style");
this.fontStyleElement.textContent = "@font-face { font-family: \"" + uniqueFontName + "\"; src: url(" + this.resource.url + "); }";
document.getElementsByTagName("head").item(0).appendChild(this.fontStyleElement);
this.fontPreviewElement = document.createElement("div");
this.fontPreviewElement.className = "preview";
this.contentElement.appendChild(this.fontPreviewElement);
this.fontPreviewElement.style.setProperty("font-family", uniqueFontName, null);
this.fontPreviewElement.innerHTML = "ABCDEFGHIJKLM<br>NOPQRSTUVWXYZ<br>abcdefghijklm<br>nopqrstuvwxyz<br>1234567890";
this.updateFontPreviewSize();
}
WebInspector.FontView.prototype = {
show: function(parentElement)
{
WebInspector.ResourceView.prototype.show.call(this, parentElement);
this.updateFontPreviewSize();
},
resize: function()
{
this.updateFontPreviewSize();
},
updateFontPreviewSize: function ()
{
if (!this.fontPreviewElement || !this.visible)
return;
this.fontPreviewElement.removeStyleClass("preview");
var measureFontSize = 50;
this.fontPreviewElement.style.setProperty("position", "absolute", null);
this.fontPreviewElement.style.setProperty("font-size", measureFontSize + "px", null);
this.fontPreviewElement.style.removeProperty("height");
var height = this.fontPreviewElement.offsetHeight;
var width = this.fontPreviewElement.offsetWidth;
var containerWidth = this.contentElement.offsetWidth;
// Subtract some padding. This should match the padding in the CSS plus room for the scrollbar.
containerWidth -= 40;
if (!height || !width || !containerWidth) {
this.fontPreviewElement.style.removeProperty("font-size");
this.fontPreviewElement.style.removeProperty("position");
this.fontPreviewElement.addStyleClass("preview");
return;
}
var lineCount = this.fontPreviewElement.getElementsByTagName("br").length + 1;
var realLineHeight = Math.floor(height / lineCount);
var fontSizeLineRatio = measureFontSize / realLineHeight;
var widthRatio = containerWidth / width;
var finalFontSize = Math.floor(realLineHeight * widthRatio * fontSizeLineRatio) - 1;
this.fontPreviewElement.style.setProperty("font-size", finalFontSize + "px", null);
this.fontPreviewElement.style.setProperty("height", this.fontPreviewElement.offsetHeight + "px", null);
this.fontPreviewElement.style.removeProperty("position");
this.fontPreviewElement.addStyleClass("preview");
}
}
WebInspector.FontView.prototype.__proto__ = WebInspector.ResourceView.prototype;
/* ImageView.js */
/*
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
*
* 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.
*/
WebInspector.ImageView = function(resource)
{
WebInspector.ResourceView.call(this, resource);
this.element.addStyleClass("image");
var container = document.createElement("div");
container.className = "image";
this.contentElement.appendChild(container);
this.imagePreviewElement = document.createElement("img");
this.imagePreviewElement.setAttribute("src", this.resource.url);
container.appendChild(this.imagePreviewElement);
container = document.createElement("div");
container.className = "info";
this.contentElement.appendChild(container);
var imageNameElement = document.createElement("h1");
imageNameElement.className = "title";
imageNameElement.textContent = this.resource.displayName;
container.appendChild(imageNameElement);
var infoListElement = document.createElement("dl");
infoListElement.className = "infoList";
var imageProperties = [
{ name: WebInspector.UIString("Dimensions"), value: WebInspector.UIString("%d × %d", this.imagePreviewElement.naturalWidth, this.imagePreviewElement.height) },
{ name: WebInspector.UIString("File size"), value: Number.bytesToString(this.resource.contentLength, WebInspector.UIString.bind(WebInspector)) },
{ name: WebInspector.UIString("MIME type"), value: this.resource.mimeType }
];
var listHTML = '';
for (var i = 0; i < imageProperties.length; ++i)
listHTML += "<dt>" + imageProperties[i].name + "</dt><dd>" + imageProperties[i].value + "</dd>";
infoListElement.innerHTML = listHTML;
container.appendChild(infoListElement);
}
WebInspector.ImageView.prototype = {
}
WebInspector.ImageView.prototype.__proto__ = WebInspector.ResourceView.prototype;
/* DatabaseTableView.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
WebInspector.DatabaseTableView = function(database, tableName)
{
WebInspector.View.call(this);
this.database = database;
this.tableName = tableName;
this.element.addStyleClass("database-view");
this.element.addStyleClass("table");
}
WebInspector.DatabaseTableView.prototype = {
show: function(parentElement)
{
WebInspector.View.prototype.show.call(this, parentElement);
this.update();
},
update: function()
{
function queryTransaction(tx)
{
tx.executeSql("SELECT * FROM " + this.tableName, null, InspectorController.wrapCallback(this._queryFinished.bind(this)), InspectorController.wrapCallback(this._queryError.bind(this)));
}
this.database.database.transaction(InspectorController.wrapCallback(queryTransaction.bind(this)), InspectorController.wrapCallback(this._queryError.bind(this)));
},
_queryFinished: function(tx, result)
{
this.element.removeChildren();
var dataGrid = WebInspector.panels.databases.dataGridForResult(result);
if (!dataGrid) {
var emptyMsgElement = document.createElement("div");
emptyMsgElement.className = "database-table-empty";
emptyMsgElement.textContent = WebInspector.UIString("The “%s”\ntable is empty.", this.tableName);
this.element.appendChild(emptyMsgElement);
return;
}
this.element.appendChild(dataGrid.element);
},
_queryError: function(tx, error)
{
this.element.removeChildren();
var errorMsgElement = document.createElement("div");
errorMsgElement.className = "database-table-error";
errorMsgElement.textContent = WebInspector.UIString("An error occurred trying to\nread the “%s” table.", this.tableName);
this.element.appendChild(errorMsgElement);
},
}
WebInspector.DatabaseTableView.prototype.__proto__ = WebInspector.View.prototype;
/* DatabaseQueryView.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
WebInspector.DatabaseQueryView = function(database)
{
WebInspector.View.call(this);
this.database = database;
this.element.addStyleClass("database-view");
this.element.addStyleClass("query");
this.element.tabIndex = 0;
this.element.addEventListener("selectstart", this._selectStart.bind(this), false);
this.promptElement = document.createElement("div");
this.promptElement.className = "database-query-prompt";
this.promptElement.appendChild(document.createElement("br"));
this.promptElement.handleKeyEvent = this._promptKeyDown.bind(this);
this.element.appendChild(this.promptElement);
this.prompt = new WebInspector.TextPrompt(this.promptElement, this.completions.bind(this), " ");
}
WebInspector.DatabaseQueryView.prototype = {
show: function(parentElement)
{
WebInspector.View.prototype.show.call(this, parentElement);
function moveBackIfOutside()
{
if (!this.prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed)
this.prompt.moveCaretToEndOfPrompt();
}
setTimeout(moveBackIfOutside.bind(this), 0);
},
completions: function(wordRange, bestMatchOnly)
{
var prefix = wordRange.toString().toLowerCase();
if (!prefix.length)
return;
var results = [];
function accumulateMatches(textArray)
{
if (bestMatchOnly && results.length)
return;
for (var i = 0; i < textArray.length; ++i) {
var text = textArray[i].toLowerCase();
if (text.length < prefix.length)
continue;
if (text.indexOf(prefix) !== 0)
continue;
results.push(textArray[i]);
if (bestMatchOnly)
return;
}
}
accumulateMatches(this.database.tableNames.map(function(name) { return name + " " }));
accumulateMatches(["SELECT ", "FROM ", "WHERE ", "LIMIT ", "DELETE FROM ", "CREATE ", "DROP ", "TABLE ", "INDEX ", "UPDATE ", "INSERT INTO ", "VALUES ("]);
return results;
},
_promptKeyDown: function(event)
{
switch (event.keyIdentifier) {
case "Enter":
this._enterKeyPressed(event);
return;
}
this.prompt.handleKeyEvent(event);
},
_selectStart: function(event)
{
if (this._selectionTimeout)
clearTimeout(this._selectionTimeout);
this.prompt.clearAutoComplete();
function moveBackIfOutside()
{
delete this._selectionTimeout;
if (!this.prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed)
this.prompt.moveCaretToEndOfPrompt();
this.prompt.autoCompleteSoon();
}
this._selectionTimeout = setTimeout(moveBackIfOutside.bind(this), 100);
},
_enterKeyPressed: function(event)
{
event.preventDefault();
event.stopPropagation();
this.prompt.clearAutoComplete(true);
var query = this.prompt.text;
if (!query.length)
return;
this.prompt.history.push(query);
this.prompt.historyOffset = 0;
this.prompt.text = "";
function queryTransaction(tx)
{
tx.executeSql(query, null, InspectorController.wrapCallback(this._queryFinished.bind(this, query)), InspectorController.wrapCallback(this._executeSqlError.bind(this, query)));
}
this.database.database.transaction(InspectorController.wrapCallback(queryTransaction.bind(this)), InspectorController.wrapCallback(this._queryError.bind(this, query)));
},
_queryFinished: function(query, tx, result)
{
var dataGrid = WebInspector.panels.databases.dataGridForResult(result);
dataGrid.element.addStyleClass("inline");
this._appendQueryResult(query, dataGrid.element);
if (query.match(/^create /i) || query.match(/^drop table /i))
WebInspector.panels.databases.updateDatabaseTables(this.database);
},
_queryError: function(query, error)
{
if (error.code == 1)
var message = error.message;
else if (error.code == 2)
var message = WebInspector.UIString("Database no longer has expected version.");
else
var message = WebInspector.UIString("An unexpected error %s occured.", error.code);
this._appendQueryResult(query, message, "error");
},
_executeSqlError: function(query, tx, error)
{
this._queryError(query, error);
},
_appendQueryResult: function(query, result, resultClassName)
{
var element = document.createElement("div");
element.className = "database-user-query";
var commandTextElement = document.createElement("span");
commandTextElement.className = "database-query-text";
commandTextElement.textContent = query;
element.appendChild(commandTextElement);
var resultElement = document.createElement("div");
resultElement.className = "database-query-result";
if (resultClassName)
resultElement.addStyleClass(resultClassName);
if (typeof result === "string" || result instanceof String)
resultElement.textContent = result;
else if (result && result.nodeName)
resultElement.appendChild(result);
if (resultElement.childNodes.length)
element.appendChild(resultElement);
this.element.insertBefore(element, this.promptElement);
this.promptElement.scrollIntoView(false);
}
}
WebInspector.DatabaseQueryView.prototype.__proto__ = WebInspector.View.prototype;
/* ScriptView.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
WebInspector.ScriptView = function(script)
{
WebInspector.View.call(this);
this.element.addStyleClass("script-view");
this.script = script;
this._frameNeedsSetup = true;
this._sourceFrameSetup = false;
this.sourceFrame = new WebInspector.SourceFrame(null, this._addBreakpoint.bind(this));
this.element.appendChild(this.sourceFrame.element);
}
WebInspector.ScriptView.prototype = {
show: function(parentElement)
{
WebInspector.View.prototype.show.call(this, parentElement);
this.setupSourceFrameIfNeeded();
},
hide: function()
{
WebInspector.View.prototype.hide.call(this);
this._currentSearchResultIndex = -1;
},
setupSourceFrameIfNeeded: function()
{
if (!("_frameNeedsSetup" in this))
return;
delete this._frameNeedsSetup;
this.attach();
InspectorController.addSourceToFrame("text/javascript", this.script.source, this.sourceFrame.element);
this.sourceFrame.addEventListener("syntax highlighting complete", this._syntaxHighlightingComplete, this);
this.sourceFrame.syntaxHighlightJavascript();
},
attach: function()
{
if (!this.element.parentNode)
document.getElementById("script-resource-views").appendChild(this.element);
},
_addBreakpoint: function(line)
{
var breakpoint = new WebInspector.Breakpoint(this.script.sourceURL, line, this.script.sourceID);
WebInspector.panels.scripts.addBreakpoint(breakpoint);
},
// The follow methods are pulled from SourceView, since they are
// generic and work with ScriptView just fine.
revealLine: WebInspector.SourceView.prototype.revealLine,
highlightLine: WebInspector.SourceView.prototype.highlightLine,
addMessage: WebInspector.SourceView.prototype.addMessage,
clearMessages: WebInspector.SourceView.prototype.clearMessages,
searchCanceled: WebInspector.SourceView.prototype.searchCanceled,
performSearch: WebInspector.SourceView.prototype.performSearch,
jumpToFirstSearchResult: WebInspector.SourceView.prototype.jumpToFirstSearchResult,
jumpToLastSearchResult: WebInspector.SourceView.prototype.jumpToLastSearchResult,
jumpToNextSearchResult: WebInspector.SourceView.prototype.jumpToNextSearchResult,
jumpToPreviousSearchResult: WebInspector.SourceView.prototype.jumpToPreviousSearchResult,
showingFirstSearchResult: WebInspector.SourceView.prototype.showingFirstSearchResult,
showingLastSearchResult: WebInspector.SourceView.prototype.showingLastSearchResult,
_jumpToSearchResult: WebInspector.SourceView.prototype._jumpToSearchResult,
_sourceFrameSetupFinished: WebInspector.SourceView.prototype._sourceFrameSetupFinished,
_syntaxHighlightingComplete: WebInspector.SourceView.prototype._syntaxHighlightingComplete
}
WebInspector.ScriptView.prototype.__proto__ = WebInspector.View.prototype;
/* ProfileView.js */
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
WebInspector.ProfileView = function(profile)
{
WebInspector.View.call(this);
this.element.addStyleClass("profile-view");
this.showSelfTimeAsPercent = true;
this.showTotalTimeAsPercent = true;
var columns = { "self": { title: WebInspector.UIString("Self"), width: "72px", sort: "descending", sortable: true },
"total": { title: WebInspector.UIString("Total"), width: "72px", sortable: true },
"calls": { title: WebInspector.UIString("Calls"), width: "54px", sortable: true },
"function": { title: WebInspector.UIString("Function"), disclosure: true, sortable: true } };
this.dataGrid = new WebInspector.DataGrid(columns);
this.dataGrid.addEventListener("sorting changed", this._sortData, this);
this.dataGrid.element.addEventListener("mousedown", this._mouseDownInDataGrid.bind(this), true);
this.element.appendChild(this.dataGrid.element);
this.viewSelectElement = document.createElement("select");
this.viewSelectElement.className = "status-bar-item";
this.viewSelectElement.addEventListener("change", this._changeView.bind(this), false);
this.view = "Heavy";
var heavyViewOption = document.createElement("option");
heavyViewOption.label = WebInspector.UIString("Heavy (Bottom Up)");
var treeViewOption = document.createElement("option");
treeViewOption.label = WebInspector.UIString("Tree (Top Down)");
this.viewSelectElement.appendChild(heavyViewOption);
this.viewSelectElement.appendChild(treeViewOption);
this.percentButton = document.createElement("button");
this.percentButton.className = "percent-time-status-bar-item status-bar-item";
this.percentButton.addEventListener("click", this._percentClicked.bind(this), false);
this.focusButton = document.createElement("button");
this.focusButton.title = WebInspector.UIString("Focus selected function.");
this.focusButton.className = "focus-profile-node-status-bar-item status-bar-item";
this.focusButton.disabled = true;
this.focusButton.addEventListener("click", this._focusClicked.bind(this), false);
this.excludeButton = document.createElement("button");
this.excludeButton.title = WebInspector.UIString("Exclude selected function.");
this.excludeButton.className = "exclude-profile-node-status-bar-item status-bar-item";
this.excludeButton.disabled = true;
this.excludeButton.addEventListener("click", this._excludeClicked.bind(this), false);
this.resetButton = document.createElement("button");
this.resetButton.title = WebInspector.UIString("Restore all functions.");
this.resetButton.className = "reset-profile-status-bar-item status-bar-item hidden";
this.resetButton.addEventListener("click", this._resetClicked.bind(this), false);
// Default to the heavy profile.
profile = profile.heavyProfile;
// By default the profile isn't sorted, so sort based on our default sort
// column and direction added to the DataGrid columns above.
profile.sortSelfTimeDescending();
this._updatePercentButton();
this.profile = profile;
}
WebInspector.ProfileView.prototype = {
get statusBarItems()
{
return [this.viewSelectElement, this.percentButton, this.focusButton, this.excludeButton, this.resetButton];
},
get profile()
{
return this._profile;
},
set profile(profile)
{
this._profile = profile;
this.refresh();
},
hide: function()
{
WebInspector.View.prototype.hide.call(this);
this._currentSearchResultIndex = -1;
},
refresh: function()
{
var selectedProfileNode = this.dataGrid.selectedNode ? this.dataGrid.selectedNode.profileNode : null;
this.dataGrid.removeChildren();
var children = this.profile.head.children;
var childrenLength = children.length;
for (var i = 0; i < childrenLength; ++i)
if (children[i].visible)
this.dataGrid.appendChild(new WebInspector.ProfileDataGridNode(this, children[i]));
if (selectedProfileNode && selectedProfileNode._dataGridNode)
selectedProfileNode._dataGridNode.selected = true;
},
refreshShowAsPercents: function()
{
this._updatePercentButton();
var child = this.dataGrid.children[0];
while (child) {
child.refresh();
child = child.traverseNextNode(false, null, true);
}
},
searchCanceled: function()
{
if (this._searchResults) {
for (var i = 0; i < this._searchResults.length; ++i) {
var profileNode = this._searchResults[i].profileNode;
delete profileNode._searchMatchedSelfColumn;
delete profileNode._searchMatchedTotalColumn;
delete profileNode._searchMatchedCallsColumn;
delete profileNode._searchMatchedFunctionColumn;
if (profileNode._dataGridNode)
profileNode._dataGridNode.refresh();
}
}
delete this._searchFinishedCallback;
this._currentSearchResultIndex = -1;
this._searchResults = [];
},
performSearch: function(query, finishedCallback)
{
// Call searchCanceled since it will reset everything we need before doing a new search.
this.searchCanceled();
query = query.trimWhitespace();
if (!query.length)
return;
this._searchFinishedCallback = finishedCallback;
var greaterThan = (query.indexOf(">") === 0);
var lessThan = (query.indexOf("<") === 0);
var equalTo = (query.indexOf("=") === 0 || ((greaterThan || lessThan) && query.indexOf("=") === 1));
var percentUnits = (query.lastIndexOf("%") === (query.length - 1));
var millisecondsUnits = (query.length > 2 && query.lastIndexOf("ms") === (query.length - 2));
var secondsUnits = (!millisecondsUnits && query.lastIndexOf("s") === (query.length - 1));
var queryNumber = parseFloat(query);
if (greaterThan || lessThan || equalTo) {
if (equalTo && (greaterThan || lessThan))
queryNumber = parseFloat(query.substring(2));
else
queryNumber = parseFloat(query.substring(1));
}
var queryNumberMilliseconds = (secondsUnits ? (queryNumber * 1000) : queryNumber);
// Make equalTo implicitly true if it wasn't specified there is no other operator.
if (!isNaN(queryNumber) && !(greaterThan || lessThan))
equalTo = true;
function matchesQuery(profileNode)
{
delete profileNode._searchMatchedSelfColumn;
delete profileNode._searchMatchedTotalColumn;
delete profileNode._searchMatchedCallsColumn;
delete profileNode._searchMatchedFunctionColumn;
if (percentUnits) {
if (lessThan) {
if (profileNode.selfPercent < queryNumber)
profileNode._searchMatchedSelfColumn = true;
if (profileNode.totalPercent < queryNumber)
profileNode._searchMatchedTotalColumn = true;
} else if (greaterThan) {
if (profileNode.selfPercent > queryNumber)
profileNode._searchMatchedSelfColumn = true;
if (profileNode.totalPercent > queryNumber)
profileNode._searchMatchedTotalColumn = true;
}
if (equalTo) {
if (profileNode.selfPercent == queryNumber)
profileNode._searchMatchedSelfColumn = true;
if (profileNode.totalPercent == queryNumber)
profileNode._searchMatchedTotalColumn = true;
}
} else if (millisecondsUnits || secondsUnits) {
if (lessThan) {
if (profileNode.selfTime < queryNumberMilliseconds)
profileNode._searchMatchedSelfColumn = true;
if (profileNode.totalTime < queryNumberMilliseconds)
profileNode._searchMatchedTotalColumn = true;
} else if (greaterThan) {
if (profileNode.selfTime > queryNumberMilliseconds)
profileNode._searchMatchedSelfColumn = true;
if (profileNode.totalTime > queryNumberMilliseconds)
profileNode._searchMatchedTotalColumn = true;
}
if (equalTo) {
if (profileNode.selfTime == queryNumberMilliseconds)
profileNode._searchMatchedSelfColumn = true;
if (profileNode.totalTime == queryNumberMilliseconds)
profileNode._searchMatchedTotalColumn = true;
}
} else {
if (equalTo && profileNode.numberOfCalls == queryNumber)
profileNode._searchMatchedCallsColumn = true;
if (greaterThan && profileNode.numberOfCalls > queryNumber)
profileNode._searchMatchedCallsColumn = true;
if (lessThan && profileNode.numberOfCalls < queryNumber)
profileNode._searchMatchedCallsColumn = true;
}
if (profileNode.functionName.hasSubstring(query, true) || profileNode.url.hasSubstring(query, true))
profileNode._searchMatchedFunctionColumn = true;
var matched = (profileNode._searchMatchedSelfColumn || profileNode._searchMatchedTotalColumn || profileNode._searchMatchedCallsColumn || profileNode._searchMatchedFunctionColumn);
if (matched && profileNode._dataGridNode)
profileNode._dataGridNode.refresh();
return matched;
}
var current = this.profile.head;
var ancestors = [];
var nextIndexes = [];
var startIndex = 0;
while (current) {
var children = current.children;
var childrenLength = children.length;
if (startIndex >= childrenLength) {
current = ancestors.pop();
startIndex = nextIndexes.pop();
continue;
}
for (var i = startIndex; i < childrenLength; ++i) {
var child = children[i];
if (matchesQuery(child)) {
if (child._dataGridNode) {
// The child has a data grid node already, no need to remember the ancestors.
this._searchResults.push({ profileNode: child });
} else {
var ancestorsCopy = [].concat(ancestors);
ancestorsCopy.push(current);
this._searchResults.push({ profileNode: child, ancestors: ancestorsCopy });
}
}
if (child.children.length) {
ancestors.push(current);
nextIndexes.push(i + 1);
current = child;
startIndex = 0;
break;
}
if (i === (childrenLength - 1)) {
current = ancestors.pop();
startIndex = nextIndexes.pop();
}
}
}
finishedCallback(this, this._searchResults.length);
},
jumpToFirstSearchResult: function()
{
if (!this._searchResults || !this._searchResults.length)
return;
this._currentSearchResultIndex = 0;
this._jumpToSearchResult(this._currentSearchResultIndex);
},
jumpToLastSearchResult: function()
{
if (!this._searchResults || !this._searchResults.length)
return;
this._currentSearchResultIndex = (this._searchResults.length - 1);
this._jumpToSearchResult(this._currentSearchResultIndex);
},
jumpToNextSearchResult: function()
{
if (!this._searchResults || !this._searchResults.length)
return;
if (++this._currentSearchResultIndex >= this._searchResults.length)
this._currentSearchResultIndex = 0;
this._jumpToSearchResult(this._currentSearchResultIndex);
},
jumpToPreviousSearchResult: function()
{
if (!this._searchResults || !this._searchResults.length)
return;
if (--this._currentSearchResultIndex < 0)
this._currentSearchResultIndex = (this._searchResults.length - 1);
this._jumpToSearchResult(this._currentSearchResultIndex);
},
showingFirstSearchResult: function()
{
return (this._currentSearchResultIndex === 0);
},
showingLastSearchResult: function()
{
return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1));
},
_jumpToSearchResult: function(index)
{
var searchResult = this._searchResults[index];
if (!searchResult)
return;
var profileNode = this._searchResults[index].profileNode;
if (!profileNode._dataGridNode && searchResult.ancestors) {
var ancestors = searchResult.ancestors;
for (var i = 0; i < ancestors.length; ++i) {
var ancestorProfileNode = ancestors[i];
var gridNode = ancestorProfileNode._dataGridNode;
if (gridNode)
gridNode.expand();
}
// No need to keep the ancestors around.
delete searchResult.ancestors;
}
gridNode = profileNode._dataGridNode;
if (!gridNode)
return;
gridNode.reveal();
gridNode.select();
},
_changeView: function(event)
{
if (!event || !this.profile)
return;
if (event.target.selectedIndex == 1 && this.view == "Heavy") {
this._sortProfile(this.profile.treeProfile);
this.profile = this.profile.treeProfile;
this.view = "Tree";
} else if (event.target.selectedIndex == 0 && this.view == "Tree") {
this._sortProfile(this.profile.heavyProfile);
this.profile = this.profile.heavyProfile;
this.view = "Heavy";
}
if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
return;
// The current search needs to be performed again. First negate out previous match
// count by calling the search finished callback with a negative number of matches.
// Then perform the search again the with same query and callback.
this._searchFinishedCallback(this, -this._searchResults.length);
this.performSearch(this.currentQuery, this._searchFinishedCallback);
},
_percentClicked: function(event)
{
var currentState = this.showSelfTimeAsPercent && this.showTotalTimeAsPercent;
this.showSelfTimeAsPercent = !currentState;
this.showTotalTimeAsPercent = !currentState;
this.refreshShowAsPercents();
},
_updatePercentButton: function()
{
if (this.showSelfTimeAsPercent && this.showTotalTimeAsPercent) {
this.percentButton.title = WebInspector.UIString("Show absolute total and self times.");
this.percentButton.addStyleClass("toggled-on");
} else {
this.percentButton.title = WebInspector.UIString("Show total and self times as percentages.");
this.percentButton.removeStyleClass("toggled-on");
}
},
_focusClicked: function(event)
{
if (!this.dataGrid.selectedNode || !this.dataGrid.selectedNode.profileNode)
return;
this.resetButton.removeStyleClass("hidden");
this.profile.focus(this.dataGrid.selectedNode.profileNode);
this.refresh();
},
_excludeClicked: function(event)
{
if (!this.dataGrid.selectedNode || !this.dataGrid.selectedNode.profileNode)
return;
this.resetButton.removeStyleClass("hidden");
this.profile.exclude(this.dataGrid.selectedNode.profileNode);
this.dataGrid.selectedNode.deselect();
this.refresh();
},
_resetClicked: function(event)
{
this.resetButton.addStyleClass("hidden");
this.profile.restoreAll();
this.refresh();
},
_dataGridNodeSelected: function(node)
{
this.focusButton.disabled = false;
this.excludeButton.disabled = false;
},
_dataGridNodeDeselected: function(node)
{
this.focusButton.disabled = true;
this.excludeButton.disabled = true;
},
_sortData: function(event)
{
this._sortProfile(this.profile);
},
_sortProfile: function(profile)
{
if (!profile)
return;
var sortOrder = this.dataGrid.sortOrder;
var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
var sortingFunctionName = "sort";
if (sortColumnIdentifier === "self")
sortingFunctionName += "SelfTime";
else if (sortColumnIdentifier === "total")
sortingFunctionName += "TotalTime";
else if (sortColumnIdentifier === "calls")
sortingFunctionName += "Calls";
else if (sortColumnIdentifier === "function")
sortingFunctionName += "FunctionName";
if (sortOrder === "ascending")
sortingFunctionName += "Ascending";
else
sortingFunctionName += "Descending";
if (!(sortingFunctionName in this.profile))
return;
profile[sortingFunctionName]();
if (profile === this.profile)
this.refresh();
},
_mouseDownInDataGrid: function(event)
{
if (event.detail < 2)
return;
var cell = event.target.enclosingNodeOrSelfWithNodeName("td");
if (!cell || (!cell.hasStyleClass("total-column") && !cell.hasStyleClass("self-column")))
return;
if (cell.hasStyleClass("total-column"))
this.showTotalTimeAsPercent = !this.showTotalTimeAsPercent;
else if (cell.hasStyleClass("self-column"))
this.showSelfTimeAsPercent = !this.showSelfTimeAsPercent;
this.refreshShowAsPercents();
event.preventDefault();
event.stopPropagation();
}
}
WebInspector.ProfileView.prototype.__proto__ = WebInspector.View.prototype;
WebInspector.ProfileDataGridNode = function(profileView, profileNode)
{
this.profileView = profileView;
this.profileNode = profileNode;
profileNode._dataGridNode = this;
// Find the first child that is visible. Since we don't want to claim
// we have children if all the children are invisible.
var hasChildren = false;
var children = this.profileNode.children;
var childrenLength = children.length;
for (var i = 0; i < childrenLength; ++i) {
if (children[i].visible) {
hasChildren = true;
break;
}
}
WebInspector.DataGridNode.call(this, null, hasChildren);
this.addEventListener("populate", this._populate, this);
this.expanded = profileNode._expanded;
}
WebInspector.ProfileDataGridNode.prototype = {
get data()
{
function formatMilliseconds(time)
{
return Number.secondsToString(time / 1000, WebInspector.UIString.bind(WebInspector), true);
}
var data = {};
data["function"] = this.profileNode.functionName;
data["calls"] = this.profileNode.numberOfCalls;
if (this.profileView.showSelfTimeAsPercent)
data["self"] = WebInspector.UIString("%.2f%%", this.profileNode.selfPercent);
else
data["self"] = formatMilliseconds(this.profileNode.selfTime);
if (this.profileView.showTotalTimeAsPercent)
data["total"] = WebInspector.UIString("%.2f%%", this.profileNode.totalPercent);
else
data["total"] = formatMilliseconds(this.profileNode.totalTime);
return data;
},
createCell: function(columnIdentifier)
{
var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
if (columnIdentifier === "self" && this.profileNode._searchMatchedSelfColumn)
cell.addStyleClass("highlight");
else if (columnIdentifier === "total" && this.profileNode._searchMatchedTotalColumn)
cell.addStyleClass("highlight");
else if (columnIdentifier === "calls" && this.profileNode._searchMatchedCallsColumn)
cell.addStyleClass("highlight");
if (columnIdentifier !== "function")
return cell;
if (this.profileNode._searchMatchedFunctionColumn)
cell.addStyleClass("highlight");
if (this.profileNode.url) {
var fileName = WebInspector.displayNameForURL(this.profileNode.url);
var urlElement = document.createElement("a");
urlElement.className = "profile-node-file webkit-html-resource-link";
urlElement.href = this.profileNode.url;
urlElement.lineNumber = this.profileNode.lineNumber;
if (this.profileNode.lineNumber > 0)
urlElement.textContent = fileName + ":" + this.profileNode.lineNumber;
else
urlElement.textContent = fileName;
cell.insertBefore(urlElement, cell.firstChild);
}
return cell;
},
select: function(supressSelectedEvent)
{
WebInspector.DataGridNode.prototype.select.call(this, supressSelectedEvent);
this.profileView._dataGridNodeSelected(this);
},
deselect: function(supressDeselectedEvent)
{
WebInspector.DataGridNode.prototype.deselect.call(this, supressDeselectedEvent);
this.profileView._dataGridNodeDeselected(this);
},
expand: function()
{
WebInspector.DataGridNode.prototype.expand.call(this);
this.profileNode._expanded = true;
},
collapse: function()
{
WebInspector.DataGridNode.prototype.collapse.call(this);
this.profileNode._expanded = false;
},
_populate: function(event)
{
var children = this.profileNode.children;
var childrenLength = children.length;
for (var i = 0; i < childrenLength; ++i)
if (children[i].visible)
this.appendChild(new WebInspector.ProfileDataGridNode(this.profileView, children[i]));
this.removeEventListener("populate", this._populate, this);
}
}
WebInspector.ProfileDataGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment