blob: 5c32de629ccd1593c4ad95e31220f2c3723ac587 [file] [log] [blame]
/*
* Copyright (C) 2013 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. 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 INC. 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.LogContentView = function(representedObject)
{
WebInspector.ContentView.call(this, representedObject);
this._nestingLevel = 0;
this._selectedMessages = [];
this.element.classList.add(WebInspector.LogContentView.StyleClassName);
this.messagesElement = document.createElement("div");
this.messagesElement.className = "console-messages";
this.messagesElement.tabIndex = 0;
this.messagesElement.setAttribute("role", "log");
this.messagesElement.addEventListener("mousedown", this._mousedown.bind(this));
this.messagesElement.addEventListener("focus", this._didFocus.bind(this));
this.messagesElement.addEventListener("blur", this._didBlur.bind(this));
this.messagesElement.addEventListener("keydown", this._keyDown.bind(this));
this.messagesElement.addEventListener("click", this._click.bind(this), true);
this.messagesElement.addEventListener("dragstart", this._ondragstart.bind(this), true);
this.element.appendChild(this.messagesElement);
this.prompt = WebInspector.quickConsole.prompt;
this._keyboardShortcutCommandA = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "A");
this._keyboardShortcutEsc = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.Escape);
this._logViewController = new WebInspector.JavaScriptLogViewController(this.messagesElement, this.element, this.prompt, this, "console-prompt-history");
this._searchBar = new WebInspector.SearchBar("log-search-bar", WebInspector.UIString("Filter Console Log"), this);
this._searchBar.addEventListener(WebInspector.SearchBar.Event.TextChanged, this._searchTextDidChange, this);
var scopeBarItems = [
new WebInspector.ScopeBarItem(WebInspector.LogContentView.Scopes.All, WebInspector.UIString("All"), true),
new WebInspector.ScopeBarItem(WebInspector.LogContentView.Scopes.Errors, WebInspector.UIString("Errors")),
new WebInspector.ScopeBarItem(WebInspector.LogContentView.Scopes.Warnings, WebInspector.UIString("Warnings")),
new WebInspector.ScopeBarItem(WebInspector.LogContentView.Scopes.Logs, WebInspector.UIString("Logs"))
];
this._scopeBar = new WebInspector.ScopeBar("log-scope-bar", scopeBarItems, scopeBarItems[0]);
this._scopeBar.addEventListener(WebInspector.ScopeBar.Event.SelectionChanged, this._scopeBarSelectionDidChange, this);
this._clearLogNavigationItem = new WebInspector.ButtonNavigationItem("clear-log", WebInspector.UIString("Clear log (%s or %s)").format(this._logViewController.messagesClearKeyboardShortcut.displayName, this._logViewController.messagesAlternateClearKeyboardShortcut.displayName), "Images/NavigationItemTrash.svg", 16, 16);
this._clearLogNavigationItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._clearLog, this);
var toolTip = WebInspector.UIString("Show split console");
var altToolTip = WebInspector.UIString("Show full-height console");
this._toggleSplitNavigationItem = new WebInspector.ToggleButtonNavigationItem("split-toggle", toolTip, altToolTip, "Images/SplitToggleDown.svg", "Images/SplitToggleUp.svg", 16, 16);
this._toggleSplitNavigationItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._toggleSplit, this);
this._toggleSplitNavigationItem.toggled = WebInspector.isShowingSplitConsole();
this.messagesElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), false);
WebInspector.logManager.addEventListener(WebInspector.LogManager.Event.Cleared, this._sessionsCleared, this);
WebInspector.logManager.addEventListener(WebInspector.LogManager.Event.SessionStarted, this._sessionStarted, this);
WebInspector.logManager.addEventListener(WebInspector.LogManager.Event.MessageAdded, this._messageAdded, this);
WebInspector.logManager.addEventListener(WebInspector.LogManager.Event.PreviousMessageRepeatCountUpdated, this._previousMessageRepeatCountUpdated, this);
WebInspector.logManager.addEventListener(WebInspector.LogManager.Event.ActiveLogCleared, this._activeLogCleared, this);
}
WebInspector.LogContentView.Scopes = {
All: "log-all",
Errors: "log-errors",
Warnings: "log-warnings",
Logs: "log-logs"
};
WebInspector.LogContentView.StyleClassName = "log";
WebInspector.LogContentView.ItemWrapperStyleClassName = "console-item";
WebInspector.LogContentView.FilteredOutStyleClassName = "filtered-out";
WebInspector.LogContentView.SelectedStyleClassName = "selected";
WebInspector.LogContentView.SearchInProgressStyleClassName = "search-in-progress";
WebInspector.LogContentView.FilteredOutBySearchStyleClassName = "filtered-out-by-search";
WebInspector.LogContentView.HighlightedStyleClassName = "highlighted";
WebInspector.LogContentView.prototype = {
constructor: WebInspector.LogContentView,
// Public
get navigationItems()
{
return [this._searchBar, this._scopeBar, this._clearLogNavigationItem, this._toggleSplitNavigationItem];
},
get scopeBar()
{
return this._scopeBar;
},
updateLayout: function()
{
WebInspector.ContentView.prototype.updateLayout.call(this);
this._scrollElementHeight = this.messagesElement.getBoundingClientRect().height;
},
shown: function()
{
this._toggleSplitNavigationItem.toggled = WebInspector.isShowingSplitConsole();
this.prompt.focus();
},
get scrollableElements()
{
return [this.element];
},
get shouldKeepElementsScrolledToBottom()
{
return true;
},
get searchInProgress()
{
return this.messagesElement.classList.contains(WebInspector.LogContentView.SearchInProgressStyleClassName);
},
didClearMessages: function()
{
if (this._ignoreDidClearMessages)
return;
WebInspector.logManager.requestClearMessages();
},
didAppendConsoleMessage: function(message)
{
WebInspector.quickConsole.updateLayout();
// Nest the message.
if (message.type !== WebInspector.ConsoleMessage.MessageType.EndGroup) {
var x = 16 * this._nestingLevel;
var messageElement = message.toMessageElement();
messageElement.style.left = x + "px";
messageElement.style.width = "calc(100% - " + x + "px)";
}
// Update the nesting level.
switch (message.type) {
case WebInspector.ConsoleMessage.MessageType.StartGroup:
case WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed:
++this._nestingLevel;
break;
case WebInspector.ConsoleMessage.MessageType.EndGroup:
--this._nestingLevel;
break;
}
this._clearFocusableChildren();
// Some results don't populate until further backend dispatches occur (like the DOM tree).
// We want to remove focusable children after those pending dispatches too.
InspectorBackend.runAfterPendingDispatches(this._clearFocusableChildren.bind(this));
// We only auto show the console if the message is a result.
// This is when the user evaluated something directly in the prompt.
if (message.type !== WebInspector.ConsoleMessage.MessageType.Result)
return;
if (!WebInspector.isShowingConsoleView())
WebInspector.showSplitConsole();
this._logViewController.scrollToBottom();
},
promptDidChangeHeight: function()
{
WebInspector.quickConsole.updateLayout();
},
get supportsSave()
{
return true;
},
get saveData()
{
return {url: "web-inspector:///Console.txt", content: this._formatMessagesAsData(false), forceSaveAs: true};
},
handleCopyEvent: function(event)
{
if (!this._selectedMessages.length)
return;
event.clipboardData.setData("text/plain", this._formatMessagesAsData(true));
event.stopPropagation();
event.preventDefault();
},
focusSearchBar: function()
{
this._searchBar.focus();
},
highlightPreviousSearchMatch: function()
{
if (!this.searchInProgress || isEmptyObject(this._searchMatches))
return;
var index = this._selectedSearchMatch ? this._searchMatches.indexOf(this._selectedSearchMatch) : this._searchMatches.length;
this._highlightSearchMatchAtIndex(index - 1);
},
highlightNextSearchMatch: function()
{
if (!this.searchInProgress || isEmptyObject(this._searchMatches))
return;
var index = this._selectedSearchMatch ? this._searchMatches.indexOf(this._selectedSearchMatch) + 1 : 0;
this._highlightSearchMatchAtIndex(index);
},
searchBarWantsToLoseFocus: function(searchBar)
{
if (this._selectedMessages.length)
this.messagesElement.focus();
else
this.prompt.focus();
},
searchBarDidActivate: function(searchBar)
{
if (!isEmptyObject(this._searchMatches))
this._highlightSearchMatchAtIndex(0);
this.prompt.focus();
},
// Private
_formatMessagesAsData: function(onlySelected)
{
var messages = this._allMessages();
if (onlySelected) {
messages = this._allMessages().filter(function(message) {
return message.parentNode.classList.contains(WebInspector.LogContentView.SelectedStyleClassName);
});
}
var data = "";
var isPrefixOptional = messages.length <= 1 && onlySelected;
messages.forEach(function (messageElement, index) {
var messageObject = messageElement.message;
if (!messageObject)
messageObject = messageElement.command;
if (!messageObject)
return;
if (index > 0)
data += "\n";
data += messageObject.toClipboardString(isPrefixOptional);
});
return data;
},
_sessionsCleared: function(event)
{
this._ignoreDidClearMessages = true;
this._logViewController.clear();
this._ignoreDidClearMessages = false;
},
_sessionStarted: function(event)
{
this._logViewController.startNewSession();
},
_messageAdded: function(event)
{
var message = this._logViewController.appendConsoleMessage(event.data.message);
if (message.type !== WebInspector.ConsoleMessage.MessageType.EndGroup)
this._filterMessages([message.toMessageElement()]);
},
_previousMessageRepeatCountUpdated: function(event)
{
this._logViewController.updatePreviousMessageRepeatCount(event.data.count);
},
_handleContextMenuEvent: function(event)
{
if (!window.getSelection().isCollapsed) {
// If there is a selection, we want to show our normal context menu
// (with Copy, etc.), and not Clear Log.
return;
}
// We don't want to show the custom menu for links in the console.
if (event.target.enclosingNodeOrSelfWithNodeName("a"))
return;
var contextMenu = new WebInspector.ContextMenu(event);
contextMenu.appendItem(WebInspector.UIString("Clear Log"), this._clearLog.bind(this));
contextMenu.show();
},
_mousedown: function(event)
{
if (event.button !== 0 || event.ctrlKey)
return;
if (event.defaultPrevented) {
// Default was prevented on the event, so this means something deeper (like a disclosure triangle)
// handled the mouse down. In this case we want to clear the selection and don't make a new selection.
this._clearMessagesSelection();
return;
}
if (!this._focused) {
this.messagesElement.focus();
if (this._selectedMessages.length)
return;
}
this._mouseDownWrapper = event.target.enclosingNodeOrSelfWithClass(WebInspector.LogContentView.ItemWrapperStyleClassName);
this._mouseDownShiftKey = event.shiftKey;
this._mouseDownCommandKey = event.metaKey;
this._mouseMoveIsRowSelection = false;
window.addEventListener("mousemove", this);
window.addEventListener("mouseup", this);
},
_targetInMessageCanBeSelected: function(target, message)
{
if (target.enclosingNodeOrSelfWithNodeName("a"))
return false;
return true;
},
_mousemove: function(event)
{
var selection = window.getSelection();
var wrapper = event.target.enclosingNodeOrSelfWithClass(WebInspector.LogContentView.ItemWrapperStyleClassName);
if (!wrapper) {
// No wrapper under the mouse, so look at the selection to try and find one.
if (!selection.isCollapsed) {
wrapper = selection.focusNode.parentNode.enclosingNodeOrSelfWithClass(WebInspector.LogContentView.ItemWrapperStyleClassName);
selection.removeAllRanges();
}
if (!wrapper)
return;
}
if (!selection.isCollapsed)
this._clearMessagesSelection();
if (wrapper === this._mouseDownWrapper && !this._mouseMoveIsRowSelection)
return;
selection.removeAllRanges();
if (!this._mouseMoveIsRowSelection)
this._updateMessagesSelection(this._mouseDownWrapper.messageElement, this._mouseDownCommandKey, this._mouseDownShiftKey);
this._updateMessagesSelection(wrapper.messageElement, false, true);
this._mouseMoveIsRowSelection = true;
event.preventDefault();
event.stopPropagation();
},
_mouseup: function(event)
{
window.removeEventListener("mousemove", this);
window.removeEventListener("mouseup", this);
var selection = window.getSelection();
var wrapper = event.target.enclosingNodeOrSelfWithClass(WebInspector.LogContentView.ItemWrapperStyleClassName);
if (wrapper && (selection.isCollapsed || event.shiftKey)) {
selection.removeAllRanges();
var message = wrapper.messageElement;
if (this._targetInMessageCanBeSelected(event.target, message)) {
var sameWrapper = wrapper === this._mouseDownWrapper;
this._mouseInteractionShouldPreventClickPropagation = !this._isMessageSelected(message);
this._updateMessagesSelection(message, sameWrapper ? this._mouseDownCommandKey : false, sameWrapper ? this._mouseDownShiftKey : true);
}
} else if (!selection.isCollapsed) {
// There is a text selection, clear the row selection.
this._clearMessagesSelection();
} else if (!this._mouseDownWrapper) {
// The mouse didn't hit a console item, so clear the row selection.
this._clearMessagesSelection();
// Focus the prompt. Focusing the prompt needs to happen after the click to work.
setTimeout(function () { this.prompt.focus() }.bind(this), 0);
}
delete this._mouseMoveIsRowSelection;
delete this._mouseDownWrapper;
delete this._mouseDownShiftKey;
delete this._mouseDownCommandKey;
},
_click: function(event)
{
if (!this._mouseInteractionShouldPreventClickPropagation)
return;
event.stopPropagation();
delete this._mouseInteractionShouldPreventClickPropagation;
},
_ondragstart: function(event)
{
if (event.target.enclosingNodeOrSelfWithClass(WebInspector.DOMTreeOutline.StyleClassName)) {
event.stopPropagation();
event.preventDefault();
}
},
handleEvent: function(event)
{
switch (event.type) {
case "mousemove":
this._mousemove(event);
break;
case "mouseup":
this._mouseup(event);
break;
}
},
_updateMessagesSelection: function(message, multipleSelection, rangeSelection)
{
var alreadySelectedMessage = this._selectedMessages.contains(message);
if (alreadySelectedMessage && this._selectedMessages.length && multipleSelection) {
message.parentNode.classList.remove(WebInspector.LogContentView.SelectedStyleClassName);
this._selectedMessages.remove(message);
return;
}
if (!multipleSelection && !rangeSelection)
this._clearMessagesSelection();
if (rangeSelection) {
var messages = this._visibleMessages();
var refIndex = this._referenceMessageForRangeSelection ? messages.indexOf(this._referenceMessageForRangeSelection) : 0;
var targetIndex = messages.indexOf(message);
var newRange = [Math.min(refIndex, targetIndex), Math.max(refIndex, targetIndex)];
if (this._selectionRange && this._selectionRange[0] === newRange[0] && this._selectionRange[1] === newRange[1])
return;
var startIndex = this._selectionRange ? Math.min(this._selectionRange[0], newRange[0]) : newRange[0];
var endIndex = this._selectionRange ? Math.max(this._selectionRange[1], newRange[1]) : newRange[1];
for (var i = startIndex; i <= endIndex; ++i) {
var messageInRange = messages[i];
if (i >= newRange[0] && i <= newRange[1] && !messageInRange.parentNode.classList.contains(WebInspector.LogContentView.SelectedStyleClassName)) {
messageInRange.parentNode.classList.add(WebInspector.LogContentView.SelectedStyleClassName);
this._selectedMessages.push(messageInRange);
} else if (i < newRange[0] || i > newRange[1] && messageInRange.parentNode.classList.contains(WebInspector.LogContentView.SelectedStyleClassName)) {
messageInRange.parentNode.classList.remove(WebInspector.LogContentView.SelectedStyleClassName);
this._selectedMessages.remove(messageInRange);
}
}
this._selectionRange = newRange;
} else {
message.parentNode.classList.add(WebInspector.LogContentView.SelectedStyleClassName);
this._selectedMessages.push(message);
}
if (!rangeSelection)
this._referenceMessageForRangeSelection = message;
if (!alreadySelectedMessage)
this._ensureMessageIsVisible(this._selectedMessages.lastValue);
},
_ensureMessageIsVisible: function(message)
{
if (!message)
return;
var y = this._positionForMessage(message).y;
if (y < 0) {
this.element.scrollTop += y;
return;
}
var nextMessage = this._nextMessage(message);
if (nextMessage) {
y = this._positionForMessage(nextMessage).y;
if (y > this._scrollElementHeight)
this.element.scrollTop += y - this._scrollElementHeight;
} else {
y += message.getBoundingClientRect().height;
if (y > this._scrollElementHeight)
this.element.scrollTop += y - this._scrollElementHeight;
}
},
_positionForMessage: function(message)
{
var pagePoint = window.webkitConvertPointFromNodeToPage(message, new WebKitPoint(0, 0));
return window.webkitConvertPointFromPageToNode(this.element, pagePoint);
},
_isMessageVisible: function(message)
{
var node = message.parentNode;
if (node.classList.contains(WebInspector.LogContentView.FilteredOutStyleClassName))
return false;
if (this.searchInProgress && node.classList.contains(WebInspector.LogContentView.FilteredOutBySearchStyleClassName))
return false;
if (message.classList.contains("console-group-title"))
node = node.parentNode.parentNode;
while (node && node !== this.messagesElement) {
if (node.classList.contains("collapsed"))
return false;
node = node.parentNode;
}
return true;
},
_isMessageSelected: function(message)
{
return message.parentNode.classList.contains(WebInspector.LogContentView.SelectedStyleClassName);
},
_clearMessagesSelection: function()
{
this._selectedMessages.forEach(function(message) {
message.parentNode.classList.remove(WebInspector.LogContentView.SelectedStyleClassName);
});
this._selectedMessages = [];
delete this._referenceMessageForRangeSelection;
},
_selectAllMessages: function()
{
this._clearMessagesSelection();
var messages = this._visibleMessages();
for (var i = 0; i < messages.length; ++i) {
var message = messages[i];
message.parentNode.classList.add(WebInspector.LogContentView.SelectedStyleClassName);
this._selectedMessages.push(message);
}
},
_allMessages: function()
{
return Array.prototype.slice.call(this.messagesElement.querySelectorAll(".console-message, .console-user-command"));
},
_unfilteredMessages: function()
{
return this._allMessages().filter(function(message) {
return !message.parentNode.classList.contains(WebInspector.LogContentView.FilteredOutStyleClassName);
});
},
_visibleMessages: function()
{
var unfilteredMessages = this._unfilteredMessages();
if (!this.searchInProgress)
return unfilteredMessages;
return unfilteredMessages.filter(function(message) {
return !message.parentNode.classList.contains(WebInspector.LogContentView.FilteredOutBySearchStyleClassName);
});
},
_activeLogCleared: function(event)
{
this._ignoreDidClearMessages = true;
this._logViewController.clear();
this._ignoreDidClearMessages = false;
},
_toggleSplit: function()
{
if (WebInspector.isShowingSplitConsole())
WebInspector.showFullHeightConsole();
else
WebInspector.showSplitConsole();
},
_clearLog: function()
{
this._logViewController.clear();
},
_scopeBarSelectionDidChange: function(event)
{
this._filterMessages(this._allMessages());
},
_filterMessages: function(messages)
{
var showsAll = this._scopeBar.item(WebInspector.LogContentView.Scopes.All).selected;
var showsErrors = this._scopeBar.item(WebInspector.LogContentView.Scopes.Errors).selected;
var showsWarnings = this._scopeBar.item(WebInspector.LogContentView.Scopes.Warnings).selected;
var showsLogs = this._scopeBar.item(WebInspector.LogContentView.Scopes.Logs).selected;
messages.forEach(function(message) {
var visible = showsAll || message.command instanceof WebInspector.ConsoleCommand || message.message instanceof WebInspector.ConsoleCommandResult;
if (!visible) {
switch(message.message.level) {
case WebInspector.ConsoleMessage.MessageLevel.Warning:
visible = showsWarnings;
break;
case WebInspector.ConsoleMessage.MessageLevel.Error:
visible = showsErrors;
break;
case WebInspector.ConsoleMessage.MessageLevel.Log:
visible = showsLogs;
break;
}
}
var classList = message.parentNode.classList;
if (visible)
classList.remove(WebInspector.LogContentView.FilteredOutStyleClassName);
else {
this._selectedMessages.remove(message);
classList.remove(WebInspector.LogContentView.SelectedStyleClassName);
classList.add(WebInspector.LogContentView.FilteredOutStyleClassName);
}
}.bind(this));
this._performSearch();
},
_didFocus: function(event)
{
this._focused = true;
},
_didBlur: function(event)
{
this._focused = false;
},
_keyDown: function(event)
{
if (this._keyboardShortcutCommandA.matchesEvent(event))
this._commandAWasPressed(event);
else if (this._keyboardShortcutEsc.matchesEvent(event))
this._escapeWasPressed(event);
else if (event.keyIdentifier === "Up")
this._upArrowWasPressed(event);
else if (event.keyIdentifier === "Down")
this._downArrowWasPressed(event);
else if (event.keyIdentifier === "Left")
this._leftArrowWasPressed(event);
else if (event.keyIdentifier === "Right")
this._rightArrowWasPressed(event);
},
_commandAWasPressed: function(event)
{
this._selectAllMessages();
event.preventDefault();
},
_escapeWasPressed: function(event)
{
if (this._selectedMessages.length)
this._clearMessagesSelection();
else
this.prompt.focus();
event.preventDefault();
},
_upArrowWasPressed: function(event)
{
var messages = this._visibleMessages();
if (!this._selectedMessages.length) {
if (messages.length)
this._updateMessagesSelection(messages.lastValue, false, false);
return;
}
var lastMessage = this._selectedMessages.lastValue;
var previousMessage = this._previousMessage(lastMessage);
if (previousMessage)
this._updateMessagesSelection(previousMessage, false, event.shiftKey);
else if (!event.shiftKey) {
this._clearMessagesSelection();
this._updateMessagesSelection(messages[0], false, false);
}
event.preventDefault();
},
_downArrowWasPressed: function(event)
{
var messages = this._visibleMessages();
if (!this._selectedMessages.length) {
if (messages.length)
this._updateMessagesSelection(messages[0], false, false);
return;
}
var lastMessage = this._selectedMessages.lastValue;
var nextMessage = this._nextMessage(lastMessage);
if (nextMessage)
this._updateMessagesSelection(nextMessage, false, event.shiftKey);
else if (!event.shiftKey) {
this._clearMessagesSelection();
this._updateMessagesSelection(messages.lastValue, false, false);
}
event.preventDefault();
},
_leftArrowWasPressed: function(event)
{
if (this._selectedMessages.length !== 1)
return;
var currentMessage = this._selectedMessages[0];
if (currentMessage.classList.contains("console-group-title"))
currentMessage.parentNode.parentNode.classList.add("collapsed");
else {
var outlineTitle = currentMessage.querySelector("ol.outline-disclosure > li.parent");
if (outlineTitle) {
if (event.altKey)
outlineTitle.treeElement.collapseRecursively();
else
outlineTitle.treeElement.collapse();
} else {
var outlineSection = currentMessage.querySelector(".console-formatted-object > .section");
if (outlineSection)
outlineSection._section.collapse();
}
}
},
_rightArrowWasPressed: function(event)
{
if (this._selectedMessages.length !== 1)
return;
var currentMessage = this._selectedMessages[0];
if (currentMessage.classList.contains("console-group-title"))
currentMessage.parentNode.parentNode.classList.remove("collapsed");
else {
var outlineTitle = currentMessage.querySelector("ol.outline-disclosure > li.parent");
if (outlineTitle) {
outlineTitle.treeElement.onexpand = function() {
setTimeout(function () {
this._ensureMessageIsVisible(currentMessage);
this._clearFocusableChildren();
delete outlineTitle.treeElement.onexpand;
}.bind(this));
}.bind(this);
if (event.altKey)
outlineTitle.treeElement.expandRecursively();
else
outlineTitle.treeElement.expand();
} else {
var outlineSection = currentMessage.querySelector(".console-formatted-object > .section");
if (outlineSection) {
outlineSection._section.addEventListener(WebInspector.Section.Event.VisibleContentDidChange, this._propertiesSectionDidUpdateContent, this);
outlineSection._section.expand();
}
}
}
},
_propertiesSectionDidUpdateContent: function(event)
{
var section = event.target;
section.removeEventListener(WebInspector.Section.Event.VisibleContentDidChange, this._propertiesSectionDidUpdateContent, this);
var message = section.element.enclosingNodeOrSelfWithClass(WebInspector.LogContentView.ItemWrapperStyleClassName).messageElement;
if (!this._isMessageSelected(message))
return;
setTimeout(function () {
this._ensureMessageIsVisible(message);
this._clearFocusableChildren();
}.bind(this));
},
_previousMessage: function(message)
{
var messages = this._visibleMessages();
for (var i = messages.indexOf(message) - 1; i >= 0; --i) {
if (this._isMessageVisible(messages[i]))
return messages[i];
}
},
_nextMessage: function(message)
{
var messages = this._visibleMessages();
for (var i = messages.indexOf(message) + 1; i < messages.length; ++i) {
if (this._isMessageVisible(messages[i]))
return messages[i];
}
},
_clearFocusableChildren: function()
{
var focusableElements = this.messagesElement.querySelectorAll("[tabindex]");
for (var i = 0, count = focusableElements.length; i < count; ++i)
focusableElements[i].removeAttribute("tabindex");
},
_searchTextDidChange: function(event)
{
this._performSearch();
},
_performSearch: function()
{
if (!isEmptyObject(this._searchHighlightDOMChanges))
WebInspector.revertDomChanges(this._searchHighlightDOMChanges);
var searchTerms = this._searchBar.text;
if (searchTerms === "") {
delete this._selectedSearchMatch;
this._matchingSearchElements = [];
this.messagesElement.classList.remove(WebInspector.LogContentView.SearchInProgressStyleClassName);
return;
}
this.messagesElement.classList.add(WebInspector.LogContentView.SearchInProgressStyleClassName);
this._searchHighlightDOMChanges = [];
this._searchMatches = [];
this._selectedSearchMathIsValid = false;
var searchRegex = new RegExp(searchTerms.escapeForRegExp(), "gi");
this._unfilteredMessages().forEach(function(message) {
var matchRanges = [];
var text = message.textContent;
var match = searchRegex.exec(text);
while (match) {
matchRanges.push({ offset: match.index, length: match[0].length });
match = searchRegex.exec(text);
}
if (!isEmptyObject(matchRanges))
this._highlightRanges(message, matchRanges);
var classList = message.parentNode.classList;
if (!isEmptyObject(matchRanges) || message.command instanceof WebInspector.ConsoleCommand || message.message instanceof WebInspector.ConsoleCommandResult)
classList.remove(WebInspector.LogContentView.FilteredOutBySearchStyleClassName);
else
classList.add(WebInspector.LogContentView.FilteredOutBySearchStyleClassName);
}, this);
if (!this._selectedSearchMathIsValid && this._selectedSearchMatch) {
this._selectedSearchMatch.highlight.classList.remove(WebInspector.LogContentView.SelectedStyleClassName);
delete this._selectedSearchMatch;
}
},
_highlightRanges: function(message, matchRanges)
{
var highlightedElements = WebInspector.highlightRangesWithStyleClass(message, matchRanges, WebInspector.LogContentView.HighlightedStyleClassName, this._searchHighlightDOMChanges);
console.assert(highlightedElements.length === matchRanges.length);
matchRanges.forEach(function (range, index) {
this._searchMatches.push({
message: message,
range: range,
highlight: highlightedElements[index]
});
if (this._selectedSearchMatch && !this._selectedSearchMathIsValid && this._selectedSearchMatch.message === message) {
this._selectedSearchMathIsValid = this._rangesOverlap(this._selectedSearchMatch.range, range);
if (this._selectedSearchMathIsValid) {
delete this._selectedSearchMatch;
this._highlightSearchMatchAtIndex(this._searchMatches.length - 1);
}
}
}, this);
},
_rangesOverlap: function(range1, range2)
{
return range1.offset <= range2.offset + range2.length && range2.offset <= range1.offset + range1.length;
},
_highlightSearchMatchAtIndex: function(index)
{
if (index >= this._searchMatches.length)
index = 0;
else if (index < 0)
index = this._searchMatches.length - 1;
if (this._selectedSearchMatch)
this._selectedSearchMatch.highlight.classList.remove(WebInspector.LogContentView.SelectedStyleClassName);
this._selectedSearchMatch = this._searchMatches[index];
this._selectedSearchMatch.highlight.classList.add(WebInspector.LogContentView.SelectedStyleClassName);
this._ensureMessageIsVisible(this._selectedSearchMatch.message);
}
}
WebInspector.LogContentView.prototype.__proto__ = WebInspector.ContentView.prototype;