/*
* 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];
if (WebInspector.panels.resources)
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();
if (WebInspector.panels.resources)
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 && 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 (dotNotation && !/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(property))
continue;
if (bracketNotation) {
if (!/^[0-9]+$/.test(property))
property = quoteUsed + property.escapeCharacters(quoteUsed + "\\") + quoteUsed;
property += "]";
}
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 && 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, inline)
{
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, inline);
return span;
},
_formatvalue: function(val, elem, inline)
{
elem.appendChild(document.createTextNode(val));
},
_formatstring: function(str, elem, inline)
{
elem.appendChild(document.createTextNode("\"" + str + "\""));
},
_formatregexp: function(re, elem, inline)
{
var formatted = String(re).replace(/([\\\/])/g, "\\$1").replace(/\\(\/[gim]*)$/, "$1").substring(1);
elem.appendChild(document.createTextNode(formatted));
},
_formatarray: function(arr, elem, inline)
{
elem.appendChild(document.createTextNode("["));
for (var i = 0; i < arr.length; ++i) {
elem.appendChild(this._format(arr[i], true));
if (i < arr.length - 1)
elem.appendChild(document.createTextNode(", "));
}
elem.appendChild(document.createTextNode("]"));
},
_formatnode: function(node, elem, inline)
{
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);
if (inline)
elem.appendChild(anchor);
else
elem.appendChild(new WebInspector.ObjectPropertiesSection(node, anchor, null, null, true).element);
},
_formatobject: function(obj, elem, inline)
{
if (inline)
elem.appendChild(document.createTextNode(Object.describe(obj)));
else
elem.appendChild(new WebInspector.ObjectPropertiesSection(obj, null, null, null, true).element);
},
_formaterror: function(obj, elem, inline)
{
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 || 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, true);
}
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 if (parameters.length === 1)
formattedResult.appendChild(WebInspector.console._format(parameters[0]));
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.WML:
element.addStyleClass("console-wml-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.WML:
sourceString = "WML";
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 Console.h
WebInspector.ConsoleMessage.MessageSource = {
HTML: 0,
WML: 1,
XML: 2,
JS: 3,
CSS: 4,
Other: 5
}
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();
}
}