summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/WebKit/Source/devtools/front_end/ui
diff options
context:
space:
mode:
authorJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-08 14:30:41 +0200
committerJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-12 13:49:54 +0200
commitab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch)
tree498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/third_party/WebKit/Source/devtools/front_end/ui
parent4ce69f7403811819800e7c5ae1318b2647e778d1 (diff)
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/third_party/WebKit/Source/devtools/front_end/ui')
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/ActionRegistry.js79
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/Checkbox.js69
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/Context.js119
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/ContextMenu.js307
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/DataGrid.js1837
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/Dialog.js218
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/DropDownMenu.js68
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/EmptyView.js57
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/InplaceEditor.js260
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/KeyboardShortcut.js291
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/PieChart.js115
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/Popover.js395
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/ProgressIndicator.js125
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/ResizerWidget.js156
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/SettingsUI.js265
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/ShortcutRegistry.js214
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/ShowMoreDataGridNode.js153
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/SidebarPane.js207
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/SidebarTreeElement.js195
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js386
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/SplitView.js888
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/StackView.js73
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/StatusBarButton.js688
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js443
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/TabbedPane.js1180
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/TextEditor.js373
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js908
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/TextUtils.js169
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js911
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/View.js703
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/ViewportControl.js529
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/ZoomManager.js42
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/treeoutline.js990
33 files changed, 13413 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/ActionRegistry.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/ActionRegistry.js
new file mode 100644
index 00000000000..0cf9faf7538
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/ActionRegistry.js
@@ -0,0 +1,79 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @constructor
+ */
+WebInspector.ActionRegistry = function()
+{
+ /** @type {!StringMap.<!WebInspector.ModuleManager.Extension>} */
+ this._actionsById = new StringMap();
+ this._registerActions();
+}
+
+WebInspector.ActionRegistry.prototype = {
+ _registerActions: function()
+ {
+ WebInspector.moduleManager.extensions(WebInspector.ActionDelegate).forEach(registerExtension, this);
+
+ /**
+ * @param {!WebInspector.ModuleManager.Extension} extension
+ * @this {WebInspector.ActionRegistry}
+ */
+ function registerExtension(extension)
+ {
+ var actionId = extension.descriptor()["actionId"];
+ console.assert(actionId);
+ console.assert(!this._actionsById.get(actionId));
+ this._actionsById.put(actionId, extension);
+ }
+ },
+
+ /**
+ * @param {!Array.<string>} actionIds
+ * @param {!WebInspector.Context} context
+ * @return {!Array.<string>}
+ */
+ applicableActions: function(actionIds, context)
+ {
+ var extensions = [];
+ actionIds.forEach(function(actionId) {
+ var extension = this._actionsById.get(actionId);
+ if (extension)
+ extensions.push(extension);
+ }, this);
+ return context.applicableExtensions(extensions).values().map(function(extension) {
+ return extension.descriptor()["actionId"];
+ });
+ },
+
+ /**
+ * @param {string} actionId
+ * @return {boolean}
+ */
+ execute: function(actionId)
+ {
+ var extension = this._actionsById.get(actionId);
+ console.assert(extension, "No action found for actionId '" + actionId + "'");
+ return extension.instance().handleAction(WebInspector.context);
+ }
+}
+
+/**
+ * @interface
+ */
+WebInspector.ActionDelegate = function()
+{
+}
+
+WebInspector.ActionDelegate.prototype = {
+ /**
+ * @param {!WebInspector.Context} context
+ * @return {boolean}
+ */
+ handleAction: function(context) {}
+}
+
+/** @type {!WebInspector.ActionRegistry} */
+WebInspector.actionRegistry;
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/Checkbox.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/Checkbox.js
new file mode 100644
index 00000000000..7802c095490
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/Checkbox.js
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 Google 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.
+ */
+
+/**
+ * @constructor
+ * @param {string} label
+ * @param {string} className
+ * @param {string=} tooltip
+ */
+WebInspector.Checkbox = function(label, className, tooltip)
+{
+ this.element = document.createElement('label');
+ this._inputElement = document.createElement('input');
+ this._inputElement.type = "checkbox";
+
+ this.element.className = className;
+ this.element.appendChild(this._inputElement);
+ this.element.appendChild(document.createTextNode(label));
+ if (tooltip)
+ this.element.title = tooltip;
+}
+
+WebInspector.Checkbox.prototype = {
+ set checked(checked)
+ {
+ this._inputElement.checked = checked;
+ },
+
+ get checked()
+ {
+ return this._inputElement.checked;
+ },
+
+ addEventListener: function(listener)
+ {
+ function listenerWrapper(event)
+ {
+ if (listener)
+ listener(event);
+ event.consume();
+ return true;
+ }
+
+ this._inputElement.addEventListener("click", listenerWrapper, false);
+ this.element.addEventListener("click", listenerWrapper, false);
+ }
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/Context.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/Context.js
new file mode 100644
index 00000000000..0dd80bc98d3
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/Context.js
@@ -0,0 +1,119 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @constructor
+ */
+WebInspector.Context = function()
+{
+ this._flavors = new Map();
+ this._eventDispatchers = new Map();
+}
+
+/**
+ * @enum {string}
+ */
+WebInspector.Context.Events = {
+ FlavorChanged: "FlavorChanged"
+}
+
+WebInspector.Context.prototype = {
+ /**
+ * @param {function(new:T, ...)} flavorType
+ * @param {?T} flavorValue
+ * @template T
+ */
+ setFlavor: function(flavorType, flavorValue)
+ {
+ var value = this._flavors.get(flavorType) || null;
+ if (value === flavorValue)
+ return;
+ if (flavorValue)
+ this._flavors.put(flavorType, flavorValue);
+ else
+ this._flavors.remove(flavorType);
+
+ this._dispatchFlavorChange(flavorType, flavorValue);
+ },
+
+ /**
+ * @param {function(new:T, ...)} flavorType
+ * @param {?T} flavorValue
+ * @template T
+ */
+ _dispatchFlavorChange: function(flavorType, flavorValue)
+ {
+ var dispatcher = this._eventDispatchers.get(flavorType);
+ if (!dispatcher)
+ return;
+ dispatcher.dispatchEventToListeners(WebInspector.Context.Events.FlavorChanged, flavorValue);
+ },
+
+ /**
+ * @param {function(new:Object, ...)} flavorType
+ * @param {function(!WebInspector.Event)} listener
+ * @param {!Object=} thisObject
+ */
+ addFlavorChangeListener: function(flavorType, listener, thisObject)
+ {
+ var dispatcher = this._eventDispatchers.get(flavorType);
+ if (!dispatcher) {
+ dispatcher = new WebInspector.Object();
+ this._eventDispatchers.put(flavorType, dispatcher);
+ }
+ dispatcher.addEventListener(WebInspector.Context.Events.FlavorChanged, listener, thisObject);
+ },
+
+ /**
+ * @param {function(new:Object, ...)} flavorType
+ * @param {function(!WebInspector.Event)} listener
+ * @param {!Object=} thisObject
+ */
+ removeFlavorChangeListener: function(flavorType, listener, thisObject)
+ {
+ var dispatcher = this._eventDispatchers.get(flavorType);
+ if (!dispatcher)
+ return;
+ dispatcher.removeEventListener(WebInspector.Context.Events.FlavorChanged, listener, thisObject);
+ if (!dispatcher.hasEventListeners(WebInspector.Context.Events.FlavorChanged))
+ this._eventDispatchers.remove(flavorType);
+ },
+
+ /**
+ * @param {function(new:T, ...)} flavorType
+ * @return {?T}
+ * @template T
+ */
+ flavor: function(flavorType)
+ {
+ return this._flavors.get(flavorType) || null;
+ },
+
+ /**
+ * @return {!Array.<function(new:Object, ...)>}
+ */
+ flavors: function()
+ {
+ return this._flavors.keys();
+ },
+
+ /**
+ * @param {!Array.<!WebInspector.ModuleManager.Extension>} extensions
+ * @return {!Set.<!WebInspector.ModuleManager.Extension>}
+ */
+ applicableExtensions: function(extensions)
+ {
+ var targetExtensionSet = new Set();
+
+ var availableFlavors = Set.fromArray(this.flavors());
+ extensions.forEach(function(extension) {
+ if (WebInspector.moduleManager.isExtensionApplicableToContextTypes(extension, availableFlavors))
+ targetExtensionSet.add(extension);
+ });
+
+ return targetExtensionSet;
+ }
+}
+
+WebInspector.context = new WebInspector.Context(); \ No newline at end of file
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/ContextMenu.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/ContextMenu.js
new file mode 100644
index 00000000000..0eb53028f5e
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/ContextMenu.js
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2009 Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+/**
+ * @constructor
+ * @param {!WebInspector.ContextMenu} topLevelMenu
+ * @param {string} type
+ * @param {string=} label
+ * @param {boolean=} disabled
+ * @param {boolean=} checked
+ */
+WebInspector.ContextMenuItem = function(topLevelMenu, type, label, disabled, checked)
+{
+ this._type = type;
+ this._label = label;
+ this._disabled = disabled;
+ this._checked = checked;
+ this._contextMenu = topLevelMenu;
+ if (type === "item" || type === "checkbox")
+ this._id = topLevelMenu.nextId();
+}
+
+WebInspector.ContextMenuItem.prototype = {
+ /**
+ * @return {number}
+ */
+ id: function()
+ {
+ return this._id;
+ },
+
+ /**
+ * @return {string}
+ */
+ type: function()
+ {
+ return this._type;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isEnabled: function()
+ {
+ return !this._disabled;
+ },
+
+ /**
+ * @param {boolean} enabled
+ */
+ setEnabled: function(enabled)
+ {
+ this._disabled = !enabled;
+ },
+
+ _buildDescriptor: function()
+ {
+ switch (this._type) {
+ case "item":
+ return { type: "item", id: this._id, label: this._label, enabled: !this._disabled };
+ case "separator":
+ return { type: "separator" };
+ case "checkbox":
+ return { type: "checkbox", id: this._id, label: this._label, checked: !!this._checked, enabled: !this._disabled };
+ }
+ }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ContextMenuItem}
+ * @param {!WebInspector.ContextMenu} topLevelMenu
+ * @param {string=} label
+ * @param {boolean=} disabled
+ */
+WebInspector.ContextSubMenuItem = function(topLevelMenu, label, disabled)
+{
+ WebInspector.ContextMenuItem.call(this, topLevelMenu, "subMenu", label, disabled);
+ /** @type {!Array.<!WebInspector.ContextMenuItem>} */
+ this._items = [];
+}
+
+WebInspector.ContextSubMenuItem.prototype = {
+ /**
+ * @param {string} label
+ * @param {function(?)} handler
+ * @param {boolean=} disabled
+ * @return {!WebInspector.ContextMenuItem}
+ */
+ appendItem: function(label, handler, disabled)
+ {
+ var item = new WebInspector.ContextMenuItem(this._contextMenu, "item", label, disabled);
+ this._pushItem(item);
+ this._contextMenu._setHandler(item.id(), handler);
+ return item;
+ },
+
+ /**
+ * @param {string} label
+ * @param {boolean=} disabled
+ * @return {!WebInspector.ContextSubMenuItem}
+ */
+ appendSubMenuItem: function(label, disabled)
+ {
+ var item = new WebInspector.ContextSubMenuItem(this._contextMenu, label, disabled);
+ this._pushItem(item);
+ return item;
+ },
+
+ /**
+ * @param {string} label
+ * @param {function()} handler
+ * @param {boolean=} checked
+ * @param {boolean=} disabled
+ * @return {!WebInspector.ContextMenuItem}
+ */
+ appendCheckboxItem: function(label, handler, checked, disabled)
+ {
+ var item = new WebInspector.ContextMenuItem(this._contextMenu, "checkbox", label, disabled, checked);
+ this._pushItem(item);
+ this._contextMenu._setHandler(item.id(), handler);
+ return item;
+ },
+
+ appendSeparator: function()
+ {
+ if (this._items.length)
+ this._pendingSeparator = true;
+ },
+
+ /**
+ * @param {!WebInspector.ContextMenuItem} item
+ */
+ _pushItem: function(item)
+ {
+ if (this._pendingSeparator) {
+ this._items.push(new WebInspector.ContextMenuItem(this._contextMenu, "separator"));
+ delete this._pendingSeparator;
+ }
+ this._items.push(item);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isEmpty: function()
+ {
+ return !this._items.length;
+ },
+
+ _buildDescriptor: function()
+ {
+ var result = { type: "subMenu", label: this._label, enabled: !this._disabled, subItems: [] };
+ for (var i = 0; i < this._items.length; ++i)
+ result.subItems.push(this._items[i]._buildDescriptor());
+ return result;
+ },
+
+ __proto__: WebInspector.ContextMenuItem.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ContextSubMenuItem}
+ * @param {?Event} event
+ */
+WebInspector.ContextMenu = function(event)
+{
+ WebInspector.ContextSubMenuItem.call(this, this, "");
+ this._event = /** @type {!Event} */ (event);
+ this._handlers = {};
+ this._id = 0;
+}
+
+/**
+ * @param {boolean} useSoftMenu
+ */
+WebInspector.ContextMenu.setUseSoftMenu = function(useSoftMenu)
+{
+ WebInspector.ContextMenu._useSoftMenu = useSoftMenu;
+}
+
+WebInspector.ContextMenu.prototype = {
+ /**
+ * @return {number}
+ */
+ nextId: function()
+ {
+ return this._id++;
+ },
+
+ show: function()
+ {
+ var menuObject = this._buildDescriptor();
+
+ if (menuObject.length) {
+ WebInspector._contextMenu = this;
+ if (WebInspector.ContextMenu._useSoftMenu) {
+ var softMenu = new WebInspector.SoftContextMenu(menuObject);
+ softMenu.show(this._event);
+ } else {
+ InspectorFrontendHost.showContextMenu(this._event, menuObject);
+ }
+ this._event.consume(true);
+ }
+ },
+
+ /**
+ * @param {number} id
+ * @param {function(?)} handler
+ */
+ _setHandler: function(id, handler)
+ {
+ if (handler)
+ this._handlers[id] = handler;
+ },
+
+ _buildDescriptor: function()
+ {
+ var result = [];
+ for (var i = 0; i < this._items.length; ++i)
+ result.push(this._items[i]._buildDescriptor());
+ return result;
+ },
+
+ _itemSelected: function(id)
+ {
+ if (this._handlers[id])
+ this._handlers[id].call(this);
+ },
+
+ /**
+ * @param {!Object} target
+ */
+ appendApplicableItems: function(target)
+ {
+ WebInspector.moduleManager.extensions(WebInspector.ContextMenu.Provider, target).forEach(processProviders.bind(this));
+
+ /**
+ * @param {!WebInspector.ModuleManager.Extension} extension
+ * @this {WebInspector.ContextMenu}
+ */
+ function processProviders(extension)
+ {
+ var provider = /** @type {!WebInspector.ContextMenu.Provider} */ (extension.instance());
+ this.appendSeparator();
+ provider.appendApplicableItems(this._event, this, target);
+ this.appendSeparator();
+ }
+ },
+
+ __proto__: WebInspector.ContextSubMenuItem.prototype
+}
+
+/**
+ * @interface
+ */
+WebInspector.ContextMenu.Provider = function() {
+}
+
+WebInspector.ContextMenu.Provider.prototype = {
+ /**
+ * @param {!Event} event
+ * @param {!WebInspector.ContextMenu} contextMenu
+ * @param {!Object} target
+ */
+ appendApplicableItems: function(event, contextMenu, target) { }
+}
+
+WebInspector.contextMenuItemSelected = function(id)
+{
+ if (WebInspector._contextMenu)
+ WebInspector._contextMenu._itemSelected(id);
+}
+
+WebInspector.contextMenuCleared = function()
+{
+ // FIXME: Unfortunately, contextMenuCleared is invoked between show and item selected
+ // so we can't delete last menu object from WebInspector. Fix the contract.
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/DataGrid.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/DataGrid.js
new file mode 100644
index 00000000000..7edaf5c62d6
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/DataGrid.js
@@ -0,0 +1,1837 @@
+/*
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>} columnsArray
+ * @param {function(!WebInspector.DataGridNode, string, string, string)=} editCallback
+ * @param {function(!WebInspector.DataGridNode)=} deleteCallback
+ * @param {function()=} refreshCallback
+ * @param {function(!WebInspector.ContextMenu, !WebInspector.DataGridNode)=} contextMenuCallback
+ */
+WebInspector.DataGrid = function(columnsArray, editCallback, deleteCallback, refreshCallback, contextMenuCallback)
+{
+ WebInspector.View.call(this);
+ this.registerRequiredCSS("dataGrid.css");
+
+ this.element.className = "data-grid"; // Override
+ this.element.tabIndex = 0;
+ this.element.addEventListener("keydown", this._keyDown.bind(this), false);
+
+ this._headerTable = document.createElement("table");
+ this._headerTable.className = "header";
+ /**
+ * @type {!Object.<string, !Element>}
+ */
+ this._headerTableHeaders = {};
+
+ 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);
+
+ this._dataTable.addEventListener("contextmenu", this._contextMenuInDataTable.bind(this), true);
+
+ // FIXME: Add a createCallback which is different from editCallback and has different
+ // behavior when creating a new node.
+ if (editCallback)
+ this._dataTable.addEventListener("dblclick", this._ondblclick.bind(this), false);
+ this._editCallback = editCallback;
+ this._deleteCallback = deleteCallback;
+ this._refreshCallback = refreshCallback;
+ this._contextMenuCallback = contextMenuCallback;
+
+ this._scrollContainer = document.createElement("div");
+ this._scrollContainer.className = "data-container";
+ this._scrollContainer.appendChild(this._dataTable);
+
+ this.element.appendChild(this._headerTable);
+ this.element.appendChild(this._scrollContainer);
+
+ this._headerRow = document.createElement("tr");
+ this._headerTableColumnGroup = document.createElement("colgroup");
+ this._dataTableColumnGroup = document.createElement("colgroup");
+
+ this._fillerRow = document.createElement("tr");
+ this._fillerRow.className = "filler";
+
+ this._columnsArray = columnsArray;
+ this._visibleColumnsArray = columnsArray;
+ this._columns = {};
+
+ for (var i = 0; i < columnsArray.length; ++i) {
+ var column = columnsArray[i];
+ var columnIdentifier = column.identifier = column.id || i;
+ this._columns[columnIdentifier] = column;
+ if (column.disclosure)
+ this.disclosureColumnIdentifier = columnIdentifier;
+
+ var cell = document.createElement("th");
+ cell.className = columnIdentifier + "-column";
+ cell.columnIdentifier = columnIdentifier;
+ this._headerTableHeaders[columnIdentifier] = cell;
+
+ var div = document.createElement("div");
+ if (column.titleDOMFragment)
+ div.appendChild(column.titleDOMFragment);
+ else
+ div.textContent = column.title;
+ cell.appendChild(div);
+
+ if (column.sort) {
+ cell.classList.add("sort-" + column.sort);
+ this._sortColumnCell = cell;
+ }
+
+ if (column.sortable) {
+ cell.addEventListener("click", this._clickInHeaderCell.bind(this), false);
+ cell.classList.add("sortable");
+ }
+ }
+
+ this._headerTable.appendChild(this._headerTableColumnGroup);
+ this.headerTableBody.appendChild(this._headerRow);
+
+ this._dataTable.appendChild(this._dataTableColumnGroup);
+ this.dataTableBody.appendChild(this._fillerRow);
+
+ this._refreshHeader();
+
+ this.selectedNode = null;
+ this.expandNodesWhenArrowing = false;
+ this.setRootNode(new WebInspector.DataGridNode());
+ this.indentWidth = 15;
+ this._resizers = [];
+ this._columnWidthsInitialized = false;
+ this._cornerWidth = WebInspector.DataGrid.CornerWidth;
+}
+
+// Keep in sync with .data-grid col.corner style rule.
+WebInspector.DataGrid.CornerWidth = 14;
+
+/** @typedef {!{id: ?string, editable: boolean, longText: ?boolean, sort: !WebInspector.DataGrid.Order, sortable: boolean, align: !WebInspector.DataGrid.Align}} */
+WebInspector.DataGrid.ColumnDescriptor;
+
+WebInspector.DataGrid.Events = {
+ SelectedNode: "SelectedNode",
+ DeselectedNode: "DeselectedNode",
+ SortingChanged: "SortingChanged",
+ ColumnsResized: "ColumnsResized"
+}
+
+/** @enum {string} */
+WebInspector.DataGrid.Order = {
+ Ascending: "ascending",
+ Descending: "descending"
+}
+
+/** @enum {string} */
+WebInspector.DataGrid.Align = {
+ Center: "center",
+ Right: "right"
+}
+
+/**
+ * @param {!Array.<string>} columnNames
+ * @param {!Array.<string>} values
+ * @return {?WebInspector.DataGrid}
+ */
+WebInspector.DataGrid.createSortableDataGrid = function(columnNames, values)
+{
+ var numColumns = columnNames.length;
+ if (!numColumns)
+ return null;
+
+ var columns = [];
+ for (var i = 0; i < columnNames.length; ++i)
+ columns.push({title: columnNames[i], width: columnNames[i].length, sortable: true});
+
+ var nodes = [];
+ for (var i = 0; i < values.length / numColumns; ++i) {
+ var data = {};
+ for (var j = 0; j < columnNames.length; ++j)
+ data[j] = values[numColumns * i + j];
+
+ var node = new WebInspector.DataGridNode(data, false);
+ node.selectable = false;
+ nodes.push(node);
+ }
+
+ var dataGrid = new WebInspector.DataGrid(columns);
+ var length = nodes.length;
+ for (var i = 0; i < length; ++i)
+ dataGrid.rootNode().appendChild(nodes[i]);
+
+ dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, sortDataGrid);
+
+ function sortDataGrid()
+ {
+ var nodes = dataGrid._rootNode.children.slice();
+ var sortColumnIdentifier = dataGrid.sortColumnIdentifier();
+ var sortDirection = dataGrid.isSortOrderAscending() ? 1 : -1;
+ var columnIsNumeric = true;
+
+ for (var i = 0; i < nodes.length; i++) {
+ var value = nodes[i].data[sortColumnIdentifier];
+ value = value instanceof Node ? Number(value.textContent) : Number(value);
+ if (isNaN(value)) {
+ columnIsNumeric = false;
+ break;
+ }
+ }
+
+ function comparator(dataGridNode1, dataGridNode2)
+ {
+ var item1 = dataGridNode1.data[sortColumnIdentifier];
+ var item2 = dataGridNode2.data[sortColumnIdentifier];
+ item1 = item1 instanceof Node ? item1.textContent : String(item1);
+ item2 = item2 instanceof Node ? item2.textContent : String(item2);
+
+ var comparison;
+ if (columnIsNumeric) {
+ // Sort numbers based on comparing their values rather than a lexicographical comparison.
+ var number1 = parseFloat(item1);
+ var number2 = parseFloat(item2);
+ comparison = number1 < number2 ? -1 : (number1 > number2 ? 1 : 0);
+ } else
+ comparison = item1 < item2 ? -1 : (item1 > item2 ? 1 : 0);
+
+ return sortDirection * comparison;
+ }
+
+ nodes.sort(comparator);
+ dataGrid.rootNode().removeChildren();
+ for (var i = 0; i < nodes.length; i++)
+ dataGrid._rootNode.appendChild(nodes[i]);
+ }
+ return dataGrid;
+}
+
+WebInspector.DataGrid.prototype = {
+ _refreshHeader: function()
+ {
+ this._headerTableColumnGroup.removeChildren();
+ this._dataTableColumnGroup.removeChildren();
+ this._headerRow.removeChildren();
+ this._fillerRow.removeChildren();
+
+ for (var i = 0; i < this._visibleColumnsArray.length; ++i) {
+ var column = this._visibleColumnsArray[i];
+ var columnIdentifier = column.identifier;
+ var headerColumn = this._headerTableColumnGroup.createChild("col");
+ var dataColumn = this._dataTableColumnGroup.createChild("col");
+ if (column.width) {
+ headerColumn.style.width = column.width;
+ dataColumn.style.width = column.width;
+ }
+ this._headerRow.appendChild(this._headerTableHeaders[columnIdentifier]);
+ this._fillerRow.createChild("td", columnIdentifier + "-column");
+ }
+
+ this._headerRow.createChild("th", "corner");
+ this._fillerRow.createChild("td", "corner");
+ this._headerTableColumnGroup.createChild("col", "corner");
+ this._dataTableColumnGroup.createChild("col", "corner");
+ },
+
+ /**
+ * @param {!WebInspector.DataGridNode} rootNode
+ */
+ setRootNode: function(rootNode)
+ {
+ if (this._rootNode) {
+ this._rootNode.removeChildren();
+ this._rootNode.dataGrid = null;
+ this._rootNode._isRoot = false;
+ }
+ /** @type {!WebInspector.DataGridNode} */
+ this._rootNode = rootNode;
+ rootNode._isRoot = true;
+ rootNode.hasChildren = false;
+ rootNode._expanded = true;
+ rootNode._revealed = true;
+ rootNode.dataGrid = this;
+ },
+
+ /**
+ * @return {!WebInspector.DataGridNode}
+ */
+ rootNode: function()
+ {
+ return this._rootNode;
+ },
+
+ _ondblclick: function(event)
+ {
+ if (this._editing || this._editingNode)
+ return;
+
+ var columnIdentifier = this.columnIdentifierFromNode(event.target);
+ if (!columnIdentifier || !this._columns[columnIdentifier].editable)
+ return;
+ this._startEditing(event.target);
+ },
+
+ /**
+ * @param {!WebInspector.DataGridNode} node
+ * @param {number} cellIndex
+ */
+ _startEditingColumnOfDataGridNode: function(node, cellIndex)
+ {
+ this._editing = true;
+ /** @type {!WebInspector.DataGridNode} */
+ this._editingNode = node;
+ this._editingNode.select();
+
+ var element = this._editingNode._element.children[cellIndex];
+ WebInspector.InplaceEditor.startEditing(element, this._startEditingConfig(element));
+ window.getSelection().setBaseAndExtent(element, 0, element, 1);
+ },
+
+ _startEditing: function(target)
+ {
+ var element = target.enclosingNodeOrSelfWithNodeName("td");
+ if (!element)
+ return;
+
+ this._editingNode = this.dataGridNodeFromNode(target);
+ if (!this._editingNode) {
+ if (!this.creationNode)
+ return;
+ this._editingNode = this.creationNode;
+ }
+
+ // Force editing the 1st column when editing the creation node
+ if (this._editingNode.isCreationNode)
+ return this._startEditingColumnOfDataGridNode(this._editingNode, this._nextEditableColumn(-1));
+
+ this._editing = true;
+ WebInspector.InplaceEditor.startEditing(element, this._startEditingConfig(element));
+
+ window.getSelection().setBaseAndExtent(element, 0, element, 1);
+ },
+
+ renderInline: function()
+ {
+ this.element.classList.add("inline");
+ this._cornerWidth = 0;
+ this.updateWidths();
+ },
+
+ _startEditingConfig: function(element)
+ {
+ return new WebInspector.InplaceEditor.Config(this._editingCommitted.bind(this), this._editingCancelled.bind(this), element.textContent);
+ },
+
+ _editingCommitted: function(element, newText, oldText, context, moveDirection)
+ {
+ var columnIdentifier = this.columnIdentifierFromNode(element);
+ if (!columnIdentifier) {
+ this._editingCancelled(element);
+ return;
+ }
+ var column = this._columns[columnIdentifier];
+ var cellIndex = this._visibleColumnsArray.indexOf(column);
+ var textBeforeEditing = this._editingNode.data[columnIdentifier];
+ var currentEditingNode = this._editingNode;
+
+ /**
+ * @param {boolean} wasChange
+ * @this {WebInspector.DataGrid}
+ */
+ function moveToNextIfNeeded(wasChange) {
+ if (!moveDirection)
+ return;
+
+ if (moveDirection === "forward") {
+ var firstEditableColumn = this._nextEditableColumn(-1);
+ if (currentEditingNode.isCreationNode && cellIndex === firstEditableColumn && !wasChange)
+ return;
+
+ var nextEditableColumn = this._nextEditableColumn(cellIndex);
+ if (nextEditableColumn !== -1)
+ return this._startEditingColumnOfDataGridNode(currentEditingNode, nextEditableColumn);
+
+ var nextDataGridNode = currentEditingNode.traverseNextNode(true, null, true);
+ if (nextDataGridNode)
+ return this._startEditingColumnOfDataGridNode(nextDataGridNode, firstEditableColumn);
+ if (currentEditingNode.isCreationNode && wasChange) {
+ this.addCreationNode(false);
+ return this._startEditingColumnOfDataGridNode(this.creationNode, firstEditableColumn);
+ }
+ return;
+ }
+
+ if (moveDirection === "backward") {
+ var prevEditableColumn = this._nextEditableColumn(cellIndex, true);
+ if (prevEditableColumn !== -1)
+ return this._startEditingColumnOfDataGridNode(currentEditingNode, prevEditableColumn);
+
+ var lastEditableColumn = this._nextEditableColumn(this._visibleColumnsArray.length, true);
+ var nextDataGridNode = currentEditingNode.traversePreviousNode(true, true);
+ if (nextDataGridNode)
+ return this._startEditingColumnOfDataGridNode(nextDataGridNode, lastEditableColumn);
+ return;
+ }
+ }
+
+ if (textBeforeEditing == newText) {
+ this._editingCancelled(element);
+ moveToNextIfNeeded.call(this, false);
+ return;
+ }
+
+ // Update the text in the datagrid that we typed
+ this._editingNode.data[columnIdentifier] = newText;
+
+ // Make the callback - expects an editing node (table row), the column number that is being edited,
+ // the text that used to be there, and the new text.
+ this._editCallback(this._editingNode, columnIdentifier, textBeforeEditing, newText);
+
+ if (this._editingNode.isCreationNode)
+ this.addCreationNode(false);
+
+ this._editingCancelled(element);
+ moveToNextIfNeeded.call(this, true);
+ },
+
+ _editingCancelled: function(element)
+ {
+ delete this._editing;
+ this._editingNode = null;
+ },
+
+ /**
+ * @param {number} cellIndex
+ * @param {boolean=} moveBackward
+ * @return {number}
+ */
+ _nextEditableColumn: function(cellIndex, moveBackward)
+ {
+ var increment = moveBackward ? -1 : 1;
+ var columns = this._visibleColumnsArray;
+ for (var i = cellIndex + increment; (i >= 0) && (i < columns.length); i += increment) {
+ if (columns[i].editable)
+ return i;
+ }
+ return -1;
+ },
+
+ /**
+ * @return {?string}
+ */
+ sortColumnIdentifier: function()
+ {
+ if (!this._sortColumnCell)
+ return null;
+ return this._sortColumnCell.columnIdentifier;
+ },
+
+ /**
+ * @return {?string}
+ */
+ sortOrder: function()
+ {
+ if (!this._sortColumnCell || this._sortColumnCell.classList.contains("sort-ascending"))
+ return WebInspector.DataGrid.Order.Ascending;
+ if (this._sortColumnCell.classList.contains("sort-descending"))
+ return WebInspector.DataGrid.Order.Descending;
+ return null;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isSortOrderAscending: function()
+ {
+ return !this._sortColumnCell || this._sortColumnCell.classList.contains("sort-ascending");
+ },
+
+ 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;
+ },
+
+ /**
+ * @param {!Array.<number>} widths
+ * @param {number} minPercent
+ * @param {number=} maxPercent
+ * @return {!Array.<number>}
+ */
+ _autoSizeWidths: function(widths, minPercent, maxPercent)
+ {
+ if (minPercent)
+ minPercent = Math.min(minPercent, Math.floor(100 / widths.length));
+ var totalWidth = 0;
+ for (var i = 0; i < widths.length; ++i)
+ totalWidth += widths[i];
+ var totalPercentWidth = 0;
+ for (var i = 0; i < widths.length; ++i) {
+ var width = Math.round(100 * widths[i] / totalWidth);
+ if (minPercent && width < minPercent)
+ width = minPercent;
+ else if (maxPercent && width > maxPercent)
+ width = maxPercent;
+ totalPercentWidth += width;
+ widths[i] = width;
+ }
+ var recoupPercent = totalPercentWidth - 100;
+
+ while (minPercent && recoupPercent > 0) {
+ for (var i = 0; i < widths.length; ++i) {
+ if (widths[i] > minPercent) {
+ --widths[i];
+ --recoupPercent;
+ if (!recoupPercent)
+ break;
+ }
+ }
+ }
+
+ while (maxPercent && recoupPercent < 0) {
+ for (var i = 0; i < widths.length; ++i) {
+ if (widths[i] < maxPercent) {
+ ++widths[i];
+ ++recoupPercent;
+ if (!recoupPercent)
+ break;
+ }
+ }
+ }
+
+ return widths;
+ },
+
+ /**
+ * @param {number} minPercent
+ * @param {number=} maxPercent
+ * @param {number=} maxDescentLevel
+ */
+ autoSizeColumns: function(minPercent, maxPercent, maxDescentLevel)
+ {
+ var widths = [];
+ for (var i = 0; i < this._columnsArray.length; ++i)
+ widths.push((this._columnsArray[i].title || "").length);
+
+ maxDescentLevel = maxDescentLevel || 0;
+ var children = this._enumerateChildren(this._rootNode, [], maxDescentLevel + 1);
+ for (var i = 0; i < children.length; ++i) {
+ var node = children[i];
+ for (var j = 0; j < this._columnsArray.length; ++j) {
+ var text = node.data[this._columnsArray[j].identifier] || "";
+ if (text.length > widths[j])
+ widths[j] = text.length;
+ }
+ }
+
+ widths = this._autoSizeWidths(widths, minPercent, maxPercent);
+
+ for (var i = 0; i < this._columnsArray.length; ++i)
+ this._columnsArray[i].weight = widths[i];
+ this._columnWidthsInitialized = false;
+ this.updateWidths();
+ },
+
+ _enumerateChildren: function(rootNode, result, maxLevel)
+ {
+ if (!rootNode._isRoot)
+ result.push(rootNode);
+ if (!maxLevel)
+ return;
+ for (var i = 0; i < rootNode.children.length; ++i)
+ this._enumerateChildren(rootNode.children[i], result, maxLevel - 1);
+ return result;
+ },
+
+ onResize: function()
+ {
+ this.updateWidths();
+ },
+
+ // Updates the widths of the table, including the positions of the column
+ // resizers.
+ //
+ // IMPORTANT: This function MUST be called once after the element of the
+ // DataGrid is attached to its parent element and every subsequent time the
+ // width of the parent element is changed in order to make it possible to
+ // resize the columns.
+ //
+ // If this function is not called after the DataGrid is attached to its
+ // parent element, then the DataGrid's columns will not be resizable.
+ updateWidths: function()
+ {
+ var headerTableColumns = this._headerTableColumnGroup.children;
+
+ // Use container size to avoid changes of table width caused by change of column widths.
+ var tableWidth = this.element.offsetWidth - this._cornerWidth;
+ var numColumns = headerTableColumns.length - 1; // Do not process corner column.
+
+ // Do not attempt to use offsetes if we're not attached to the document tree yet.
+ if (!this._columnWidthsInitialized && this.element.offsetWidth) {
+ // Give all the columns initial widths now so that during a resize,
+ // when the two columns that get resized get a percent value for
+ // their widths, all the other columns already have percent values
+ // for their widths.
+ for (var i = 0; i < numColumns; i++) {
+ var columnWidth = this.headerTableBody.rows[0].cells[i].offsetWidth;
+ var column = this._visibleColumnsArray[i];
+ if (!column.weight)
+ column.weight = 100 * columnWidth / tableWidth;
+ }
+ this._columnWidthsInitialized = true;
+ }
+ this._applyColumnWeights();
+ },
+
+ /**
+ * @param {string} name
+ */
+ setName: function(name)
+ {
+ this._columnWeightsSetting = WebInspector.settings.createSetting("dataGrid-" + name + "-columnWeights", {});
+ this._loadColumnWeights();
+ },
+
+ _loadColumnWeights: function()
+ {
+ if (!this._columnWeightsSetting)
+ return;
+ var weights = this._columnWeightsSetting.get();
+ for (var i = 0; i < this._columnsArray.length; ++i) {
+ var column = this._columnsArray[i];
+ var weight = weights[column.identifier];
+ if (weight)
+ column.weight = weight;
+ }
+ this._applyColumnWeights();
+ },
+
+ _saveColumnWeights: function()
+ {
+ if (!this._columnWeightsSetting)
+ return;
+ var weights = {};
+ for (var i = 0; i < this._columnsArray.length; ++i) {
+ var column = this._columnsArray[i];
+ weights[column.identifier] = column.weight;
+ }
+ this._columnWeightsSetting.set(weights);
+ },
+
+ wasShown: function()
+ {
+ this._loadColumnWeights();
+ },
+
+ _applyColumnWeights: function()
+ {
+ var tableWidth = this.element.offsetWidth - this._cornerWidth;
+ if (tableWidth <= 0)
+ return;
+
+ var sumOfWeights = 0.0;
+ for (var i = 0; i < this._visibleColumnsArray.length; ++i)
+ sumOfWeights += this._visibleColumnsArray[i].weight;
+
+ var sum = 0;
+ var lastOffset = 0;
+
+ for (var i = 0; i < this._visibleColumnsArray.length; ++i) {
+ sum += this._visibleColumnsArray[i].weight;
+ var offset = (sum * tableWidth / sumOfWeights) | 0;
+ var width = (offset - lastOffset) + "px";
+ this._headerTableColumnGroup.children[i].style.width = width;
+ this._dataTableColumnGroup.children[i].style.width = width;
+ lastOffset = offset;
+ }
+
+ this._positionResizers();
+ this.dispatchEventToListeners(WebInspector.DataGrid.Events.ColumnsResized);
+ },
+
+ /**
+ * @param {!Object.<string, boolean>} columnsVisibility
+ */
+ setColumnsVisiblity: function(columnsVisibility)
+ {
+ this._visibleColumnsArray = [];
+ for (var i = 0; i < this._columnsArray.length; ++i) {
+ var column = this._columnsArray[i];
+ if (columnsVisibility[column.identifier])
+ this._visibleColumnsArray.push(column);
+ }
+ this._refreshHeader();
+ this._applyColumnWeights();
+ var nodes = this._enumerateChildren(this.rootNode(), [], -1);
+ for (var i = 0; i < nodes.length; ++i)
+ nodes[i].refresh();
+ },
+
+ get scrollContainer()
+ {
+ return this._scrollContainer;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isScrolledToLastRow: function()
+ {
+ return this._scrollContainer.isScrolledToBottom();
+ },
+
+ scrollToLastRow: function()
+ {
+ this._scrollContainer.scrollTop = this._scrollContainer.scrollHeight - this._scrollContainer.offsetHeight;
+ },
+
+ _positionResizers: function()
+ {
+ var headerTableColumns = this._headerTableColumnGroup.children;
+ var numColumns = headerTableColumns.length - 1; // Do not process corner column.
+ var left = [];
+ var resizers = this._resizers;
+
+ while (resizers.length > numColumns - 1)
+ resizers.pop().remove();
+
+ for (var i = 0; i < numColumns - 1; i++) {
+ // Get the width of the cell in the first (and only) row of the
+ // header table in order to determine the width of the column, since
+ // it is not possible to query a column for its width.
+ left[i] = (left[i-1] || 0) + this.headerTableBody.rows[0].cells[i].offsetWidth;
+ }
+
+ // Make n - 1 resizers for n columns.
+ for (var i = 0; i < numColumns - 1; i++) {
+ var resizer = this._resizers[i];
+ if (!resizer) {
+ // This is the first call to updateWidth, so the resizers need
+ // to be created.
+ resizer = document.createElement("div");
+ resizer.__index = i;
+ resizer.classList.add("data-grid-resizer");
+ // This resizer is associated with the column to its right.
+ WebInspector.installDragHandle(resizer, this._startResizerDragging.bind(this), this._resizerDragging.bind(this), this._endResizerDragging.bind(this), "col-resize");
+ this.element.appendChild(resizer);
+ resizers.push(resizer);
+ }
+ if (resizer.__position !== left[i]) {
+ resizer.__position = left[i];
+ resizer.style.left = left[i] + "px";
+ }
+ }
+ },
+
+ addCreationNode: function(hasChildren)
+ {
+ if (this.creationNode)
+ this.creationNode.makeNormal();
+
+ var emptyData = {};
+ for (var column in this._columns)
+ emptyData[column] = null;
+ this.creationNode = new WebInspector.CreationDataGridNode(emptyData, hasChildren);
+ this.rootNode().appendChild(this.creationNode);
+ },
+
+ sortNodes: function(comparator, reverseMode)
+ {
+ function comparatorWrapper(a, b)
+ {
+ if (a._dataGridNode._data.summaryRow)
+ return 1;
+ if (b._dataGridNode._data.summaryRow)
+ return -1;
+
+ var aDataGirdNode = a._dataGridNode;
+ var bDataGirdNode = b._dataGridNode;
+ return reverseMode ? comparator(bDataGirdNode, aDataGirdNode) : comparator(aDataGirdNode, bDataGirdNode);
+ }
+
+ var tbody = this.dataTableBody;
+ var tbodyParent = tbody.parentElement;
+ tbodyParent.removeChild(tbody);
+
+ var childNodes = tbody.childNodes;
+ var fillerRow = childNodes[childNodes.length - 1];
+
+ var sortedRows = Array.prototype.slice.call(childNodes, 0, childNodes.length - 1);
+ sortedRows.sort(comparatorWrapper);
+ var sortedRowsLength = sortedRows.length;
+
+ tbody.removeChildren();
+ var previousSiblingNode = null;
+ for (var i = 0; i < sortedRowsLength; ++i) {
+ var row = sortedRows[i];
+ var node = row._dataGridNode;
+ node.previousSibling = previousSiblingNode;
+ if (previousSiblingNode)
+ previousSiblingNode.nextSibling = node;
+ tbody.appendChild(row);
+ previousSiblingNode = node;
+ }
+ if (previousSiblingNode)
+ previousSiblingNode.nextSibling = null;
+
+ tbody.appendChild(fillerRow);
+ tbodyParent.appendChild(tbody);
+ },
+
+ _keyDown: function(event)
+ {
+ if (!this.selectedNode || event.shiftKey || event.metaKey || event.ctrlKey || this._editing)
+ return;
+
+ var handled = false;
+ var nextSelectedNode;
+ if (event.keyIdentifier === "Up" && !event.altKey) {
+ nextSelectedNode = this.selectedNode.traversePreviousNode(true);
+ while (nextSelectedNode && !nextSelectedNode.selectable)
+ nextSelectedNode = nextSelectedNode.traversePreviousNode(true);
+ handled = nextSelectedNode ? true : false;
+ } else if (event.keyIdentifier === "Down" && !event.altKey) {
+ nextSelectedNode = this.selectedNode.traverseNextNode(true);
+ while (nextSelectedNode && !nextSelectedNode.selectable)
+ nextSelectedNode = nextSelectedNode.traverseNextNode(true);
+ 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._isRoot) {
+ 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();
+ }
+ }
+ } else if (event.keyCode === 8 || event.keyCode === 46) {
+ if (this._deleteCallback) {
+ handled = true;
+ this._deleteCallback(this.selectedNode);
+ this.changeNodeAfterDeletion();
+ }
+ } else if (isEnterKey(event)) {
+ if (this._editCallback) {
+ handled = true;
+ this._startEditing(this.selectedNode._element.children[this._nextEditableColumn(-1)]);
+ }
+ }
+
+ if (nextSelectedNode) {
+ nextSelectedNode.reveal();
+ nextSelectedNode.select();
+ }
+
+ if (handled)
+ event.consume(true);
+ },
+
+ changeNodeAfterDeletion: function()
+ {
+ var nextSelectedNode = this.selectedNode.traverseNextNode(true);
+ while (nextSelectedNode && !nextSelectedNode.selectable)
+ nextSelectedNode = nextSelectedNode.traverseNextNode(true);
+
+ if (!nextSelectedNode || nextSelectedNode.isCreationNode) {
+ nextSelectedNode = this.selectedNode.traversePreviousNode(true);
+ while (nextSelectedNode && !nextSelectedNode.selectable)
+ nextSelectedNode = nextSelectedNode.traversePreviousNode(true);
+ }
+
+ if (nextSelectedNode) {
+ nextSelectedNode.reveal();
+ nextSelectedNode.select();
+ }
+ },
+
+ /**
+ * @param {!Node} target
+ * @return {?WebInspector.DataGridNode}
+ */
+ dataGridNodeFromNode: function(target)
+ {
+ var rowElement = target.enclosingNodeOrSelfWithNodeName("tr");
+ return rowElement && rowElement._dataGridNode;
+ },
+
+ /**
+ * @param {!Node} target
+ * @return {?string}
+ */
+ columnIdentifierFromNode: function(target)
+ {
+ var cellElement = target.enclosingNodeOrSelfWithNodeName("td");
+ return cellElement && cellElement.columnIdentifier_;
+ },
+
+ _clickInHeaderCell: function(event)
+ {
+ var cell = event.target.enclosingNodeOrSelfWithNodeName("th");
+ if (!cell || (typeof cell.columnIdentifier === "undefined") || !cell.classList.contains("sortable"))
+ return;
+
+ var sortOrder = WebInspector.DataGrid.Order.Ascending;
+ if ((cell === this._sortColumnCell) && this.isSortOrderAscending())
+ sortOrder = WebInspector.DataGrid.Order.Descending;
+
+ if (this._sortColumnCell)
+ this._sortColumnCell.removeMatchingStyleClasses("sort-\\w+");
+ this._sortColumnCell = cell;
+
+ cell.classList.add("sort-" + sortOrder);
+
+ this.dispatchEventToListeners(WebInspector.DataGrid.Events.SortingChanged);
+ },
+
+ /**
+ * @param {string} columnIdentifier
+ * @param {!WebInspector.DataGrid.Order} sortOrder
+ */
+ markColumnAsSortedBy: function(columnIdentifier, sortOrder)
+ {
+ if (this._sortColumnCell)
+ this._sortColumnCell.removeMatchingStyleClasses("sort-\\w+");
+ this._sortColumnCell = this._headerTableHeaders[columnIdentifier];
+ this._sortColumnCell.classList.add("sort-" + sortOrder);
+ },
+
+ /**
+ * @param {string} columnIdentifier
+ * @return {!Element}
+ */
+ headerTableHeader: function(columnIdentifier)
+ {
+ return this._headerTableHeaders[columnIdentifier];
+ },
+
+ _mouseDownInDataTable: function(event)
+ {
+ var gridNode = this.dataGridNodeFromNode(event.target);
+ if (!gridNode || !gridNode.selectable)
+ return;
+
+ if (gridNode.isEventWithinDisclosureTriangle(event))
+ return;
+
+ if (event.metaKey) {
+ if (gridNode.selected)
+ gridNode.deselect();
+ else
+ gridNode.select();
+ } else
+ gridNode.select();
+ },
+
+ _contextMenuInDataTable: function(event)
+ {
+ var contextMenu = new WebInspector.ContextMenu(event);
+
+ var gridNode = this.dataGridNodeFromNode(event.target);
+ if (this._refreshCallback && (!gridNode || gridNode !== this.creationNode))
+ contextMenu.appendItem(WebInspector.UIString("Refresh"), this._refreshCallback.bind(this));
+
+ if (gridNode && gridNode.selectable && !gridNode.isEventWithinDisclosureTriangle(event)) {
+ if (this._editCallback) {
+ if (gridNode === this.creationNode)
+ contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add new" : "Add New"), this._startEditing.bind(this, event.target));
+ else {
+ var columnIdentifier = this.columnIdentifierFromNode(event.target);
+ if (columnIdentifier && this._columns[columnIdentifier].editable)
+ contextMenu.appendItem(WebInspector.UIString("Edit \"%s\"", this._columns[columnIdentifier].title), this._startEditing.bind(this, event.target));
+ }
+ }
+ if (this._deleteCallback && gridNode !== this.creationNode)
+ contextMenu.appendItem(WebInspector.UIString("Delete"), this._deleteCallback.bind(this, gridNode));
+ if (this._contextMenuCallback)
+ this._contextMenuCallback(contextMenu, gridNode);
+ }
+
+ contextMenu.show();
+ },
+
+ _clickInDataTable: function(event)
+ {
+ var gridNode = this.dataGridNodeFromNode(event.target);
+ 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();
+ }
+ },
+
+ get resizeMethod()
+ {
+ if (typeof this._resizeMethod === "undefined")
+ return WebInspector.DataGrid.ResizeMethod.Nearest;
+ return this._resizeMethod;
+ },
+
+ set resizeMethod(method)
+ {
+ this._resizeMethod = method;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ _startResizerDragging: function(event)
+ {
+ this._currentResizer = event.target;
+ return true;
+ },
+
+ _resizerDragging: function(event)
+ {
+ var resizer = this._currentResizer;
+ if (!resizer)
+ return;
+
+ var tableWidth = this.element.offsetWidth; // Cache it early, before we invalidate layout.
+
+ // Constrain the dragpoint to be within the containing div of the
+ // datagrid.
+ var dragPoint = event.clientX - this.element.totalOffsetLeft();
+ var firstRowCells = this.headerTableBody.rows[0].cells;
+ var leftEdgeOfPreviousColumn = 0;
+ // Constrain the dragpoint to be within the space made up by the
+ // column directly to the left and the column directly to the right.
+ var leftCellIndex = resizer.__index;
+ var rightCellIndex = leftCellIndex + 1;
+ for (var i = 0; i < leftCellIndex; i++)
+ leftEdgeOfPreviousColumn += firstRowCells[i].offsetWidth;
+
+ // Differences for other resize methods
+ if (this.resizeMethod == WebInspector.DataGrid.ResizeMethod.Last) {
+ rightCellIndex = this._resizers.length;
+ } else if (this.resizeMethod == WebInspector.DataGrid.ResizeMethod.First) {
+ leftEdgeOfPreviousColumn += firstRowCells[leftCellIndex].offsetWidth - firstRowCells[0].offsetWidth;
+ leftCellIndex = 0;
+ }
+
+ var rightEdgeOfNextColumn = leftEdgeOfPreviousColumn + firstRowCells[leftCellIndex].offsetWidth + firstRowCells[rightCellIndex].offsetWidth;
+
+ // Give each column some padding so that they don't disappear.
+ var leftMinimum = leftEdgeOfPreviousColumn + this.ColumnResizePadding;
+ var rightMaximum = rightEdgeOfNextColumn - this.ColumnResizePadding;
+ if (leftMinimum > rightMaximum)
+ return;
+
+ dragPoint = Number.constrain(dragPoint, leftMinimum, rightMaximum);
+
+ var position = (dragPoint - this.CenterResizerOverBorderAdjustment);
+ resizer.__position = position;
+ resizer.style.left = position + "px";
+
+ var pxLeftColumn = (dragPoint - leftEdgeOfPreviousColumn) + "px";
+ this._headerTableColumnGroup.children[leftCellIndex].style.width = pxLeftColumn;
+ this._dataTableColumnGroup.children[leftCellIndex].style.width = pxLeftColumn;
+
+ var pxRightColumn = (rightEdgeOfNextColumn - dragPoint) + "px";
+ this._headerTableColumnGroup.children[rightCellIndex].style.width = pxRightColumn;
+ this._dataTableColumnGroup.children[rightCellIndex].style.width = pxRightColumn;
+
+ var leftColumn = this._visibleColumnsArray[leftCellIndex];
+ var rightColumn = this._visibleColumnsArray[rightCellIndex];
+ if (leftColumn.weight || rightColumn.weight) {
+ var sumOfWeights = leftColumn.weight + rightColumn.weight;
+ var delta = rightEdgeOfNextColumn - leftEdgeOfPreviousColumn;
+ leftColumn.weight = (dragPoint - leftEdgeOfPreviousColumn) * sumOfWeights / delta;
+ rightColumn.weight = (rightEdgeOfNextColumn - dragPoint) * sumOfWeights / delta;
+ }
+
+ this._positionResizers();
+ event.preventDefault();
+ this.dispatchEventToListeners(WebInspector.DataGrid.Events.ColumnsResized);
+ },
+
+ /**
+ * @param {string} columnId
+ * @return {number}
+ */
+ columnOffset: function(columnId)
+ {
+ if (!this.element.offsetWidth)
+ return 0;
+ for (var i = 1; i < this._visibleColumnsArray.length; ++i) {
+ if (columnId === this._visibleColumnsArray[i].identifier)
+ return this._resizers[i - 1].__position;
+ }
+ return 0;
+ },
+
+ _endResizerDragging: function(event)
+ {
+ this._currentResizer = null;
+ this._saveColumnWeights();
+ this.dispatchEventToListeners(WebInspector.DataGrid.Events.ColumnsResized);
+ },
+
+ /**
+ * @return {?Element}
+ */
+ defaultAttachLocation: function()
+ {
+ return this.dataTableBody.firstChild;
+ },
+
+ ColumnResizePadding: 24,
+
+ CenterResizerOverBorderAdjustment: 3,
+
+ __proto__: WebInspector.View.prototype
+}
+
+WebInspector.DataGrid.ResizeMethod = {
+ Nearest: "nearest",
+ First: "first",
+ Last: "last"
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @param {?Object.<string, *>=} data
+ * @param {boolean=} hasChildren
+ */
+WebInspector.DataGridNode = function(data, hasChildren)
+{
+ this._expanded = false;
+ this._selected = false;
+ this._shouldRefreshChildren = true;
+ /** @type {!Object.<string, *>} */
+ this._data = data || {};
+ /** @type {boolean} */
+ this.hasChildren = hasChildren || false;
+ /** @type {!Array.<!WebInspector.DataGridNode>} */
+ this.children = [];
+ this.dataGrid = null;
+ this.parent = null;
+ /** @type {?WebInspector.DataGridNode} */
+ this.previousSibling = null;
+ /** @type {?WebInspector.DataGridNode} */
+ this.nextSibling = null;
+ this.disclosureToggleWidth = 10;
+}
+
+WebInspector.DataGridNode.prototype = {
+ /** @type {boolean} */
+ selectable: true,
+
+ /** @type {boolean} */
+ _isRoot: false,
+
+ 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.classList.add("parent");
+ if (this.expanded)
+ this._element.classList.add("expanded");
+ if (this.selected)
+ this._element.classList.add("selected");
+ if (this.revealed)
+ this._element.classList.add("revealed");
+
+ this.createCells();
+ this._element.createChild("td", "corner");
+
+ return this._element;
+ },
+
+ createCells: function()
+ {
+ var columnsArray = this.dataGrid._visibleColumnsArray;
+ for (var i = 0; i < columnsArray.length; ++i) {
+ var cell = this.createCell(columnsArray[i].identifier);
+ this._element.appendChild(cell);
+ }
+ },
+
+ 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._isRoot) {
+ if (!currentAncestor.expanded) {
+ this._revealed = false;
+ return false;
+ }
+
+ currentAncestor = currentAncestor.parent;
+ }
+
+ this._revealed = true;
+ return true;
+ },
+
+ set hasChildren(x)
+ {
+ if (this._hasChildren === x)
+ return;
+
+ this._hasChildren = x;
+
+ if (!this._element)
+ return;
+
+ this._element.classList.toggle("parent", this._hasChildren);
+ this._element.classList.toggle("expanded", this._hasChildren && this.expanded);
+ },
+
+ get hasChildren()
+ {
+ return this._hasChildren;
+ },
+
+ set revealed(x)
+ {
+ if (this._revealed === x)
+ return;
+
+ this._revealed = x;
+
+ if (this._element)
+ this._element.classList.toggle("revealed", this._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._isRoot)
+ this._depth = this.parent.depth + 1;
+ else
+ this._depth = 0;
+ return this._depth;
+ },
+
+ get leftPadding()
+ {
+ if (typeof this._leftPadding === "number")
+ return this._leftPadding;
+
+ this._leftPadding = this.depth * this.dataGrid.indentWidth;
+ return this._leftPadding;
+ },
+
+ 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;
+ },
+
+ /**
+ * @param {boolean} x
+ */
+ set expanded(x)
+ {
+ if (x)
+ this.expand();
+ else
+ this.collapse();
+ },
+
+ refresh: function()
+ {
+ if (!this._element || !this.dataGrid)
+ return;
+
+ this._element.removeChildren();
+ this.createCells();
+ this._element.createChild("td", "corner");
+ },
+
+ /**
+ * @param {string} columnIdentifier
+ * @return {!Element}
+ */
+ createTD: function(columnIdentifier)
+ {
+ var cell = document.createElement("td");
+ cell.className = columnIdentifier + "-column";
+ cell.columnIdentifier_ = columnIdentifier;
+
+ var alignment = this.dataGrid._columns[columnIdentifier].align;
+ if (alignment)
+ cell.classList.add(alignment);
+
+ return cell;
+ },
+
+ /**
+ * @param {string} columnIdentifier
+ * @return {!Element}
+ */
+ createCell: function(columnIdentifier)
+ {
+ var cell = this.createTD(columnIdentifier);
+
+ var data = this.data[columnIdentifier];
+ if (data instanceof Node) {
+ cell.appendChild(data);
+ } else {
+ cell.textContent = data;
+ if (this.dataGrid._columns[columnIdentifier].longText)
+ cell.title = data;
+ }
+
+ if (columnIdentifier === this.dataGrid.disclosureColumnIdentifier) {
+ cell.classList.add("disclosure");
+ if (this.leftPadding)
+ cell.style.setProperty("padding-left", this.leftPadding + "px");
+ }
+
+ return cell;
+ },
+
+ /**
+ * @return {number}
+ */
+ nodeSelfHeight: function()
+ {
+ return 16;
+ },
+
+ /**
+ * @param {!WebInspector.DataGridNode} child
+ */
+ appendChild: function(child)
+ {
+ this.insertChild(child, this.children.length);
+ },
+
+ /**
+ * @param {!WebInspector.DataGridNode} child
+ * @param {number} index
+ */
+ insertChild: function(child, index)
+ {
+ if (!child)
+ throw("insertChild: Node can't be undefined or null.");
+ if (child.parent === this)
+ throw("insertChild: Node is already a child of this node.");
+
+ if (child.parent)
+ child.parent.removeChild(child);
+
+ this.children.splice(index, 0, child);
+ this.hasChildren = true;
+
+ child.parent = this;
+ child.dataGrid = this.dataGrid;
+ child._recalculateSiblings(index);
+
+ delete child._depth;
+ delete child._revealed;
+ delete child._attached;
+ child._shouldRefreshChildren = true;
+
+ var current = child.children[0];
+ while (current) {
+ current.dataGrid = this.dataGrid;
+ delete current._depth;
+ delete current._revealed;
+ delete current._attached;
+ current._shouldRefreshChildren = true;
+ current = current.traverseNextNode(false, child, true);
+ }
+
+ if (this.expanded)
+ child._attach();
+ if (!this.revealed)
+ child.revealed = false;
+ },
+
+ /**
+ * @param {!WebInspector.DataGridNode} child
+ */
+ removeChild: function(child)
+ {
+ if (!child)
+ throw("removeChild: Node can't be undefined or null.");
+ if (child.parent !== this)
+ throw("removeChild: Node is not a child of this node.");
+
+ child.deselect();
+ child._detach();
+
+ 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;
+
+ if (this.children.length <= 0)
+ this.hasChildren = false;
+ },
+
+ 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 = [];
+ this.hasChildren = false;
+ },
+
+ /**
+ * @param {number} myIndex
+ */
+ _recalculateSiblings: function(myIndex)
+ {
+ if (!this.parent)
+ return;
+
+ var previousChild = this.parent.children[myIndex - 1] || null;
+ if (previousChild)
+ previousChild.nextSibling = this;
+ this.previousSibling = previousChild;
+
+ var nextChild = this.parent.children[myIndex + 1] || null;
+ if (nextChild)
+ nextChild.previousSibling = this;
+ this.nextSibling = nextChild;
+ },
+
+ collapse: function()
+ {
+ if (this._isRoot)
+ return;
+ if (this._element)
+ this._element.classList.remove("expanded");
+
+ this._expanded = false;
+
+ for (var i = 0; i < this.children.length; ++i)
+ this.children[i].revealed = false;
+ },
+
+ collapseRecursively: function()
+ {
+ var item = this;
+ while (item) {
+ if (item.expanded)
+ item.collapse();
+ item = item.traverseNextNode(false, this, true);
+ }
+ },
+
+ populate: function() { },
+
+ expand: function()
+ {
+ if (!this.hasChildren || this.expanded)
+ return;
+ if (this._isRoot)
+ 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.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.classList.add("expanded");
+
+ this._expanded = true;
+ },
+
+ expandRecursively: function()
+ {
+ var item = this;
+ while (item) {
+ item.expand();
+ item = item.traverseNextNode(false, this);
+ }
+ },
+
+ reveal: function()
+ {
+ if (this._isRoot)
+ return;
+ var currentAncestor = this.parent;
+ while (currentAncestor && !currentAncestor._isRoot) {
+ if (!currentAncestor.expanded)
+ currentAncestor.expand();
+ currentAncestor = currentAncestor.parent;
+ }
+
+ this.element.scrollIntoViewIfNeeded(false);
+ },
+
+ /**
+ * @param {boolean=} supressSelectedEvent
+ */
+ 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.classList.add("selected");
+
+ if (!supressSelectedEvent)
+ this.dataGrid.dispatchEventToListeners(WebInspector.DataGrid.Events.SelectedNode);
+ },
+
+ revealAndSelect: function()
+ {
+ if (this._isRoot)
+ return;
+ this.reveal();
+ this.select();
+ },
+
+ /**
+ * @param {boolean=} supressDeselectedEvent
+ */
+ 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.classList.remove("selected");
+
+ if (!supressDeselectedEvent)
+ this.dataGrid.dispatchEventToListeners(WebInspector.DataGrid.Events.DeselectedNode);
+ },
+
+ /**
+ * @param {boolean} skipHidden
+ * @param {?WebInspector.DataGridNode=} stayWithin
+ * @param {boolean=} dontPopulate
+ * @param {!Object=} info
+ * @return {?WebInspector.DataGridNode}
+ */
+ traverseNextNode: function(skipHidden, stayWithin, dontPopulate, info)
+ {
+ if (!dontPopulate && this.hasChildren)
+ this.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._isRoot && !((!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;
+ },
+
+ /**
+ * @param {boolean} skipHidden
+ * @param {boolean=} dontPopulate
+ * @return {?WebInspector.DataGridNode}
+ */
+ traversePreviousNode: function(skipHidden, dontPopulate)
+ {
+ var node = (!skipHidden || this.revealed) ? this.previousSibling : null;
+ if (!dontPopulate && node && node.hasChildren)
+ node.populate();
+
+ while (node && ((!skipHidden || (node.revealed && node.expanded)) ? node.children[node.children.length - 1] : null)) {
+ if (!dontPopulate && node.hasChildren)
+ node.populate();
+ node = ((!skipHidden || (node.revealed && node.expanded)) ? node.children[node.children.length - 1] : null);
+ }
+
+ if (node)
+ return node;
+
+ if (!this.parent || this.parent._isRoot)
+ return null;
+
+ return this.parent;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isEventWithinDisclosureTriangle: function(event)
+ {
+ if (!this.hasChildren)
+ return false;
+ var cell = event.target.enclosingNodeOrSelfWithNodeName("td");
+ if (!cell.classList.contains("disclosure"))
+ return false;
+
+ var left = cell.totalOffsetLeft() + this.leftPadding;
+ 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)
+ nextNode = previousNode.element.nextSibling;
+ if (!nextNode)
+ nextNode = this.dataGrid.defaultAttachLocation();
+ 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.remove();
+
+ for (var i = 0; i < this.children.length; ++i)
+ this.children[i]._detach();
+
+ this.wasDetached();
+ },
+
+ wasDetached: function()
+ {
+ },
+
+ savePosition: function()
+ {
+ if (this._savedPosition)
+ return;
+
+ if (!this.parent)
+ throw("savePosition: Node must have a parent.");
+ this._savedPosition = {
+ parent: this.parent,
+ index: this.parent.children.indexOf(this)
+ };
+ },
+
+ restorePosition: function()
+ {
+ if (!this._savedPosition)
+ return;
+
+ if (this.parent !== this._savedPosition.parent)
+ this._savedPosition.parent.insertChild(this, this._savedPosition.index);
+
+ delete this._savedPosition;
+ },
+
+ __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.DataGridNode}
+ */
+WebInspector.CreationDataGridNode = function(data, hasChildren)
+{
+ WebInspector.DataGridNode.call(this, data, hasChildren);
+ this.isCreationNode = true;
+}
+
+WebInspector.CreationDataGridNode.prototype = {
+ makeNormal: function()
+ {
+ delete this.isCreationNode;
+ delete this.makeNormal;
+ },
+
+ __proto__: WebInspector.DataGridNode.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/Dialog.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/Dialog.js
new file mode 100644
index 00000000000..feab83ac024
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/Dialog.js
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2012 Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+/**
+ * @constructor
+ * @param {!Element} relativeToElement
+ * @param {!WebInspector.DialogDelegate} delegate
+ */
+WebInspector.Dialog = function(relativeToElement, delegate)
+{
+ this._delegate = delegate;
+ this._relativeToElement = relativeToElement;
+
+ this._glassPane = new WebInspector.GlassPane();
+ // Install glass pane capturing events.
+ this._glassPane.element.tabIndex = 0;
+ this._glassPane.element.addEventListener("focus", this._onGlassPaneFocus.bind(this), false);
+ this._glassPane.element.addEventListener("keydown", this._onGlassPaneKeyDown.bind(this), false);
+
+ this._element = this._glassPane.element.createChild("div");
+ this._element.tabIndex = 0;
+ this._element.addEventListener("focus", this._onFocus.bind(this), false);
+ this._element.addEventListener("keydown", this._onKeyDown.bind(this), false);
+ this._closeKeys = [
+ WebInspector.KeyboardShortcut.Keys.Enter.code,
+ WebInspector.KeyboardShortcut.Keys.Esc.code,
+ ];
+
+ delegate.show(this._element);
+
+ this._position();
+ this._delegate.focus();
+}
+
+/**
+ * @return {?WebInspector.Dialog}
+ */
+WebInspector.Dialog.currentInstance = function()
+{
+ return WebInspector.Dialog._instance;
+}
+
+/**
+ * @param {!Element} relativeToElement
+ * @param {!WebInspector.DialogDelegate} delegate
+ */
+WebInspector.Dialog.show = function(relativeToElement, delegate)
+{
+ if (WebInspector.Dialog._instance)
+ return;
+ WebInspector.Dialog._instance = new WebInspector.Dialog(relativeToElement, delegate);
+}
+
+WebInspector.Dialog.hide = function()
+{
+ if (!WebInspector.Dialog._instance)
+ return;
+ WebInspector.Dialog._instance._hide();
+}
+
+WebInspector.Dialog.prototype = {
+ _hide: function()
+ {
+ if (this._isHiding)
+ return;
+ this._isHiding = true;
+
+ this._delegate.willHide();
+
+ delete WebInspector.Dialog._instance;
+ this._glassPane.dispose();
+ },
+
+ _onGlassPaneFocus: function(event)
+ {
+ this._hide();
+ },
+
+ /**
+ * @param {?Event} event
+ */
+ _onGlassPaneKeyDown: function(event)
+ {
+ var actions = WebInspector.shortcutRegistry.applicableActions(WebInspector.KeyboardShortcut.makeKeyFromEvent(/** @type {?KeyboardEvent} */ (event)));
+ if (actions.length)
+ event.consume(true);
+ },
+
+ _onFocus: function(event)
+ {
+ this._delegate.focus();
+ },
+
+ _position: function()
+ {
+ this._delegate.position(this._element, this._relativeToElement);
+ },
+
+ _onKeyDown: function(event)
+ {
+ if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Tab.code) {
+ event.preventDefault();
+ return;
+ }
+
+ if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Enter.code)
+ this._delegate.onEnter();
+
+ if (this._closeKeys.indexOf(event.keyCode) >= 0) {
+ this._hide();
+ event.consume(true);
+ }
+ }
+};
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.DialogDelegate = function()
+{
+ /** @type {!Element} */
+ this.element;
+}
+
+WebInspector.DialogDelegate.prototype = {
+ /**
+ * @param {!Element} element
+ */
+ show: function(element)
+ {
+ element.appendChild(this.element);
+ this.element.classList.add("dialog-contents");
+ element.classList.add("dialog");
+ },
+
+ /**
+ * @param {!Element} element
+ * @param {!Element} relativeToElement
+ */
+ position: function(element, relativeToElement)
+ {
+ var container = WebInspector.Dialog._modalHostView.element;
+ var box = relativeToElement.boxInWindow(window).relativeToElement(container);
+
+ var positionX = box.x + (relativeToElement.offsetWidth - element.offsetWidth) / 2;
+ positionX = Number.constrain(positionX, 0, container.offsetWidth - element.offsetWidth);
+
+ var positionY = box.y + (relativeToElement.offsetHeight - element.offsetHeight) / 2;
+ positionY = Number.constrain(positionY, 0, container.offsetHeight - element.offsetHeight);
+
+ element.style.position = "absolute";
+ element.positionAt(positionX, positionY, container);
+ },
+
+ focus: function() { },
+
+ onEnter: function() { },
+
+ willHide: function() { },
+
+ __proto__: WebInspector.Object.prototype
+}
+
+/** @type {?WebInspector.View} */
+WebInspector.Dialog._modalHostView = null;
+
+/**
+ * @param {!WebInspector.View} view
+ */
+WebInspector.Dialog.setModalHostView = function(view)
+{
+ WebInspector.Dialog._modalHostView = view;
+};
+
+/**
+ * FIXME: make utility method in Dialog, so clients use it instead of this getter.
+ * Method should be like Dialog.showModalElement(position params, reposition callback).
+ * @return {?WebInspector.View}
+ */
+WebInspector.Dialog.modalHostView = function()
+{
+ return WebInspector.Dialog._modalHostView;
+};
+
+WebInspector.Dialog.modalHostRepositioned = function()
+{
+ if (WebInspector.Dialog._instance)
+ WebInspector.Dialog._instance._position();
+};
+
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/DropDownMenu.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/DropDownMenu.js
new file mode 100644
index 00000000000..129cb75ba85
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/DropDownMenu.js
@@ -0,0 +1,68 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.DropDownMenu = function()
+{
+ this.element = document.createElementWithClass("select", "drop-down-menu");
+ this.element.addEventListener("mousedown", this._onBeforeMouseDown.bind(this), true);
+ this.element.addEventListener("mousedown", consumeEvent, false);
+ this.element.addEventListener("change", this._onChange.bind(this), false);
+}
+
+WebInspector.DropDownMenu.Events = {
+ BeforeShow: "BeforeShow",
+ ItemSelected: "ItemSelected"
+}
+
+WebInspector.DropDownMenu.prototype = {
+ _onBeforeMouseDown: function()
+ {
+ this.dispatchEventToListeners(WebInspector.DropDownMenu.Events.BeforeShow, null);
+ },
+
+ _onChange: function()
+ {
+ var options = this.element.options;
+ var selectedOption = options[this.element.selectedIndex];
+ this.dispatchEventToListeners(WebInspector.DropDownMenu.Events.ItemSelected, selectedOption.id);
+ },
+
+ /**
+ * @param {string} id
+ * @param {string} title
+ */
+ addItem: function(id, title)
+ {
+ var option = new Option(title);
+ option.id = id;
+ this.element.appendChild(option);
+ },
+
+ /**
+ * @param {?string} id
+ */
+ selectItem: function(id)
+ {
+ var children = this.element.children;
+ for (var i = 0; i < children.length; ++i) {
+ var child = children[i];
+ if (child.id === id) {
+ this.element.selectedIndex = i;
+ return;
+ }
+ }
+ this.element.selectedIndex = -1;
+ },
+
+ clear: function()
+ {
+ this.element.removeChildren();
+ },
+
+ __proto__: WebInspector.Object.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/EmptyView.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/EmptyView.js
new file mode 100644
index 00000000000..0394ecbe78f
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/EmptyView.js
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.VBox}
+ */
+WebInspector.EmptyView = function(text)
+{
+ WebInspector.VBox.call(this);
+ this._text = text;
+}
+
+WebInspector.EmptyView.prototype = {
+ wasShown: function()
+ {
+ this.element.classList.add("empty-view");
+ this.element.textContent = this._text;
+ },
+
+ set text(text)
+ {
+ this._text = text;
+ if (this.isShowing())
+ this.element.textContent = this._text;
+ },
+
+ __proto__: WebInspector.VBox.prototype
+}
+
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/InplaceEditor.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/InplaceEditor.js
new file mode 100644
index 00000000000..d0e1c363e1c
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/InplaceEditor.js
@@ -0,0 +1,260 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @constructor
+ */
+WebInspector.InplaceEditor = function()
+{
+};
+
+/**
+ * @param {!Element} element
+ * @param {!WebInspector.InplaceEditor.Config=} config
+ * @return {?{cancel: function(), commit: function(), setWidth: function(number)}}
+ */
+WebInspector.InplaceEditor.startEditing = function(element, config)
+{
+ if (config.multiline)
+ return WebInspector.moduleManager.instance(WebInspector.InplaceEditor).startEditing(element, config);
+
+ if (!WebInspector.InplaceEditor._defaultInstance)
+ WebInspector.InplaceEditor._defaultInstance = new WebInspector.InplaceEditor();
+ return WebInspector.InplaceEditor._defaultInstance.startEditing(element, config);
+}
+
+WebInspector.InplaceEditor.prototype = {
+ /**
+ * @return {string}
+ */
+ editorContent: function(editingContext) {
+ var element = editingContext.element;
+ if (element.tagName === "INPUT" && element.type === "text")
+ return element.value;
+
+ return element.textContent;
+ },
+
+ setUpEditor: function(editingContext)
+ {
+ var element = editingContext.element;
+ element.classList.add("editing");
+
+ var oldTabIndex = element.getAttribute("tabIndex");
+ if (typeof oldTabIndex !== "number" || oldTabIndex < 0)
+ element.tabIndex = 0;
+ WebInspector.setCurrentFocusElement(element);
+ editingContext.oldTabIndex = oldTabIndex;
+ },
+
+ closeEditor: function(editingContext)
+ {
+ var element = editingContext.element;
+ element.classList.remove("editing");
+
+ if (typeof editingContext.oldTabIndex !== "number")
+ element.removeAttribute("tabIndex");
+ else
+ element.tabIndex = editingContext.oldTabIndex;
+ element.scrollTop = 0;
+ element.scrollLeft = 0;
+ },
+
+ cancelEditing: function(editingContext)
+ {
+ var element = editingContext.element;
+ if (element.tagName === "INPUT" && element.type === "text")
+ element.value = editingContext.oldText;
+ else
+ element.textContent = editingContext.oldText;
+ },
+
+ augmentEditingHandle: function(editingContext, handle)
+ {
+ },
+
+ /**
+ * @param {!Element} element
+ * @param {!WebInspector.InplaceEditor.Config=} config
+ * @return {?{cancel: function(), commit: function()}}
+ */
+ startEditing: function(element, config)
+ {
+ if (!WebInspector.markBeingEdited(element, true))
+ return null;
+
+ config = config || new WebInspector.InplaceEditor.Config(function() {}, function() {});
+ var editingContext = { element: element, config: config };
+ var committedCallback = config.commitHandler;
+ var cancelledCallback = config.cancelHandler;
+ var pasteCallback = config.pasteHandler;
+ var context = config.context;
+ var isMultiline = config.multiline || false;
+ var moveDirection = "";
+ var self = this;
+
+ /**
+ * @param {?Event} e
+ */
+ function consumeCopy(e)
+ {
+ e.consume();
+ }
+
+ this.setUpEditor(editingContext);
+
+ editingContext.oldText = isMultiline ? config.initialValue : this.editorContent(editingContext);
+
+ /**
+ * @param {?Event=} e
+ */
+ function blurEventListener(e) {
+ if (!isMultiline || !e || !e.relatedTarget || !e.relatedTarget.isSelfOrDescendant(element))
+ editingCommitted.call(element);
+ }
+
+ function cleanUpAfterEditing()
+ {
+ WebInspector.markBeingEdited(element, false);
+
+ element.removeEventListener("blur", blurEventListener, isMultiline);
+ element.removeEventListener("keydown", keyDownEventListener, true);
+ if (pasteCallback)
+ element.removeEventListener("paste", pasteEventListener, true);
+
+ WebInspector.restoreFocusFromElement(element);
+ self.closeEditor(editingContext);
+ }
+
+ /** @this {Element} */
+ function editingCancelled()
+ {
+ self.cancelEditing(editingContext);
+ cleanUpAfterEditing();
+ cancelledCallback(this, context);
+ }
+
+ /** @this {Element} */
+ function editingCommitted()
+ {
+ cleanUpAfterEditing();
+
+ committedCallback(this, self.editorContent(editingContext), editingContext.oldText, context, moveDirection);
+ }
+
+ function defaultFinishHandler(event)
+ {
+ var isMetaOrCtrl = WebInspector.isMac() ?
+ event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey :
+ event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey;
+ if (isEnterKey(event) && (event.isMetaOrCtrlForTest || !isMultiline || isMetaOrCtrl))
+ return "commit";
+ else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code || event.keyIdentifier === "U+001B")
+ return "cancel";
+ else if (!isMultiline && event.keyIdentifier === "U+0009") // Tab key
+ return "move-" + (event.shiftKey ? "backward" : "forward");
+ }
+
+ function handleEditingResult(result, event)
+ {
+ if (result === "commit") {
+ editingCommitted.call(element);
+ event.consume(true);
+ } else if (result === "cancel") {
+ editingCancelled.call(element);
+ event.consume(true);
+ } else if (result && result.startsWith("move-")) {
+ moveDirection = result.substring(5);
+ if (event.keyIdentifier !== "U+0009")
+ blurEventListener();
+ }
+ }
+
+ function pasteEventListener(event)
+ {
+ var result = pasteCallback(event);
+ handleEditingResult(result, event);
+ }
+
+ function keyDownEventListener(event)
+ {
+ var handler = config.customFinishHandler || defaultFinishHandler;
+ var result = handler(event);
+ handleEditingResult(result, event);
+ }
+
+ element.addEventListener("blur", blurEventListener, isMultiline);
+ element.addEventListener("keydown", keyDownEventListener, true);
+ if (pasteCallback)
+ element.addEventListener("paste", pasteEventListener, true);
+
+ var handle = {
+ cancel: editingCancelled.bind(element),
+ commit: editingCommitted.bind(element)
+ };
+ this.augmentEditingHandle(editingContext, handle);
+ return handle;
+ }
+}
+
+/**
+ * @constructor
+ * @param {function(!Element,string,string,T,string)} commitHandler
+ * @param {function(!Element,T)} cancelHandler
+ * @param {T=} context
+ * @template T
+ */
+WebInspector.InplaceEditor.Config = function(commitHandler, cancelHandler, context)
+{
+ this.commitHandler = commitHandler;
+ this.cancelHandler = cancelHandler
+ this.context = context;
+
+ /**
+ * Handles the "paste" event, return values are the same as those for customFinishHandler
+ * @type {function(!Element)|undefined}
+ */
+ this.pasteHandler;
+
+ /**
+ * Whether the edited element is multiline
+ * @type {boolean|undefined}
+ */
+ this.multiline;
+
+ /**
+ * Custom finish handler for the editing session (invoked on keydown)
+ * @type {function(!Element,*)|undefined}
+ */
+ this.customFinishHandler;
+}
+
+WebInspector.InplaceEditor.Config.prototype = {
+ setPasteHandler: function(pasteHandler)
+ {
+ this.pasteHandler = pasteHandler;
+ },
+
+ /**
+ * @param {string} initialValue
+ * @param {!Object} mode
+ * @param {string} theme
+ * @param {boolean=} lineWrapping
+ * @param {boolean=} smartIndent
+ */
+ setMultilineOptions: function(initialValue, mode, theme, lineWrapping, smartIndent)
+ {
+ this.multiline = true;
+ this.initialValue = initialValue;
+ this.mode = mode;
+ this.theme = theme;
+ this.lineWrapping = lineWrapping;
+ this.smartIndent = smartIndent;
+ },
+
+ setCustomFinishHandler: function(customFinishHandler)
+ {
+ this.customFinishHandler = customFinishHandler;
+ }
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/KeyboardShortcut.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/KeyboardShortcut.js
new file mode 100644
index 00000000000..0d12ed03c7c
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/KeyboardShortcut.js
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.KeyboardShortcut = function()
+{
+}
+
+/**
+ * Constants for encoding modifier key set as a bit mask.
+ * @see #_makeKeyFromCodeAndModifiers
+ */
+WebInspector.KeyboardShortcut.Modifiers = {
+ None: 0, // Constant for empty modifiers set.
+ Shift: 1,
+ Ctrl: 2,
+ Alt: 4,
+ Meta: 8, // Command key on Mac, Win key on other platforms.
+ get CtrlOrMeta()
+ {
+ // "default" command/ctrl key for platform, Command on Mac, Ctrl on other platforms
+ return WebInspector.isMac() ? this.Meta : this.Ctrl;
+ },
+ get ShiftOrOption()
+ {
+ // Shift on Mac, Alt on other platforms
+ return WebInspector.isMac() ? this.Shift : this.Alt;
+ }
+};
+
+/** @typedef {!{code: number, name: (string|!Object.<string, string>)}} */
+WebInspector.KeyboardShortcut.Key;
+
+/** @type {!Object.<string, !WebInspector.KeyboardShortcut.Key>} */
+WebInspector.KeyboardShortcut.Keys = {
+ Backspace: { code: 8, name: "\u21a4" },
+ Tab: { code: 9, name: { mac: "\u21e5", other: "Tab" } },
+ Enter: { code: 13, name: { mac: "\u21a9", other: "Enter" } },
+ Ctrl: { code: 17, name: "Ctrl" },
+ Esc: { code: 27, name: { mac: "\u238b", other: "Esc" } },
+ Space: { code: 32, name: "Space" },
+ PageUp: { code: 33, name: { mac: "\u21de", other: "PageUp" } }, // also NUM_NORTH_EAST
+ PageDown: { code: 34, name: { mac: "\u21df", other: "PageDown" } }, // also NUM_SOUTH_EAST
+ End: { code: 35, name: { mac: "\u2197", other: "End" } }, // also NUM_SOUTH_WEST
+ Home: { code: 36, name: { mac: "\u2196", other: "Home" } }, // also NUM_NORTH_WEST
+ Left: { code: 37, name: "\u2190" }, // also NUM_WEST
+ Up: { code: 38, name: "\u2191" }, // also NUM_NORTH
+ Right: { code: 39, name: "\u2192" }, // also NUM_EAST
+ Down: { code: 40, name: "\u2193" }, // also NUM_SOUTH
+ Delete: { code: 46, name: "Del" },
+ Zero: { code: 48, name: "0" },
+ H: { code: 72, name: "H" },
+ Meta: { code: 91, name: "Meta" },
+ F1: { code: 112, name: "F1" },
+ F2: { code: 113, name: "F2" },
+ F3: { code: 114, name: "F3" },
+ F4: { code: 115, name: "F4" },
+ F5: { code: 116, name: "F5" },
+ F6: { code: 117, name: "F6" },
+ F7: { code: 118, name: "F7" },
+ F8: { code: 119, name: "F8" },
+ F9: { code: 120, name: "F9" },
+ F10: { code: 121, name: "F10" },
+ F11: { code: 122, name: "F11" },
+ F12: { code: 123, name: "F12" },
+ Semicolon: { code: 186, name: ";" },
+ NumpadPlus: { code: 107, name: "Numpad +" },
+ NumpadMinus: { code: 109, name: "Numpad -" },
+ Numpad0: { code: 96, name: "Numpad 0" },
+ Plus: { code: 187, name: "+" },
+ Comma: { code: 188, name: "," },
+ Minus: { code: 189, name: "-" },
+ Period: { code: 190, name: "." },
+ Slash: { code: 191, name: "/" },
+ QuestionMark: { code: 191, name: "?" },
+ Apostrophe: { code: 192, name: "`" },
+ Tilde: { code: 192, name: "Tilde" },
+ Backslash: { code: 220, name: "\\" },
+ SingleQuote: { code: 222, name: "\'" },
+ get CtrlOrMeta()
+ {
+ // "default" command/ctrl key for platform, Command on Mac, Ctrl on other platforms
+ return WebInspector.isMac() ? this.Meta : this.Ctrl;
+ },
+};
+
+WebInspector.KeyboardShortcut.KeyBindings = {};
+
+(function() {
+ for (var key in WebInspector.KeyboardShortcut.Keys) {
+ var descriptor = WebInspector.KeyboardShortcut.Keys[key];
+ if (typeof descriptor === "object" && descriptor["code"]) {
+ var name = typeof descriptor["name"] === "string" ? descriptor["name"] : key;
+ WebInspector.KeyboardShortcut.KeyBindings[name] = { code: descriptor["code"] };
+ }
+ }
+})();
+
+/**
+ * Creates a number encoding keyCode in the lower 8 bits and modifiers mask in the higher 8 bits.
+ * It is useful for matching pressed keys.
+ *
+ * @param {number|string} keyCode The code of the key, or a character "a-z" which is converted to a keyCode value.
+ * @param {number=} modifiers Optional list of modifiers passed as additional parameters.
+ * @return {number}
+ */
+WebInspector.KeyboardShortcut.makeKey = function(keyCode, modifiers)
+{
+ if (typeof keyCode === "string")
+ keyCode = keyCode.charCodeAt(0) - (/^[a-z]/.test(keyCode) ? 32 : 0);
+ modifiers = modifiers || WebInspector.KeyboardShortcut.Modifiers.None;
+ return WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers(keyCode, modifiers);
+}
+
+/**
+ * @param {?KeyboardEvent} keyboardEvent
+ * @return {number}
+ */
+WebInspector.KeyboardShortcut.makeKeyFromEvent = function(keyboardEvent)
+{
+ var modifiers = WebInspector.KeyboardShortcut.Modifiers.None;
+ if (keyboardEvent.shiftKey)
+ modifiers |= WebInspector.KeyboardShortcut.Modifiers.Shift;
+ if (keyboardEvent.ctrlKey)
+ modifiers |= WebInspector.KeyboardShortcut.Modifiers.Ctrl;
+ if (keyboardEvent.altKey)
+ modifiers |= WebInspector.KeyboardShortcut.Modifiers.Alt;
+ if (keyboardEvent.metaKey)
+ modifiers |= WebInspector.KeyboardShortcut.Modifiers.Meta;
+
+ function keyCodeForEvent(keyboardEvent)
+ {
+ // Use either a real or a synthetic keyCode (for events originating from extensions).
+ return keyboardEvent.keyCode || keyboardEvent["__keyCode"];
+ }
+ return WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers(keyCodeForEvent(keyboardEvent), modifiers);
+}
+
+/**
+ * @param {?KeyboardEvent} event
+ * @return {boolean}
+ */
+WebInspector.KeyboardShortcut.eventHasCtrlOrMeta = function(event)
+{
+ return WebInspector.isMac() ? event.metaKey && !event.ctrlKey : event.ctrlKey && !event.metaKey;
+}
+
+/**
+ * @param {?Event} event
+ * @return {boolean}
+ */
+WebInspector.KeyboardShortcut.hasNoModifiers = function(event)
+{
+ return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
+}
+
+/** @typedef {!{key: number, name: string}} */
+WebInspector.KeyboardShortcut.Descriptor;
+
+/**
+ * @param {string|!WebInspector.KeyboardShortcut.Key} key
+ * @param {number=} modifiers
+ * @return {!WebInspector.KeyboardShortcut.Descriptor}
+ */
+WebInspector.KeyboardShortcut.makeDescriptor = function(key, modifiers)
+{
+ return {
+ key: WebInspector.KeyboardShortcut.makeKey(typeof key === "string" ? key : key.code, modifiers),
+ name: WebInspector.KeyboardShortcut.shortcutToString(key, modifiers)
+ };
+}
+
+/**
+ * @param {string} shortcut
+ * @return {number}
+ */
+WebInspector.KeyboardShortcut.makeKeyFromBindingShortcut = function(shortcut)
+{
+ var parts = shortcut.split(/\+(?!$)/);
+ var modifiers = 0;
+ for (var i = 0; i < parts.length; ++i) {
+ if (typeof WebInspector.KeyboardShortcut.Modifiers[parts[i]] !== "undefined") {
+ modifiers |= WebInspector.KeyboardShortcut.Modifiers[parts[i]];
+ continue;
+ }
+ console.assert(i === parts.length - 1, "Modifiers-only shortcuts are not allowed (encountered <" + shortcut + ">)");
+ var key = WebInspector.KeyboardShortcut.Keys[parts[i]] || WebInspector.KeyboardShortcut.KeyBindings[parts[i]];
+ if (key && key.shiftKey)
+ modifiers |= WebInspector.KeyboardShortcut.Modifiers.Shift;
+ return WebInspector.KeyboardShortcut.makeKey(key ? key.code : parts[i].toLowerCase(), modifiers)
+ }
+ console.assert(false);
+ return 0;
+}
+
+/**
+ * @param {string|!WebInspector.KeyboardShortcut.Key} key
+ * @param {number=} modifiers
+ * @return {string}
+ */
+WebInspector.KeyboardShortcut.shortcutToString = function(key, modifiers)
+{
+ return WebInspector.KeyboardShortcut._modifiersToString(modifiers) + WebInspector.KeyboardShortcut._keyName(key);
+}
+
+/**
+ * @param {string|!WebInspector.KeyboardShortcut.Key} key
+ * @return {string}
+ */
+WebInspector.KeyboardShortcut._keyName = function(key)
+{
+ if (typeof key === "string")
+ return key.toUpperCase();
+ if (typeof key.name === "string")
+ return key.name;
+ return key.name[WebInspector.platform()] || key.name.other || '';
+}
+
+/**
+ * @param {number} keyCode
+ * @param {?number} modifiers
+ * @return {number}
+ */
+WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers = function(keyCode, modifiers)
+{
+ return (keyCode & 255) | (modifiers << 8);
+};
+
+/**
+ * @param {number} key
+ * @return {!{keyCode: number, modifiers: number}}
+ */
+WebInspector.KeyboardShortcut.keyCodeAndModifiersFromKey = function(key)
+{
+ return { keyCode: key & 255, modifiers: key >> 8 };
+}
+
+/**
+ * @param {number|undefined} modifiers
+ * @return {string}
+ */
+WebInspector.KeyboardShortcut._modifiersToString = function(modifiers)
+{
+ const cmdKey = "\u2318";
+ const optKey = "\u2325";
+ const shiftKey = "\u21e7";
+ const ctrlKey = "\u2303";
+
+ var isMac = WebInspector.isMac();
+ var res = "";
+ if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Ctrl)
+ res += isMac ? ctrlKey : "Ctrl + ";
+ if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Alt)
+ res += isMac ? optKey : "Alt + ";
+ if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Shift)
+ res += isMac ? shiftKey : "Shift + ";
+ if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Meta)
+ res += isMac ? cmdKey : "Win + ";
+
+ return res;
+};
+
+WebInspector.KeyboardShortcut.SelectAll = WebInspector.KeyboardShortcut.makeKey("a", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta);
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/PieChart.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/PieChart.js
new file mode 100644
index 00000000000..8cacabdd920
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/PieChart.js
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2013 Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+/**
+ * @constructor
+ * @param {number=} totalValue
+ * @param {function(number):string=} formatter
+ */
+WebInspector.PieChart = function(totalValue, formatter)
+{
+ const shadowOffset = 0.04;
+ this.element = document.createElementWithClass("div", "pie-chart");
+ var svg = this._createSVGChild(this.element, "svg");
+ svg.setAttribute("width", "100%");
+ svg.setAttribute("height", (100 * (1 + shadowOffset)) + "%");
+ this._group = this._createSVGChild(svg, "g");
+ var shadow = this._createSVGChild(this._group, "circle");
+ shadow.setAttribute("r", 1);
+ shadow.setAttribute("cy", shadowOffset);
+ shadow.setAttribute("fill", "hsl(0,0%,70%)");
+ var background = this._createSVGChild(this._group, "circle");
+ background.setAttribute("r", 1);
+ background.setAttribute("fill", "hsl(0,0%,92%)");
+ if (totalValue) {
+ var totalString = formatter ? formatter(totalValue) : totalValue;
+ this._totalElement = this.element.createChild("div", "pie-chart-foreground");
+ this._totalElement.textContent = totalString;
+ this._totalValue = totalValue;
+ }
+ this._lastAngle = -Math.PI/2;
+ this.setSize(100);
+}
+
+WebInspector.PieChart.prototype = {
+ /**
+ * @param {number} value
+ */
+ setTotal: function(value)
+ {
+ this._totalValue = value;
+ },
+
+ /**
+ * @param {number} value
+ */
+ setSize: function(value)
+ {
+ this._group.setAttribute("transform", "scale(" + (value / 2) + ") translate(1,1)");
+ var size = value + "px";
+ this.element.style.width = size;
+ this.element.style.height = size;
+ if (this._totalElement)
+ this._totalElement.style.lineHeight = size;
+ },
+
+ /**
+ * @param {number} value
+ * @param {string} color
+ */
+ addSlice: function(value, color)
+ {
+ var sliceAngle = value / this._totalValue * 2 * Math.PI;
+ if (!isFinite(sliceAngle))
+ return;
+ sliceAngle = Math.min(sliceAngle, 2 * Math.PI * 0.9999);
+ var path = this._createSVGChild(this._group, "path");
+ var x1 = Math.cos(this._lastAngle);
+ var y1 = Math.sin(this._lastAngle);
+ this._lastAngle += sliceAngle;
+ var x2 = Math.cos(this._lastAngle);
+ var y2 = Math.sin(this._lastAngle);
+ var largeArc = sliceAngle > Math.PI ? 1 : 0;
+ path.setAttribute("d", "M0,0 L" + x1 + "," + y1 + " A1,1,0," + largeArc + ",1," + x2 + "," + y2 + " Z");
+ path.setAttribute("fill", color);
+ },
+
+ /**
+ * @param {!Element} parent
+ * @param {string} childType
+ * @return {!Element}
+ */
+ _createSVGChild: function(parent, childType)
+ {
+ var child = document.createElementNS("http://www.w3.org/2000/svg", childType);
+ parent.appendChild(child);
+ return child;
+ }
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/Popover.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/Popover.js
new file mode 100644
index 00000000000..d183d2a1c4e
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/Popover.js
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2009 Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {!WebInspector.PopoverHelper=} popoverHelper
+ */
+WebInspector.Popover = function(popoverHelper)
+{
+ WebInspector.View.call(this);
+ this.markAsRoot();
+ this.element.className = "popover custom-popup-vertical-scroll custom-popup-horizontal-scroll"; // Override
+
+ this._popupArrowElement = document.createElement("div");
+ this._popupArrowElement.className = "arrow";
+ this.element.appendChild(this._popupArrowElement);
+
+ this._contentDiv = document.createElement("div");
+ this._contentDiv.className = "content";
+ this.element.appendChild(this._contentDiv);
+
+ this._popoverHelper = popoverHelper;
+}
+
+WebInspector.Popover.prototype = {
+ /**
+ * @param {!Element} element
+ * @param {!Element|!AnchorBox} anchor
+ * @param {?number=} preferredWidth
+ * @param {?number=} preferredHeight
+ * @param {?WebInspector.Popover.Orientation=} arrowDirection
+ */
+ show: function(element, anchor, preferredWidth, preferredHeight, arrowDirection)
+ {
+ this._innerShow(null, element, anchor, preferredWidth, preferredHeight, arrowDirection);
+ },
+
+ /**
+ * @param {!WebInspector.View} view
+ * @param {!Element|!AnchorBox} anchor
+ * @param {?number=} preferredWidth
+ * @param {?number=} preferredHeight
+ */
+ showView: function(view, anchor, preferredWidth, preferredHeight)
+ {
+ this._innerShow(view, view.element, anchor, preferredWidth, preferredHeight);
+ },
+
+ /**
+ * @param {?WebInspector.View} view
+ * @param {!Element} contentElement
+ * @param {!Element|!AnchorBox} anchor
+ * @param {?number=} preferredWidth
+ * @param {?number=} preferredHeight
+ * @param {?WebInspector.Popover.Orientation=} arrowDirection
+ */
+ _innerShow: function(view, contentElement, anchor, preferredWidth, preferredHeight, arrowDirection)
+ {
+ if (this._disposed)
+ return;
+ this.contentElement = contentElement;
+
+ // This should not happen, but we hide previous popup to be on the safe side.
+ if (WebInspector.Popover._popover)
+ WebInspector.Popover._popover.detach();
+ WebInspector.Popover._popover = this;
+
+ // Temporarily attach in order to measure preferred dimensions.
+ var preferredSize = view ? view.measurePreferredSize() : this.contentElement.measurePreferredSize();
+ preferredWidth = preferredWidth || preferredSize.width;
+ preferredHeight = preferredHeight || preferredSize.height;
+
+ WebInspector.View.prototype.show.call(this, document.body);
+
+ if (view)
+ view.show(this._contentDiv);
+ else
+ this._contentDiv.appendChild(this.contentElement);
+
+ this._positionElement(anchor, preferredWidth, preferredHeight, arrowDirection);
+
+ if (this._popoverHelper) {
+ this._contentDiv.addEventListener("mousemove", this._popoverHelper._killHidePopoverTimer.bind(this._popoverHelper), true);
+ this.element.addEventListener("mouseout", this._popoverHelper._popoverMouseOut.bind(this._popoverHelper), true);
+ }
+ },
+
+ hide: function()
+ {
+ this.detach();
+ delete WebInspector.Popover._popover;
+ },
+
+ get disposed()
+ {
+ return this._disposed;
+ },
+
+ dispose: function()
+ {
+ if (this.isShowing())
+ this.hide();
+ this._disposed = true;
+ },
+
+ setCanShrink: function(canShrink)
+ {
+ this._hasFixedHeight = !canShrink;
+ this._contentDiv.classList.add("fixed-height");
+ },
+
+ /**
+ * @param {!Element|!AnchorBox} anchorElement
+ * @param {number} preferredWidth
+ * @param {number} preferredHeight
+ * @param {?WebInspector.Popover.Orientation=} arrowDirection
+ */
+ _positionElement: function(anchorElement, preferredWidth, preferredHeight, arrowDirection)
+ {
+ const borderWidth = 25;
+ const scrollerWidth = this._hasFixedHeight ? 0 : 11;
+ const arrowHeight = 15;
+ const arrowOffset = 10;
+ const borderRadius = 10;
+
+ // Skinny tooltips are not pretty, their arrow location is not nice.
+ preferredWidth = Math.max(preferredWidth, 50);
+ // Position relative to main DevTools element.
+ const container = WebInspector.Dialog.modalHostView().element;
+ const totalWidth = container.offsetWidth;
+ const totalHeight = container.offsetHeight;
+
+ var anchorBox = anchorElement instanceof AnchorBox ? anchorElement : anchorElement.boxInWindow(window);
+ anchorBox = anchorBox.relativeToElement(container);
+ var newElementPosition = { x: 0, y: 0, width: preferredWidth + scrollerWidth, height: preferredHeight };
+
+ var verticalAlignment;
+ var roomAbove = anchorBox.y;
+ var roomBelow = totalHeight - anchorBox.y - anchorBox.height;
+
+ if ((roomAbove > roomBelow) || (arrowDirection === WebInspector.Popover.Orientation.Bottom)) {
+ // Positioning above the anchor.
+ if ((anchorBox.y > newElementPosition.height + arrowHeight + borderRadius) || (arrowDirection === WebInspector.Popover.Orientation.Bottom))
+ newElementPosition.y = anchorBox.y - newElementPosition.height - arrowHeight;
+ else {
+ newElementPosition.y = borderRadius;
+ newElementPosition.height = anchorBox.y - borderRadius * 2 - arrowHeight;
+ if (this._hasFixedHeight && newElementPosition.height < preferredHeight) {
+ newElementPosition.y = borderRadius;
+ newElementPosition.height = preferredHeight;
+ }
+ }
+ verticalAlignment = WebInspector.Popover.Orientation.Bottom;
+ } else {
+ // Positioning below the anchor.
+ newElementPosition.y = anchorBox.y + anchorBox.height + arrowHeight;
+ if ((newElementPosition.y + newElementPosition.height + borderRadius >= totalHeight) && (arrowDirection !== WebInspector.Popover.Orientation.Top)) {
+ newElementPosition.height = totalHeight - borderRadius - newElementPosition.y;
+ if (this._hasFixedHeight && newElementPosition.height < preferredHeight) {
+ newElementPosition.y = totalHeight - preferredHeight - borderRadius;
+ newElementPosition.height = preferredHeight;
+ }
+ }
+ // Align arrow.
+ verticalAlignment = WebInspector.Popover.Orientation.Top;
+ }
+
+ var horizontalAlignment;
+ if (anchorBox.x + newElementPosition.width < totalWidth) {
+ newElementPosition.x = Math.max(borderRadius, anchorBox.x - borderRadius - arrowOffset);
+ horizontalAlignment = "left";
+ } else if (newElementPosition.width + borderRadius * 2 < totalWidth) {
+ newElementPosition.x = totalWidth - newElementPosition.width - borderRadius;
+ horizontalAlignment = "right";
+ // Position arrow accurately.
+ var arrowRightPosition = Math.max(0, totalWidth - anchorBox.x - anchorBox.width - borderRadius - arrowOffset);
+ arrowRightPosition += anchorBox.width / 2;
+ arrowRightPosition = Math.min(arrowRightPosition, newElementPosition.width - borderRadius - arrowOffset);
+ this._popupArrowElement.style.right = arrowRightPosition + "px";
+ } else {
+ newElementPosition.x = borderRadius;
+ newElementPosition.width = totalWidth - borderRadius * 2;
+ newElementPosition.height += scrollerWidth;
+ horizontalAlignment = "left";
+ if (verticalAlignment === WebInspector.Popover.Orientation.Bottom)
+ newElementPosition.y -= scrollerWidth;
+ // Position arrow accurately.
+ this._popupArrowElement.style.left = Math.max(0, anchorBox.x - borderRadius * 2 - arrowOffset) + "px";
+ this._popupArrowElement.style.left += anchorBox.width / 2;
+ }
+
+ this.element.className = "popover custom-popup-vertical-scroll custom-popup-horizontal-scroll " + verticalAlignment + "-" + horizontalAlignment + "-arrow";
+ this.element.positionAt(newElementPosition.x - borderWidth, newElementPosition.y - borderWidth, container);
+ this.element.style.width = newElementPosition.width + borderWidth * 2 + "px";
+ this.element.style.height = newElementPosition.height + borderWidth * 2 + "px";
+ },
+
+ __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @param {!Element} panelElement
+ * @param {function(!Element, !Event):(!Element|!AnchorBox)|undefined} getAnchor
+ * @param {function(!Element, !WebInspector.Popover):undefined} showPopover
+ * @param {function()=} onHide
+ * @param {boolean=} disableOnClick
+ */
+WebInspector.PopoverHelper = function(panelElement, getAnchor, showPopover, onHide, disableOnClick)
+{
+ this._panelElement = panelElement;
+ this._getAnchor = getAnchor;
+ this._showPopover = showPopover;
+ this._onHide = onHide;
+ this._disableOnClick = !!disableOnClick;
+ panelElement.addEventListener("mousedown", this._mouseDown.bind(this), false);
+ panelElement.addEventListener("mousemove", this._mouseMove.bind(this), false);
+ panelElement.addEventListener("mouseout", this._mouseOut.bind(this), false);
+ this.setTimeout(1000);
+}
+
+WebInspector.PopoverHelper.prototype = {
+ setTimeout: function(timeout)
+ {
+ this._timeout = timeout;
+ },
+
+ /**
+ * @param {!MouseEvent} event
+ * @return {boolean}
+ */
+ _eventInHoverElement: function(event)
+ {
+ if (!this._hoverElement)
+ return false;
+ var box = this._hoverElement instanceof AnchorBox ? this._hoverElement : this._hoverElement.boxInWindow();
+ return (box.x <= event.clientX && event.clientX <= box.x + box.width &&
+ box.y <= event.clientY && event.clientY <= box.y + box.height);
+ },
+
+ _mouseDown: function(event)
+ {
+ if (this._disableOnClick || !this._eventInHoverElement(event))
+ this.hidePopover();
+ else {
+ this._killHidePopoverTimer();
+ this._handleMouseAction(event, true);
+ }
+ },
+
+ _mouseMove: function(event)
+ {
+ // Pretend that nothing has happened.
+ if (this._eventInHoverElement(event))
+ return;
+
+ this._startHidePopoverTimer();
+ this._handleMouseAction(event, false);
+ },
+
+ _popoverMouseOut: function(event)
+ {
+ if (!this.isPopoverVisible())
+ return;
+ if (event.relatedTarget && !event.relatedTarget.isSelfOrDescendant(this._popover._contentDiv))
+ this._startHidePopoverTimer();
+ },
+
+ _mouseOut: function(event)
+ {
+ if (!this.isPopoverVisible())
+ return;
+ if (!this._eventInHoverElement(event))
+ this._startHidePopoverTimer();
+ },
+
+ _startHidePopoverTimer: function()
+ {
+ // User has 500ms (this._timeout / 2) to reach the popup.
+ if (!this._popover || this._hidePopoverTimer)
+ return;
+
+ /**
+ * @this {WebInspector.PopoverHelper}
+ */
+ function doHide()
+ {
+ this._hidePopover();
+ delete this._hidePopoverTimer;
+ }
+ this._hidePopoverTimer = setTimeout(doHide.bind(this), this._timeout / 2);
+ },
+
+ _handleMouseAction: function(event, isMouseDown)
+ {
+ this._resetHoverTimer();
+ if (event.which && this._disableOnClick)
+ return;
+ this._hoverElement = this._getAnchor(event.target, event);
+ if (!this._hoverElement)
+ return;
+ const toolTipDelay = isMouseDown ? 0 : (this._popup ? this._timeout * 0.6 : this._timeout);
+ this._hoverTimer = setTimeout(this._mouseHover.bind(this, this._hoverElement), toolTipDelay);
+ },
+
+ _resetHoverTimer: function()
+ {
+ if (this._hoverTimer) {
+ clearTimeout(this._hoverTimer);
+ delete this._hoverTimer;
+ }
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isPopoverVisible: function()
+ {
+ return !!this._popover;
+ },
+
+ hidePopover: function()
+ {
+ this._resetHoverTimer();
+ this._hidePopover();
+ },
+
+ _hidePopover: function()
+ {
+ if (!this._popover)
+ return;
+
+ if (this._onHide)
+ this._onHide();
+
+ this._popover.dispose();
+ delete this._popover;
+ this._hoverElement = null;
+ },
+
+ _mouseHover: function(element)
+ {
+ delete this._hoverTimer;
+
+ this._hidePopover();
+ this._popover = new WebInspector.Popover(this);
+ this._showPopover(element, this._popover);
+ },
+
+ _killHidePopoverTimer: function()
+ {
+ if (this._hidePopoverTimer) {
+ clearTimeout(this._hidePopoverTimer);
+ delete this._hidePopoverTimer;
+
+ // We know that we reached the popup, but we might have moved over other elements.
+ // Discard pending command.
+ this._resetHoverTimer();
+ }
+ }
+}
+
+/** @enum {string} */
+WebInspector.Popover.Orientation = {
+ Top: "top",
+ Bottom: "bottom"
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/ProgressIndicator.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/ProgressIndicator.js
new file mode 100644
index 00000000000..8831be5b9dc
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/ProgressIndicator.js
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2012 Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+/**
+ * @constructor
+ * @implements {WebInspector.Progress}
+ * @extends {WebInspector.Object}
+ */
+WebInspector.ProgressIndicator = function()
+{
+ this.element = document.createElement("div");
+ this.element.className = "progress-bar-container";
+ this._labelElement = this.element.createChild("span");
+ this._progressElement = this.element.createChild("progress");
+ this._stopButton = new WebInspector.StatusBarButton(WebInspector.UIString("Cancel"), "progress-bar-stop-button");
+ this._stopButton.addEventListener("click", this.cancel, this);
+ this.element.appendChild(this._stopButton.element);
+ this._isCanceled = false;
+ this._worked = 0;
+}
+
+WebInspector.ProgressIndicator.prototype = {
+ /**
+ * @param {!Element} parent
+ */
+ show: function(parent)
+ {
+ parent.appendChild(this.element);
+ },
+
+ hide: function()
+ {
+ var parent = this.element.parentElement;
+ if (parent)
+ parent.removeChild(this.element);
+ },
+
+ done: function()
+ {
+ if (this._isDone)
+ return;
+ this._isDone = true;
+ this.hide();
+ this.dispatchEventToListeners(WebInspector.Progress.Events.Done);
+ },
+
+ cancel: function()
+ {
+ this._isCanceled = true;
+ this.dispatchEventToListeners(WebInspector.Progress.Events.Canceled);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isCanceled: function()
+ {
+ return this._isCanceled;
+ },
+
+ /**
+ * @param {string} title
+ */
+ setTitle: function(title)
+ {
+ this._labelElement.textContent = title;
+ },
+
+ /**
+ * @param {number} totalWork
+ */
+ setTotalWork: function(totalWork)
+ {
+ this._progressElement.max = totalWork;
+ },
+
+ /**
+ * @param {number} worked
+ * @param {string=} title
+ */
+ setWorked: function(worked, title)
+ {
+ this._worked = worked;
+ this._progressElement.value = worked;
+ if (title)
+ this.setTitle(title);
+ },
+
+ /**
+ * @param {number=} worked
+ */
+ worked: function(worked)
+ {
+ this.setWorked(this._worked + (worked || 1));
+ },
+
+ __proto__: WebInspector.Object.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/ResizerWidget.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/ResizerWidget.js
new file mode 100644
index 00000000000..6e61114093c
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/ResizerWidget.js
@@ -0,0 +1,156 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.ResizerWidget = function()
+{
+ WebInspector.Object.call(this);
+
+ this._isEnabled = true;
+ this._isVertical = true;
+ this._elements = [];
+ this._installDragOnMouseDownBound = this._installDragOnMouseDown.bind(this);
+};
+
+WebInspector.ResizerWidget.Events = {
+ ResizeStart: "ResizeStart",
+ ResizeUpdate: "ResizeUpdate",
+ ResizeEnd: "ResizeEnd"
+};
+
+WebInspector.ResizerWidget.prototype = {
+ /**
+ * @return {boolean}
+ */
+ isEnabled: function()
+ {
+ return this._isEnabled;
+ },
+
+ /**
+ * @param {boolean} enabled
+ */
+ setEnabled: function(enabled)
+ {
+ this._isEnabled = enabled;
+ this._updateElementsClass();
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isVertical: function()
+ {
+ return this._isVertical;
+ },
+
+ /**
+ * Vertical widget resizes height (along y-axis).
+ * @param {boolean} vertical
+ */
+ setVertical: function(vertical)
+ {
+ this._isVertical = vertical;
+ this._updateElementsClass();
+ },
+
+ /**
+ * @return {!Array.<!Element>}
+ */
+ elements: function()
+ {
+ return this._elements.slice();
+ },
+
+ /**
+ * @param {!Element} element
+ */
+ addElement: function(element)
+ {
+ if (this._elements.indexOf(element) !== -1)
+ return;
+
+ this._elements.push(element);
+ element.addEventListener("mousedown", this._installDragOnMouseDownBound, false);
+ element.classList.toggle("ns-resizer-widget", this._isVertical && this._isEnabled);
+ element.classList.toggle("ew-resizer-widget", !this._isVertical && this._isEnabled);
+ },
+
+ /**
+ * @param {!Element} element
+ */
+ removeElement: function(element)
+ {
+ if (this._elements.indexOf(element) === -1)
+ return;
+
+ this._elements.remove(element);
+ element.removeEventListener("mousedown", this._installDragOnMouseDownBound, false);
+ element.classList.remove("ns-resizer-widget");
+ element.classList.remove("ew-resizer-widget");
+ },
+
+ _updateElementsClass: function()
+ {
+ for (var i = 0; i < this._elements.length; ++i) {
+ this._elements[i].classList.toggle("ns-resizer-widget", this._isVertical && this._isEnabled);
+ this._elements[i].classList.toggle("ew-resizer-widget", !this._isVertical && this._isEnabled);
+ }
+ },
+
+ /**
+ * @param {?Event} event
+ */
+ _installDragOnMouseDown: function(event)
+ {
+ // Only handle drags of the nodes specified.
+ if (this._elements.indexOf(event.target) === -1)
+ return false;
+ WebInspector.elementDragStart(this._dragStart.bind(this), this._drag.bind(this), this._dragEnd.bind(this), this._isVertical ? "ns-resize" : "ew-resize", event);
+ },
+
+ /**
+ * @param {!MouseEvent} event
+ * @return {boolean}
+ */
+ _dragStart: function(event)
+ {
+ if (!this._isEnabled)
+ return false;
+ this._startPosition = this._isVertical ? event.pageY : event.pageX;
+ this.dispatchEventToListeners(WebInspector.ResizerWidget.Events.ResizeStart, { startPosition: this._startPosition, currentPosition: this._startPosition });
+ return true;
+ },
+
+ /**
+ * @param {!MouseEvent} event
+ * @return {boolean}
+ */
+ _drag: function(event)
+ {
+ if (!this._isEnabled) {
+ this._dragEnd(event);
+ return true; // Cancel drag.
+ }
+
+ var position = this._isVertical ? event.pageY : event.pageX;
+ this.dispatchEventToListeners(WebInspector.ResizerWidget.Events.ResizeUpdate, { startPosition: this._startPosition, currentPosition: position, shiftKey: event.shiftKey });
+ event.preventDefault();
+ return false; // Continue drag.
+ },
+
+ /**
+ * @param {!MouseEvent} event
+ */
+ _dragEnd: function(event)
+ {
+ this.dispatchEventToListeners(WebInspector.ResizerWidget.Events.ResizeEnd);
+ delete this._startPosition;
+ },
+
+ __proto__: WebInspector.Object.prototype
+};
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/SettingsUI.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/SettingsUI.js
new file mode 100644
index 00000000000..5c5b3451acb
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/SettingsUI.js
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2014 Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.SettingsUI = {}
+
+/**
+ * @param {string} name
+ * @param {!WebInspector.Setting} setting
+ * @param {boolean=} omitParagraphElement
+ * @param {!Element=} inputElement
+ * @param {string=} tooltip
+ * @return {!Element}
+ */
+WebInspector.SettingsUI.createSettingCheckbox = function(name, setting, omitParagraphElement, inputElement, tooltip)
+{
+ var input = inputElement || document.createElement("input");
+ input.type = "checkbox";
+ input.name = name;
+ WebInspector.SettingsUI.bindCheckbox(input, setting);
+
+ var label = document.createElement("label");
+ label.appendChild(input);
+ label.createTextChild(name);
+ if (tooltip)
+ label.title = tooltip;
+
+ if (omitParagraphElement)
+ return label;
+
+ var p = document.createElement("p");
+ p.appendChild(label);
+ return p;
+}
+
+/**
+ * @param {!Element} input
+ * @param {!WebInspector.Setting} setting
+ */
+WebInspector.SettingsUI.bindCheckbox = function(input, setting)
+{
+ function settingChanged()
+ {
+ if (input.checked !== setting.get())
+ input.checked = setting.get();
+ }
+ setting.addChangeListener(settingChanged);
+ settingChanged();
+
+ function inputChanged()
+ {
+ if (setting.get() !== input.checked)
+ setting.set(input.checked);
+ }
+ input.addEventListener("change", inputChanged, false);
+}
+
+/**
+ * @param {string} label
+ * @param {!WebInspector.Setting} setting
+ * @param {boolean} numeric
+ * @param {number=} maxLength
+ * @param {string=} width
+ * @param {function(string):?string=} validatorCallback
+ * @param {boolean=} instant
+ * @param {boolean=} clearForZero
+ * @param {string=} placeholder
+ * @return {!Element}
+ */
+WebInspector.SettingsUI.createSettingInputField = function(label, setting, numeric, maxLength, width, validatorCallback, instant, clearForZero, placeholder)
+{
+ var p = document.createElement("p");
+ var labelElement = p.createChild("label");
+ labelElement.textContent = label;
+ var inputElement = p.createChild("input");
+ inputElement.type = "text";
+ if (numeric)
+ inputElement.className = "numeric";
+ if (maxLength)
+ inputElement.maxLength = maxLength;
+ if (width)
+ inputElement.style.width = width;
+ inputElement.placeholder = placeholder || "";
+
+ if (validatorCallback || instant) {
+ inputElement.addEventListener("change", onInput, false);
+ inputElement.addEventListener("input", onInput, false);
+ }
+ inputElement.addEventListener("keydown", onKeyDown, false);
+
+ var errorMessageLabel;
+ if (validatorCallback) {
+ errorMessageLabel = p.createChild("div");
+ errorMessageLabel.classList.add("field-error-message");
+ validate();
+ }
+
+ function onInput()
+ {
+ if (validatorCallback)
+ validate();
+ if (instant)
+ apply();
+ }
+
+ function onKeyDown(event)
+ {
+ if (isEnterKey(event))
+ apply();
+ }
+
+ function validate()
+ {
+ var error = validatorCallback(inputElement.value);
+ if (!error)
+ error = "";
+ inputElement.classList.toggle("error-input", !!error);
+ errorMessageLabel.textContent = error;
+ }
+
+ if (!instant)
+ inputElement.addEventListener("blur", apply, false);
+
+ function apply()
+ {
+ if (validatorCallback && validatorCallback(inputElement.value))
+ return;
+ setting.removeChangeListener(onSettingChange);
+ setting.set(numeric ? Number(inputElement.value) : inputElement.value);
+ setting.addChangeListener(onSettingChange);
+ }
+
+ setting.addChangeListener(onSettingChange);
+
+ function onSettingChange()
+ {
+ var value = setting.get();
+ if (clearForZero && !value)
+ value = "";
+ inputElement.value = value;
+ }
+ onSettingChange();
+
+ return p;
+}
+
+/**
+ * @param {string} name
+ * @param {!Element} element
+ * @return {!Element}
+ */
+WebInspector.SettingsUI.createCustomSetting = function(name, element)
+{
+ var p = document.createElement("p");
+ var fieldsetElement = document.createElement("fieldset");
+ fieldsetElement.createChild("label").textContent = name;
+ fieldsetElement.appendChild(element);
+ p.appendChild(fieldsetElement);
+ return p;
+}
+
+/**
+ * @param {!WebInspector.Setting} setting
+ * @return {!Element}
+ */
+WebInspector.SettingsUI.createSettingFieldset = function(setting)
+{
+ var fieldset = document.createElement("fieldset");
+ fieldset.disabled = !setting.get();
+ setting.addChangeListener(settingChanged);
+ return fieldset;
+
+ function settingChanged()
+ {
+ fieldset.disabled = !setting.get();
+ }
+}
+
+/**
+ * @param {string} text
+ * @return {?string}
+ */
+WebInspector.SettingsUI.regexValidator = function(text)
+{
+ var regex;
+ try {
+ regex = new RegExp(text);
+ } catch (e) {
+ }
+ return regex ? null : WebInspector.UIString("Invalid pattern");
+}
+
+/**
+ * Creates an input element under the parentElement with the given id and defaultText.
+ * @param {!Element} parentElement
+ * @param {string} id
+ * @param {string} defaultText
+ * @param {function(*)} eventListener
+ * @param {boolean=} numeric
+ * @param {string=} size
+ * @return {!Element} element
+ */
+WebInspector.SettingsUI.createInput = function(parentElement, id, defaultText, eventListener, numeric, size)
+{
+ var element = parentElement.createChild("input");
+ element.id = id;
+ element.type = "text";
+ element.maxLength = 12;
+ element.style.width = size || "80px";
+ element.value = defaultText;
+ element.align = "right";
+ if (numeric)
+ element.className = "numeric";
+ element.addEventListener("input", eventListener, false);
+ element.addEventListener("keydown", keyDownListener, false);
+ function keyDownListener(event)
+ {
+ if (isEnterKey(event))
+ eventListener(event);
+ }
+ return element;
+}
+
+/**
+ * @constructor
+ */
+WebInspector.UISettingDelegate = function()
+{
+}
+
+WebInspector.UISettingDelegate.prototype = {
+ /**
+ * @return {?Element}
+ */
+ settingElement: function()
+ {
+ return null;
+ }
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/ShortcutRegistry.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/ShortcutRegistry.js
new file mode 100644
index 00000000000..abe497398dc
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/ShortcutRegistry.js
@@ -0,0 +1,214 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @constructor
+ * @param {!WebInspector.ActionRegistry} actionRegistry
+ */
+WebInspector.ShortcutRegistry = function(actionRegistry)
+{
+ this._actionRegistry = actionRegistry;
+ /** @type {!StringMultimap.<string>} */
+ this._defaultKeyToActions = new StringMultimap();
+ this._registerBindings();
+}
+
+WebInspector.ShortcutRegistry.prototype = {
+ /**
+ * @param {number} key
+ * @return {!Array.<string>}
+ */
+ applicableActions: function(key)
+ {
+ return this._actionRegistry.applicableActions(this._actionIdsForKey(key), WebInspector.context);
+ },
+
+ /**
+ * @param {number} key
+ * @return {!Array.<string>}
+ */
+ _actionIdsForKey: function(key)
+ {
+ var result = new StringSet();
+ var defaults = this._defaultActionsForKey(key);
+ defaults.values().forEach(function(actionId) {
+ result.add(actionId);
+ }, this);
+
+ return result.values();
+ },
+
+ /**
+ * @param {number} key
+ * @return {!Set.<string>}
+ */
+ _defaultActionsForKey: function(key)
+ {
+ return this._defaultKeyToActions.get(String(key));
+ },
+
+ /**
+ * @param {!Array.<string>} actionIds
+ * @return {!Array.<number>}
+ */
+ keysForActions: function(actionIds)
+ {
+ var actionIdSet = actionIds.keySet();
+ var result = [];
+ this._defaultKeyToActions.keys().forEach(function(key) {
+ var actionIdsForKey = this._defaultKeyToActions.get(key);
+ actionIdsForKey.values().some(function(actionId) {
+ if (actionIdSet.hasOwnProperty(actionId)) {
+ result.push(key);
+ return true;
+ }
+ });
+ }, this);
+ return result;
+ },
+
+ /**
+ * @param {!KeyboardEvent} event
+ */
+ handleShortcut: function(event)
+ {
+ this.handleKey(WebInspector.KeyboardShortcut.makeKeyFromEvent(event), event.keyIdentifier, event);
+ },
+
+ /**
+ * @param {number} key
+ * @param {string} keyIdentifier
+ * @param {!KeyboardEvent=} event
+ */
+ handleKey: function(key, keyIdentifier, event)
+ {
+ var actionIds = this.applicableActions(key);
+
+ for (var i = 0; i < actionIds.length; ++i) {
+ var keyModifiers = key >> 8;
+ if (!isPossiblyInputKey()) {
+ if (handler.call(this, actionIds[i]))
+ break;
+ } else {
+ this._pendingActionTimer = setTimeout(handler.bind(this, actionIds[i]), 0);
+ break;
+ }
+ }
+
+ /**
+ * @return {boolean}
+ */
+ function isPossiblyInputKey()
+ {
+ if (!event || !WebInspector.isBeingEdited(/** @type {!Node} */ (event.target)) || /^F\d+|Control|Shift|Alt|Meta|Win|U\+001B$/.test(keyIdentifier))
+ return false;
+
+ if (!keyModifiers)
+ return true;
+
+ var modifiers = WebInspector.KeyboardShortcut.Modifiers;
+ if ((keyModifiers & (modifiers.Ctrl | modifiers.Alt)) === (modifiers.Ctrl | modifiers.Alt))
+ return WebInspector.isWin();
+
+ return !hasModifier(modifiers.Ctrl) && !hasModifier(modifiers.Alt) && !hasModifier(modifiers.Meta);
+ }
+
+ /**
+ * @param {number} mod
+ * @return {boolean}
+ */
+ function hasModifier(mod)
+ {
+ return !!(keyModifiers & mod);
+ }
+
+ /**
+ * @param {string} actionId
+ * @return {boolean}
+ * @this {WebInspector.ShortcutRegistry}
+ */
+ function handler(actionId)
+ {
+ var result = this._actionRegistry.execute(actionId);
+ if (result && event)
+ event.consume(true);
+ delete this._pendingActionTimer;
+ return result;
+ }
+ },
+
+ /**
+ * @param {string} actionId
+ * @param {string} shortcut
+ */
+ registerShortcut: function(actionId, shortcut)
+ {
+ var key = WebInspector.KeyboardShortcut.makeKeyFromBindingShortcut(shortcut);
+ if (!key)
+ return;
+ this._defaultKeyToActions.put(String(key), actionId);
+ },
+
+ /**
+ * @param {?Event} event
+ */
+ _onInput: function(event)
+ {
+ if (this._pendingActionTimer) {
+ clearTimeout(this._pendingActionTimer);
+ delete this._pendingActionTimer;
+ }
+ },
+
+ _registerBindings: function()
+ {
+ document.addEventListener("input", this._onInput.bind(this), true);
+ var extensions = WebInspector.moduleManager.extensions(WebInspector.ActionDelegate);
+ extensions.forEach(registerExtension, this);
+
+ /**
+ * @param {!WebInspector.ModuleManager.Extension} extension
+ * @this {WebInspector.ShortcutRegistry}
+ */
+ function registerExtension(extension)
+ {
+ var descriptor = extension.descriptor();
+ var bindings = descriptor["bindings"];
+ for (var i = 0; bindings && i < bindings.length; ++i) {
+ if (!platformMatches(bindings[i].platform))
+ continue;
+ var shortcuts = bindings[i]["shortcut"].split(/\s+/);
+ shortcuts.forEach(this.registerShortcut.bind(this, descriptor["actionId"]));
+ }
+ }
+
+ /**
+ * @param {string=} platformsString
+ * @return {boolean}
+ */
+ function platformMatches(platformsString)
+ {
+ if (!platformsString)
+ return true;
+ var platforms = platformsString.split(",");
+ var isMatch = false;
+ var currentPlatform = WebInspector.platform();
+ for (var i = 0; !isMatch && i < platforms.length; ++i)
+ isMatch = platforms[i] === currentPlatform;
+ return isMatch;
+ }
+ }
+}
+
+/**
+ * @constructor
+ */
+WebInspector.ShortcutRegistry.ForwardedShortcut = function()
+{
+}
+
+WebInspector.ShortcutRegistry.ForwardedShortcut.instance = new WebInspector.ShortcutRegistry.ForwardedShortcut();
+
+/** @type {!WebInspector.ShortcutRegistry} */
+WebInspector.shortcutRegistry;
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/ShowMoreDataGridNode.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/ShowMoreDataGridNode.js
new file mode 100644
index 00000000000..e8420fce60f
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/ShowMoreDataGridNode.js
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2011 Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.DataGridNode}
+ * @param {function(number, number)} callback
+ * @param {number} startPosition
+ * @param {number} endPosition
+ * @param {number} chunkSize
+ */
+WebInspector.ShowMoreDataGridNode = function(callback, startPosition, endPosition, chunkSize)
+{
+ WebInspector.DataGridNode.call(this, {summaryRow:true}, false);
+ this._callback = callback;
+ this._startPosition = startPosition;
+ this._endPosition = endPosition;
+ this._chunkSize = chunkSize;
+
+ this.showNext = document.createElement("button");
+ this.showNext.setAttribute("type", "button");
+ this.showNext.addEventListener("click", this._showNextChunk.bind(this), false);
+ this.showNext.textContent = WebInspector.UIString("Show %d before", this._chunkSize);
+
+ this.showAll = document.createElement("button");
+ this.showAll.setAttribute("type", "button");
+ this.showAll.addEventListener("click", this._showAll.bind(this), false);
+
+ this.showLast = document.createElement("button");
+ this.showLast.setAttribute("type", "button");
+ this.showLast.addEventListener("click", this._showLastChunk.bind(this), false);
+ this.showLast.textContent = WebInspector.UIString("Show %d after", this._chunkSize);
+
+ this._updateLabels();
+ this.selectable = false;
+}
+
+WebInspector.ShowMoreDataGridNode.prototype = {
+ _showNextChunk: function()
+ {
+ this._callback(this._startPosition, this._startPosition + this._chunkSize);
+ },
+
+ _showAll: function()
+ {
+ this._callback(this._startPosition, this._endPosition);
+ },
+
+ _showLastChunk: function()
+ {
+ this._callback(this._endPosition - this._chunkSize, this._endPosition);
+ },
+
+ _updateLabels: function()
+ {
+ var totalSize = this._endPosition - this._startPosition;
+ if (totalSize > this._chunkSize) {
+ this.showNext.classList.remove("hidden");
+ this.showLast.classList.remove("hidden");
+ } else {
+ this.showNext.classList.add("hidden");
+ this.showLast.classList.add("hidden");
+ }
+ this.showAll.textContent = WebInspector.UIString("Show all %d", totalSize);
+ },
+
+ /** override */
+ createCells: function()
+ {
+ this._hasCells = false;
+ WebInspector.DataGridNode.prototype.createCells.call(this);
+ },
+
+ /**
+ * @override
+ * @param {string} columnIdentifier
+ * @return {!Element}
+ */
+ createCell: function(columnIdentifier)
+ {
+ var cell = this.createTD(columnIdentifier);
+ if (!this._hasCells) {
+ this._hasCells = true;
+ if (this.depth)
+ cell.style.setProperty("padding-left", (this.depth * this.dataGrid.indentWidth) + "px");
+ cell.appendChild(this.showNext);
+ cell.appendChild(this.showAll);
+ cell.appendChild(this.showLast);
+ }
+ return cell;
+ },
+
+ /**
+ * @param {number} from
+ */
+ setStartPosition: function(from)
+ {
+ this._startPosition = from;
+ this._updateLabels();
+ },
+
+ /**
+ * @param {number} to
+ */
+ setEndPosition: function(to)
+ {
+ this._endPosition = to;
+ this._updateLabels();
+ },
+
+ /**
+ * @override
+ * @return {number}
+ */
+ nodeSelfHeight: function()
+ {
+ return 32;
+ },
+
+ dispose: function()
+ {
+ },
+
+ __proto__: WebInspector.DataGridNode.prototype
+}
+
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/SidebarPane.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/SidebarPane.js
new file mode 100644
index 00000000000..85ac23037ad
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/SidebarPane.js
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.SidebarPane = function(title)
+{
+ WebInspector.View.call(this);
+ this.setMinimumSize(25, 0);
+ this.element.className = "sidebar-pane"; // Override
+
+ this.titleElement = document.createElement("div");
+ this.titleElement.className = "sidebar-pane-toolbar";
+
+ this.bodyElement = this.element.createChild("div", "body");
+
+ this._title = title;
+
+ this._expandCallback = null;
+}
+
+WebInspector.SidebarPane.EventTypes = {
+ wasShown: "wasShown"
+}
+
+WebInspector.SidebarPane.prototype = {
+ /**
+ * @return {string}
+ */
+ title: function()
+ {
+ return this._title;
+ },
+
+ /**
+ * @param {function()} callback
+ */
+ prepareContent: function(callback)
+ {
+ if (callback)
+ callback();
+ },
+
+ expand: function()
+ {
+ this.prepareContent(this.onContentReady.bind(this));
+ },
+
+ onContentReady: function()
+ {
+ if (this._expandCallback)
+ this._expandCallback();
+ else
+ this._expandPending = true;
+ },
+
+ /**
+ * @param {function()} callback
+ */
+ setExpandCallback: function(callback)
+ {
+ this._expandCallback = callback;
+ if (this._expandPending) {
+ delete this._expandPending;
+ this._expandCallback();
+ }
+ },
+
+ wasShown: function()
+ {
+ WebInspector.View.prototype.wasShown.call(this);
+ this.dispatchEventToListeners(WebInspector.SidebarPane.EventTypes.wasShown);
+ },
+
+ __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @param {!Element} container
+ * @param {!WebInspector.SidebarPane} pane
+ */
+WebInspector.SidebarPaneTitle = function(container, pane)
+{
+ this._pane = pane;
+
+ this.element = container.createChild("div", "sidebar-pane-title");
+ this.element.textContent = pane.title();
+ this.element.tabIndex = 0;
+ this.element.addEventListener("click", this._toggleExpanded.bind(this), false);
+ this.element.addEventListener("keydown", this._onTitleKeyDown.bind(this), false);
+ this.element.appendChild(this._pane.titleElement);
+
+ this._pane.setExpandCallback(this._expand.bind(this));
+}
+
+WebInspector.SidebarPaneTitle.prototype = {
+
+ _expand: function()
+ {
+ this.element.classList.add("expanded");
+ this._pane.show(this.element.parentElement, /** @type {?Element} */ (this.element.nextSibling));
+ },
+
+ _collapse: function()
+ {
+ this.element.classList.remove("expanded");
+ if (this._pane.element.parentNode == this.element.parentNode)
+ this._pane.detach();
+ },
+
+ _toggleExpanded: function()
+ {
+ if (this.element.classList.contains("expanded"))
+ this._collapse();
+ else
+ this._pane.expand();
+ },
+
+ /**
+ * @param {?Event} event
+ */
+ _onTitleKeyDown: function(event)
+ {
+ if (isEnterKey(event) || event.keyCode === WebInspector.KeyboardShortcut.Keys.Space.code)
+ this._toggleExpanded();
+ }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.SidebarPaneStack = function()
+{
+ WebInspector.View.call(this);
+ this.setMinimumSize(25, 0);
+ this.element.className = "sidebar-pane-stack"; // Override
+ this.registerRequiredCSS("sidebarPane.css");
+}
+
+WebInspector.SidebarPaneStack.prototype = {
+ /**
+ * @param {!WebInspector.SidebarPane} pane
+ */
+ addPane: function(pane)
+ {
+ new WebInspector.SidebarPaneTitle(this.element, pane);
+ },
+
+ __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.TabbedPane}
+ */
+WebInspector.SidebarTabbedPane = function()
+{
+ WebInspector.TabbedPane.call(this);
+ this.setRetainTabOrder(true);
+ this.element.classList.add("sidebar-tabbed-pane");
+ this.registerRequiredCSS("sidebarPane.css");
+}
+
+WebInspector.SidebarTabbedPane.prototype = {
+ /**
+ * @param {!WebInspector.SidebarPane} pane
+ */
+ addPane: function(pane)
+ {
+ var title = pane.title();
+ this.appendTab(title, title, pane);
+ pane.element.appendChild(pane.titleElement);
+ pane.setExpandCallback(this.selectTab.bind(this, title));
+
+ },
+
+ __proto__: WebInspector.TabbedPane.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/SidebarTreeElement.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/SidebarTreeElement.js
new file mode 100644
index 00000000000..77d97ae09a1
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/SidebarTreeElement.js
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {TreeElement}
+ */
+WebInspector.SidebarSectionTreeElement = function(title, representedObject, hasChildren)
+{
+ TreeElement.call(this, title.escapeHTML(), representedObject || {}, hasChildren);
+ this.expand();
+}
+
+WebInspector.SidebarSectionTreeElement.prototype = {
+ selectable: false,
+
+ collapse: function()
+ {
+ // Should not collapse as it is not selectable.
+ },
+
+ get smallChildren()
+ {
+ return this._smallChildren;
+ },
+
+ set smallChildren(x)
+ {
+ if (this._smallChildren === x)
+ return;
+
+ this._smallChildren = x;
+
+ this._childrenListNode.classList.toggle("small", this._smallChildren);
+ },
+
+ onattach: function()
+ {
+ this._listItemNode.classList.add("sidebar-tree-section");
+ },
+
+ onreveal: function()
+ {
+ if (this.listItemElement)
+ this.listItemElement.scrollIntoViewIfNeeded(false);
+ },
+
+ __proto__: TreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {TreeElement}
+ * @param {string} className
+ * @param {string} title
+ * @param {string=} subtitle
+ * @param {?Object=} representedObject
+ * @param {boolean=} hasChildren
+ */
+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";
+ }
+
+ this.iconElement = document.createElementWithClass("div", "icon");
+ this.statusElement = document.createElementWithClass("div", "status");
+ this.titlesElement = document.createElementWithClass("div", "titles");
+
+ this.titleContainer = this.titlesElement.createChild("span", "title-container");
+ this.titleElement = this.titleContainer.createChild("span", "title");
+
+ this.subtitleElement = this.titlesElement.createChild("span", "subtitle");
+
+ 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)
+ this._listItemNode.classList.toggle("small", this._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();
+ },
+
+ set wait(x)
+ {
+ this._listItemNode.classList.toggle("wait", 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.classList.remove("no-subtitle");
+ } else {
+ this.subtitleElement.textContent = "";
+ this.titlesElement.classList.add("no-subtitle");
+ }
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isEventWithinDisclosureTriangle: function(event)
+ {
+ return event.target === this.disclosureButton;
+ },
+
+ onattach: function()
+ {
+ this._listItemNode.classList.add("sidebar-tree-item");
+
+ if (this.className)
+ this._listItemNode.classList.add(this.className);
+
+ if (this.small)
+ this._listItemNode.classList.add("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);
+ },
+
+ __proto__: TreeElement.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js
new file mode 100644
index 00000000000..a4626e36b1b
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2011 Google 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.
+ */
+
+/**
+ * @constructor
+ * @param {!Array.<!WebInspector.ContextMenuItem>} items
+ * @param {!WebInspector.SoftContextMenu=} parentMenu
+ */
+WebInspector.SoftContextMenu = function(items, parentMenu)
+{
+ this._items = items;
+ this._parentMenu = parentMenu;
+}
+
+WebInspector.SoftContextMenu.prototype = {
+ /**
+ * @param {!Event} event
+ */
+ show: function(event)
+ {
+ this._x = event.x;
+ this._y = event.y;
+ this._time = new Date().getTime();
+
+ // Absolutely position menu for iframes.
+ var absoluteX = event.pageX;
+ var absoluteY = event.pageY;
+ var targetElement = event.target;
+ while (targetElement && window !== targetElement.ownerDocument.defaultView) {
+ var frameElement = targetElement.ownerDocument.defaultView.frameElement;
+ absoluteY += frameElement.totalOffsetTop();
+ absoluteX += frameElement.totalOffsetLeft();
+ targetElement = frameElement;
+ }
+
+ // Create context menu.
+ var targetRect;
+ this._contextMenuElement = document.createElement("div");
+ this._contextMenuElement.className = "soft-context-menu";
+ this._contextMenuElement.tabIndex = 0;
+ this._contextMenuElement.style.top = absoluteY + "px";
+ this._contextMenuElement.style.left = absoluteX + "px";
+
+ this._contextMenuElement.addEventListener("mouseup", consumeEvent, false);
+ this._contextMenuElement.addEventListener("keydown", this._menuKeyDown.bind(this), false);
+
+ for (var i = 0; i < this._items.length; ++i)
+ this._contextMenuElement.appendChild(this._createMenuItem(this._items[i]));
+
+ // Install glass pane capturing events.
+ if (!this._parentMenu) {
+ this._glassPaneElement = document.createElement("div");
+ this._glassPaneElement.className = "soft-context-menu-glass-pane";
+ this._glassPaneElement.tabIndex = 0;
+ this._glassPaneElement.addEventListener("mouseup", this._glassPaneMouseUp.bind(this), false);
+ this._glassPaneElement.appendChild(this._contextMenuElement);
+ document.body.appendChild(this._glassPaneElement);
+ this._focus();
+ } else
+ this._parentMenu._parentGlassPaneElement().appendChild(this._contextMenuElement);
+
+ // Re-position menu in case it does not fit.
+ if (document.body.offsetWidth < this._contextMenuElement.offsetLeft + this._contextMenuElement.offsetWidth)
+ this._contextMenuElement.style.left = (absoluteX - this._contextMenuElement.offsetWidth) + "px";
+ if (document.body.offsetHeight < this._contextMenuElement.offsetTop + this._contextMenuElement.offsetHeight)
+ this._contextMenuElement.style.top = (document.body.offsetHeight - this._contextMenuElement.offsetHeight) + "px";
+
+ event.consume(true);
+ },
+
+ _parentGlassPaneElement: function()
+ {
+ if (this._glassPaneElement)
+ return this._glassPaneElement;
+ if (this._parentMenu)
+ return this._parentMenu._parentGlassPaneElement();
+ return null;
+ },
+
+ _createMenuItem: function(item)
+ {
+ if (item.type === "separator")
+ return this._createSeparator();
+
+ if (item.type === "subMenu")
+ return this._createSubMenu(item);
+
+ var menuItemElement = document.createElement("div");
+ menuItemElement.className = "soft-context-menu-item";
+
+ var checkMarkElement = document.createElement("span");
+ checkMarkElement.textContent = "\u2713 "; // Checkmark Unicode symbol
+ checkMarkElement.className = "soft-context-menu-item-checkmark";
+ if (!item.checked)
+ checkMarkElement.style.opacity = "0";
+
+ menuItemElement.appendChild(checkMarkElement);
+ menuItemElement.appendChild(document.createTextNode(item.label));
+
+ menuItemElement.addEventListener("mousedown", this._menuItemMouseDown.bind(this), false);
+ menuItemElement.addEventListener("mouseup", this._menuItemMouseUp.bind(this), false);
+
+ // Manually manage hover highlight since :hover does not work in case of click-and-hold menu invocation.
+ menuItemElement.addEventListener("mouseover", this._menuItemMouseOver.bind(this), false);
+ menuItemElement.addEventListener("mouseout", this._menuItemMouseOut.bind(this), false);
+
+ menuItemElement._actionId = item.id;
+ return menuItemElement;
+ },
+
+ _createSubMenu: function(item)
+ {
+ var menuItemElement = document.createElement("div");
+ menuItemElement.className = "soft-context-menu-item";
+ menuItemElement._subItems = item.subItems;
+
+ // Occupy the same space on the left in all items.
+ var checkMarkElement = document.createElement("span");
+ checkMarkElement.textContent = "\u2713 "; // Checkmark Unicode symbol
+ checkMarkElement.className = "soft-context-menu-item-checkmark";
+ checkMarkElement.style.opacity = "0";
+ menuItemElement.appendChild(checkMarkElement);
+
+ var subMenuArrowElement = document.createElement("span");
+ subMenuArrowElement.textContent = "\u25B6"; // BLACK RIGHT-POINTING TRIANGLE
+ subMenuArrowElement.className = "soft-context-menu-item-submenu-arrow";
+
+ menuItemElement.appendChild(document.createTextNode(item.label));
+ menuItemElement.appendChild(subMenuArrowElement);
+
+ menuItemElement.addEventListener("mousedown", this._menuItemMouseDown.bind(this), false);
+ menuItemElement.addEventListener("mouseup", this._menuItemMouseUp.bind(this), false);
+
+ // Manually manage hover highlight since :hover does not work in case of click-and-hold menu invocation.
+ menuItemElement.addEventListener("mouseover", this._menuItemMouseOver.bind(this), false);
+ menuItemElement.addEventListener("mouseout", this._menuItemMouseOut.bind(this), false);
+
+ return menuItemElement;
+ },
+
+ _createSeparator: function()
+ {
+ var separatorElement = document.createElement("div");
+ separatorElement.className = "soft-context-menu-separator";
+ separatorElement._isSeparator = true;
+ separatorElement.addEventListener("mouseover", this._hideSubMenu.bind(this), false);
+ separatorElement.createChild("div", "separator-line");
+ return separatorElement;
+ },
+
+ _menuItemMouseDown: function(event)
+ {
+ // Do not let separator's mouse down hit menu's handler - we need to receive mouse up!
+ event.consume(true);
+ },
+
+ _menuItemMouseUp: function(event)
+ {
+ this._triggerAction(event.target, event);
+ event.consume();
+ },
+
+ _focus: function()
+ {
+ this._contextMenuElement.focus();
+ },
+
+ _triggerAction: function(menuItemElement, event)
+ {
+ if (!menuItemElement._subItems) {
+ this._discardMenu(true, event);
+ if (typeof menuItemElement._actionId !== "undefined") {
+ WebInspector.contextMenuItemSelected(menuItemElement._actionId);
+ delete menuItemElement._actionId;
+ }
+ return;
+ }
+
+ this._showSubMenu(menuItemElement, event);
+ event.consume();
+ },
+
+ _showSubMenu: function(menuItemElement, event)
+ {
+ if (menuItemElement._subMenuTimer) {
+ clearTimeout(menuItemElement._subMenuTimer);
+ delete menuItemElement._subMenuTimer;
+ }
+ if (this._subMenu)
+ return;
+
+ this._subMenu = new WebInspector.SoftContextMenu(menuItemElement._subItems, this);
+ this._subMenu.show(this._buildMouseEventForSubMenu(menuItemElement));
+ },
+
+ _buildMouseEventForSubMenu: function(subMenuItemElement)
+ {
+ var subMenuOffset = { x: subMenuItemElement.offsetWidth - 3, y: subMenuItemElement.offsetTop - 1 };
+ var targetX = this._x + subMenuOffset.x;
+ var targetY = this._y + subMenuOffset.y;
+ var targetPageX = parseInt(this._contextMenuElement.style.left, 10) + subMenuOffset.x;
+ var targetPageY = parseInt(this._contextMenuElement.style.top, 10) + subMenuOffset.y;
+ return { x: targetX, y: targetY, pageX: targetPageX, pageY: targetPageY, consume: function() {} };
+ },
+
+ _hideSubMenu: function()
+ {
+ if (!this._subMenu)
+ return;
+ this._subMenu._discardSubMenus();
+ this._focus();
+ },
+
+ _menuItemMouseOver: function(event)
+ {
+ this._highlightMenuItem(event.target);
+ },
+
+ _menuItemMouseOut: function(event)
+ {
+ if (!this._subMenu || !event.relatedTarget) {
+ this._highlightMenuItem(null);
+ return;
+ }
+
+ var relatedTarget = event.relatedTarget;
+ if (this._contextMenuElement.isSelfOrAncestor(relatedTarget) || relatedTarget.classList.contains("soft-context-menu-glass-pane"))
+ this._highlightMenuItem(null);
+ },
+
+ _highlightMenuItem: function(menuItemElement)
+ {
+ if (this._highlightedMenuItemElement === menuItemElement)
+ return;
+
+ this._hideSubMenu();
+ if (this._highlightedMenuItemElement) {
+ this._highlightedMenuItemElement.classList.remove("soft-context-menu-item-mouse-over");
+ if (this._highlightedMenuItemElement._subItems && this._highlightedMenuItemElement._subMenuTimer) {
+ clearTimeout(this._highlightedMenuItemElement._subMenuTimer);
+ delete this._highlightedMenuItemElement._subMenuTimer;
+ }
+ }
+ this._highlightedMenuItemElement = menuItemElement;
+ if (this._highlightedMenuItemElement) {
+ this._highlightedMenuItemElement.classList.add("soft-context-menu-item-mouse-over");
+ this._contextMenuElement.focus();
+ if (this._highlightedMenuItemElement._subItems && !this._highlightedMenuItemElement._subMenuTimer)
+ this._highlightedMenuItemElement._subMenuTimer = setTimeout(this._showSubMenu.bind(this, this._highlightedMenuItemElement, this._buildMouseEventForSubMenu(this._highlightedMenuItemElement)), 150);
+ }
+ },
+
+ _highlightPrevious: function()
+ {
+ var menuItemElement = this._highlightedMenuItemElement ? this._highlightedMenuItemElement.previousSibling : this._contextMenuElement.lastChild;
+ while (menuItemElement && menuItemElement._isSeparator)
+ menuItemElement = menuItemElement.previousSibling;
+ if (menuItemElement)
+ this._highlightMenuItem(menuItemElement);
+ },
+
+ _highlightNext: function()
+ {
+ var menuItemElement = this._highlightedMenuItemElement ? this._highlightedMenuItemElement.nextSibling : this._contextMenuElement.firstChild;
+ while (menuItemElement && menuItemElement._isSeparator)
+ menuItemElement = menuItemElement.nextSibling;
+ if (menuItemElement)
+ this._highlightMenuItem(menuItemElement);
+ },
+
+ _menuKeyDown: function(event)
+ {
+ switch (event.keyIdentifier) {
+ case "Up":
+ this._highlightPrevious(); break;
+ case "Down":
+ this._highlightNext(); break;
+ case "Left":
+ if (this._parentMenu) {
+ this._highlightMenuItem(null);
+ this._parentMenu._focus();
+ }
+ break;
+ case "Right":
+ if (!this._highlightedMenuItemElement)
+ break;
+ if (this._highlightedMenuItemElement._subItems) {
+ this._showSubMenu(this._highlightedMenuItemElement, this._buildMouseEventForSubMenu(this._highlightedMenuItemElement));
+ this._subMenu._focus();
+ this._subMenu._highlightNext();
+ }
+ break;
+ case "U+001B": // Escape
+ this._discardMenu(true, event); break;
+ case "Enter":
+ if (!isEnterKey(event))
+ break;
+ // Fall through
+ case "U+0020": // Space
+ if (this._highlightedMenuItemElement)
+ this._triggerAction(this._highlightedMenuItemElement, event);
+ break;
+ }
+ event.consume(true);
+ },
+
+ _glassPaneMouseUp: function(event)
+ {
+ // Return if this is simple 'click', since dispatched on glass pane, can't use 'click' event.
+ if (event.x === this._x && event.y === this._y && new Date().getTime() - this._time < 300)
+ return;
+ this._discardMenu(true, event);
+ event.consume();
+ },
+
+ /**
+ * @param {boolean} closeParentMenus
+ * @param {!Event=} event
+ */
+ _discardMenu: function(closeParentMenus, event)
+ {
+ if (this._subMenu && !closeParentMenus)
+ return;
+ if (this._glassPaneElement) {
+ var glassPane = this._glassPaneElement;
+ delete this._glassPaneElement;
+ // This can re-enter discardMenu due to blur.
+ document.body.removeChild(glassPane);
+ if (this._parentMenu) {
+ delete this._parentMenu._subMenu;
+ if (closeParentMenus)
+ this._parentMenu._discardMenu(closeParentMenus, event);
+ }
+
+ if (event)
+ event.consume(true);
+ } else if (this._parentMenu && this._contextMenuElement.parentElement) {
+ this._discardSubMenus();
+ if (closeParentMenus)
+ this._parentMenu._discardMenu(closeParentMenus, event);
+
+ if (event)
+ event.consume(true);
+ }
+ },
+
+ _discardSubMenus: function()
+ {
+ if (this._subMenu)
+ this._subMenu._discardSubMenus();
+ this._contextMenuElement.remove();
+ if (this._parentMenu)
+ delete this._parentMenu._subMenu;
+ }
+}
+
+if (!InspectorFrontendHost.showContextMenu) {
+
+InspectorFrontendHost.showContextMenu = function(event, items)
+{
+ new WebInspector.SoftContextMenu(items).show(event);
+}
+
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/SplitView.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/SplitView.js
new file mode 100644
index 00000000000..29745153432
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/SplitView.js
@@ -0,0 +1,888 @@
+/*
+ * Copyright (C) 2012 Google 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {boolean} isVertical
+ * @param {boolean} secondIsSidebar
+ * @param {string=} settingName
+ * @param {number=} defaultSidebarWidth
+ * @param {number=} defaultSidebarHeight
+ * @param {boolean=} constraintsInDip
+ */
+WebInspector.SplitView = function(isVertical, secondIsSidebar, settingName, defaultSidebarWidth, defaultSidebarHeight, constraintsInDip)
+{
+ WebInspector.View.call(this);
+
+ this.registerRequiredCSS("splitView.css");
+ this.element.classList.add("split-view");
+
+ this._mainView = new WebInspector.VBox();
+ this._mainElement = this._mainView.element;
+ this._mainElement.className = "split-view-contents scroll-target split-view-main vbox"; // Override
+
+ this._sidebarView = new WebInspector.VBox();
+ this._sidebarElement = this._sidebarView.element;
+ this._sidebarElement.className = "split-view-contents scroll-target split-view-sidebar vbox"; // Override
+
+ this._resizerElement = this.element.createChild("div", "split-view-resizer");
+ this._resizerElement.createChild("div", "split-view-resizer-border");
+ if (secondIsSidebar) {
+ this._mainView.show(this.element);
+ this._sidebarView.show(this.element);
+ } else {
+ this._sidebarView.show(this.element);
+ this._mainView.show(this.element);
+ }
+
+ this._resizerWidget = new WebInspector.ResizerWidget();
+ this._resizerWidget.setEnabled(true);
+ this._resizerWidget.addEventListener(WebInspector.ResizerWidget.Events.ResizeStart, this._onResizeStart, this);
+ this._resizerWidget.addEventListener(WebInspector.ResizerWidget.Events.ResizeUpdate, this._onResizeUpdate, this);
+ this._resizerWidget.addEventListener(WebInspector.ResizerWidget.Events.ResizeEnd, this._onResizeEnd, this);
+
+ this._defaultSidebarWidth = defaultSidebarWidth || 200;
+ this._defaultSidebarHeight = defaultSidebarHeight || this._defaultSidebarWidth;
+ this._constraintsInDip = !!constraintsInDip;
+ this._settingName = settingName;
+
+ this.setSecondIsSidebar(secondIsSidebar);
+
+ this._innerSetVertical(isVertical);
+ this._showMode = WebInspector.SplitView.ShowMode.Both;
+
+ // Should be called after isVertical has the right value.
+ this.installResizer(this._resizerElement);
+}
+
+/** @typedef {{showMode: string, size: number}} */
+WebInspector.SplitView.SettingForOrientation;
+
+WebInspector.SplitView.ShowMode = {
+ Both: "Both",
+ OnlyMain: "OnlyMain",
+ OnlySidebar: "OnlySidebar"
+}
+
+WebInspector.SplitView.Events = {
+ SidebarSizeChanged: "SidebarSizeChanged",
+ ShowModeChanged: "ShowModeChanged"
+}
+
+WebInspector.SplitView.MinPadding = 20;
+
+WebInspector.SplitView.prototype = {
+ /**
+ * @return {boolean}
+ */
+ isVertical: function()
+ {
+ return this._isVertical;
+ },
+
+ /**
+ * @param {boolean} isVertical
+ */
+ setVertical: function(isVertical)
+ {
+ if (this._isVertical === isVertical)
+ return;
+
+ this._innerSetVertical(isVertical);
+
+ if (this.isShowing())
+ this._updateLayout();
+ },
+
+ /**
+ * @param {boolean} isVertical
+ */
+ _innerSetVertical: function(isVertical)
+ {
+ this.element.classList.remove(this._isVertical ? "hbox" : "vbox");
+ this._isVertical = isVertical;
+ this.element.classList.add(this._isVertical ? "hbox" : "vbox");
+ delete this._resizerElementSize;
+ this._sidebarSize = -1;
+ this._restoreSidebarSizeFromSettings();
+ if (this._shouldSaveShowMode)
+ this._restoreAndApplyShowModeFromSettings();
+ this._updateShowHideSidebarButton();
+ // FIXME: reverse SplitView.isVertical meaning.
+ this._resizerWidget.setVertical(!isVertical);
+ this.invalidateConstraints();
+ },
+
+ /**
+ * @param {boolean=} animate
+ */
+ _updateLayout: function(animate)
+ {
+ delete this._totalSize; // Lazy update.
+ delete this._totalSizeOtherDimension;
+
+ // Remove properties that might affect total size calculation.
+ this._mainElement.style.removeProperty("width");
+ this._mainElement.style.removeProperty("height");
+ this._sidebarElement.style.removeProperty("width");
+ this._sidebarElement.style.removeProperty("height");
+
+ this._innerSetSidebarSize(this._preferredSidebarSize(), !!animate);
+ },
+
+ /**
+ * @return {!Element}
+ */
+ mainElement: function()
+ {
+ return this._mainElement;
+ },
+
+ /**
+ * @return {!Element}
+ */
+ sidebarElement: function()
+ {
+ return this._sidebarElement;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isSidebarSecond: function()
+ {
+ return this._secondIsSidebar;
+ },
+
+ enableShowModeSaving: function()
+ {
+ this._shouldSaveShowMode = true;
+ this._restoreAndApplyShowModeFromSettings();
+ },
+
+ /**
+ * @return {string}
+ */
+ showMode: function()
+ {
+ return this._showMode;
+ },
+
+ /**
+ * @param {boolean} secondIsSidebar
+ */
+ setSecondIsSidebar: function(secondIsSidebar)
+ {
+ this._mainElement.classList.toggle("split-view-contents-first", secondIsSidebar);
+ this._mainElement.classList.toggle("split-view-contents-second", !secondIsSidebar);
+ this._sidebarElement.classList.toggle("split-view-contents-first", !secondIsSidebar);
+ this._sidebarElement.classList.toggle("split-view-contents-second", secondIsSidebar);
+
+ // Make sure second is last in the children array.
+ if (secondIsSidebar) {
+ if (this._sidebarElement.parentElement && this._sidebarElement.nextSibling)
+ this.element.appendChild(this._sidebarElement);
+ } else {
+ if (this._mainElement.parentElement && this._mainElement.nextSibling)
+ this.element.appendChild(this._mainElement);
+ }
+
+ this._secondIsSidebar = secondIsSidebar;
+ },
+
+ /**
+ * @return {?string}
+ */
+ sidebarSide: function()
+ {
+ if (this._showMode !== WebInspector.SplitView.ShowMode.Both)
+ return null;
+ return this._isVertical ?
+ (this._secondIsSidebar ? "right" : "left") :
+ (this._secondIsSidebar ? "bottom" : "top");
+ },
+
+ /**
+ * @return {number}
+ */
+ preferredSidebarSize: function()
+ {
+ return this._preferredSidebarSize();
+ },
+
+ /**
+ * @return {!Element}
+ */
+ resizerElement: function()
+ {
+ return this._resizerElement;
+ },
+
+ /**
+ * @param {boolean=} animate
+ */
+ hideMain: function(animate)
+ {
+ this._showOnly(this._sidebarView, this._mainView, animate);
+ this._updateShowMode(WebInspector.SplitView.ShowMode.OnlySidebar);
+ },
+
+ /**
+ * @param {boolean=} animate
+ */
+ hideSidebar: function(animate)
+ {
+ this._showOnly(this._mainView, this._sidebarView, animate);
+ this._updateShowMode(WebInspector.SplitView.ShowMode.OnlyMain);
+ },
+
+ /**
+ * @override
+ */
+ detachChildViews: function()
+ {
+ this._mainView.detachChildViews();
+ this._sidebarView.detachChildViews();
+ },
+
+ /**
+ * @param {!WebInspector.View} sideToShow
+ * @param {!WebInspector.View} sideToHide
+ * @param {boolean=} animate
+ */
+ _showOnly: function(sideToShow, sideToHide, animate)
+ {
+ this._cancelAnimation();
+
+ /**
+ * @this {WebInspector.SplitView}
+ */
+ function callback()
+ {
+ sideToShow.show(this.element);
+ sideToHide.detach();
+ sideToShow.element.classList.add("maximized");
+ sideToHide.element.classList.remove("maximized");
+ this._resizerElement.classList.add("hidden");
+ this._removeAllLayoutProperties();
+ }
+
+ if (animate) {
+ this._animate(true, callback.bind(this));
+ } else {
+ callback.call(this);
+ this.doResize();
+ }
+
+ this._sidebarSize = -1;
+ this.setResizable(false);
+ },
+
+ _removeAllLayoutProperties: function()
+ {
+ this._sidebarElement.style.removeProperty("flexBasis");
+
+ this._mainElement.style.removeProperty("width");
+ this._mainElement.style.removeProperty("height");
+ this._sidebarElement.style.removeProperty("width");
+ this._sidebarElement.style.removeProperty("height");
+
+ this._resizerElement.style.removeProperty("left");
+ this._resizerElement.style.removeProperty("right");
+ this._resizerElement.style.removeProperty("top");
+ this._resizerElement.style.removeProperty("bottom");
+
+ this._resizerElement.style.removeProperty("margin-left");
+ this._resizerElement.style.removeProperty("margin-right");
+ this._resizerElement.style.removeProperty("margin-top");
+ this._resizerElement.style.removeProperty("margin-bottom");
+ },
+
+ /**
+ * @param {boolean=} animate
+ */
+ showBoth: function(animate)
+ {
+ if (this._showMode === WebInspector.SplitView.ShowMode.Both)
+ animate = false;
+
+ this._cancelAnimation();
+ this._mainElement.classList.remove("maximized");
+ this._sidebarElement.classList.remove("maximized");
+ this._resizerElement.classList.remove("hidden");
+
+ this._mainView.show(this.element);
+ this._sidebarView.show(this.element);
+ // Order views in DOM properly.
+ this.setSecondIsSidebar(this._secondIsSidebar);
+
+ this._sidebarSize = -1;
+ this.setResizable(true);
+ this._updateShowMode(WebInspector.SplitView.ShowMode.Both);
+ this._updateLayout(animate);
+ },
+
+ /**
+ * @param {boolean} resizable
+ */
+ setResizable: function(resizable)
+ {
+ this._resizerWidget.setEnabled(resizable);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isResizable: function()
+ {
+ return this._resizerWidget.isEnabled();
+ },
+
+ /**
+ * @param {number} size
+ */
+ setSidebarSize: function(size)
+ {
+ size *= WebInspector.zoomManager.zoomFactor();
+ this._savedSidebarSize = size;
+ this._saveSetting();
+ this._innerSetSidebarSize(size, false, true);
+ },
+
+ /**
+ * @return {number}
+ */
+ sidebarSize: function()
+ {
+ var size = Math.max(0, this._sidebarSize);
+ return size / WebInspector.zoomManager.zoomFactor();
+ },
+
+ /**
+ * Returns total size in DIP.
+ * @return {number}
+ */
+ _totalSizeDIP: function()
+ {
+ if (!this._totalSize) {
+ this._totalSize = this._isVertical ? this.element.offsetWidth : this.element.offsetHeight;
+ this._totalSizeOtherDimension = this._isVertical ? this.element.offsetHeight : this.element.offsetWidth;
+ }
+ return this._totalSize * WebInspector.zoomManager.zoomFactor();
+ },
+
+ /**
+ * @param {string} showMode
+ */
+ _updateShowMode: function(showMode)
+ {
+ this._showMode = showMode;
+ this._saveShowModeToSettings();
+ this._updateShowHideSidebarButton();
+ this.dispatchEventToListeners(WebInspector.SplitView.Events.ShowModeChanged, showMode);
+ this.invalidateConstraints();
+ },
+
+ /**
+ * @param {number} size
+ * @param {boolean} animate
+ * @param {boolean=} userAction
+ */
+ _innerSetSidebarSize: function(size, animate, userAction)
+ {
+ if (this._showMode !== WebInspector.SplitView.ShowMode.Both || !this.isShowing())
+ return;
+
+ size = this._applyConstraints(size, userAction);
+ if (this._sidebarSize === size)
+ return;
+
+ if (!this._resizerElementSize)
+ this._resizerElementSize = this._isVertical ? this._resizerElement.offsetWidth : this._resizerElement.offsetHeight;
+
+ // Invalidate layout below.
+
+ this._removeAllLayoutProperties();
+
+ // this._totalSize is available below since we successfully applied constraints.
+ var sidebarSizeValue = (size / WebInspector.zoomManager.zoomFactor()) + "px";
+ var mainSizeValue = (this._totalSize - size / WebInspector.zoomManager.zoomFactor()) + "px";
+ this.sidebarElement().style.flexBasis = sidebarSizeValue;
+
+ // Make both sides relayout boundaries.
+ if (this._isVertical) {
+ this._sidebarElement.style.width = sidebarSizeValue;
+ this._mainElement.style.width = mainSizeValue;
+ this._sidebarElement.style.height = this._totalSizeOtherDimension + "px";
+ this._mainElement.style.height = this._totalSizeOtherDimension + "px";
+ } else {
+ this._sidebarElement.style.height = sidebarSizeValue;
+ this._mainElement.style.height = mainSizeValue;
+ this._sidebarElement.style.width = this._totalSizeOtherDimension + "px";
+ this._mainElement.style.width = this._totalSizeOtherDimension + "px";
+ }
+
+ // Position resizer.
+ if (this._isVertical) {
+ if (this._secondIsSidebar) {
+ this._resizerElement.style.right = sidebarSizeValue;
+ this._resizerElement.style.marginRight = -this._resizerElementSize / 2 + "px";
+ } else {
+ this._resizerElement.style.left = sidebarSizeValue;
+ this._resizerElement.style.marginLeft = -this._resizerElementSize / 2 + "px";
+ }
+ } else {
+ if (this._secondIsSidebar) {
+ this._resizerElement.style.bottom = sidebarSizeValue;
+ this._resizerElement.style.marginBottom = -this._resizerElementSize / 2 + "px";
+ } else {
+ this._resizerElement.style.top = sidebarSizeValue;
+ this._resizerElement.style.marginTop = -this._resizerElementSize / 2 + "px";
+ }
+ }
+
+ this._sidebarSize = size;
+
+ // Force layout.
+
+ if (animate) {
+ this._animate(false);
+ } else {
+ // No need to recalculate this._sidebarSize and this._totalSize again.
+ this.doResize();
+ this.dispatchEventToListeners(WebInspector.SplitView.Events.SidebarSizeChanged, this.sidebarSize());
+ }
+ },
+
+ /**
+ * @param {boolean} reverse
+ * @param {function()=} callback
+ */
+ _animate: function(reverse, callback)
+ {
+ var animationTime = 50;
+ this._animationCallback = callback;
+
+ var animatedMarginPropertyName;
+ if (this._isVertical)
+ animatedMarginPropertyName = this._secondIsSidebar ? "margin-right" : "margin-left";
+ else
+ animatedMarginPropertyName = this._secondIsSidebar ? "margin-bottom" : "margin-top";
+
+ var zoomFactor = WebInspector.zoomManager.zoomFactor();
+ var marginFrom = reverse ? "0" : "-" + (this._sidebarSize / zoomFactor) + "px";
+ var marginTo = reverse ? "-" + (this._sidebarSize / zoomFactor) + "px" : "0";
+
+ // This order of things is important.
+ // 1. Resize main element early and force layout.
+ this.element.style.setProperty(animatedMarginPropertyName, marginFrom);
+ if (!reverse) {
+ suppressUnused(this._mainElement.offsetWidth);
+ suppressUnused(this._sidebarElement.offsetWidth);
+ }
+
+ // 2. Issue onresize to the sidebar element, its size won't change.
+ if (!reverse)
+ this._sidebarView.doResize();
+
+ // 3. Configure and run animation
+ this.element.style.setProperty("transition", animatedMarginPropertyName + " " + animationTime + "ms linear");
+
+ var boundAnimationFrame;
+ var startTime;
+ /**
+ * @this {WebInspector.SplitView}
+ */
+ function animationFrame()
+ {
+ delete this._animationFrameHandle;
+
+ if (!startTime) {
+ // Kick animation on first frame.
+ this.element.style.setProperty(animatedMarginPropertyName, marginTo);
+ startTime = window.performance.now();
+ } else if (window.performance.now() < startTime + animationTime) {
+ // Process regular animation frame.
+ this._mainView.doResize();
+ } else {
+ // Complete animation.
+ this._cancelAnimation();
+ this._mainView.doResize();
+ this.dispatchEventToListeners(WebInspector.SplitView.Events.SidebarSizeChanged, this.sidebarSize());
+ return;
+ }
+ this._animationFrameHandle = window.requestAnimationFrame(boundAnimationFrame);
+ }
+ boundAnimationFrame = animationFrame.bind(this);
+ this._animationFrameHandle = window.requestAnimationFrame(boundAnimationFrame);
+ },
+
+ _cancelAnimation: function()
+ {
+ this.element.style.removeProperty("margin-top");
+ this.element.style.removeProperty("margin-right");
+ this.element.style.removeProperty("margin-bottom");
+ this.element.style.removeProperty("margin-left");
+ this.element.style.removeProperty("transition");
+
+ if (this._animationFrameHandle) {
+ window.cancelAnimationFrame(this._animationFrameHandle);
+ delete this._animationFrameHandle;
+ }
+ if (this._animationCallback) {
+ this._animationCallback();
+ delete this._animationCallback;
+ }
+ },
+
+ /**
+ * @param {number} sidebarSize
+ * @param {boolean=} userAction
+ * @return {number}
+ */
+ _applyConstraints: function(sidebarSize, userAction)
+ {
+ var totalSize = this._totalSizeDIP();
+ var zoomFactor = this._constraintsInDip ? 1 : WebInspector.zoomManager.zoomFactor();
+
+ var constraints = this._sidebarView.constraints();
+ var minSidebarSize = this.isVertical() ? constraints.minimum.width : constraints.minimum.height;
+ if (!minSidebarSize)
+ minSidebarSize = WebInspector.SplitView.MinPadding;
+ minSidebarSize *= zoomFactor;
+
+ var preferredSidebarSize = this.isVertical() ? constraints.preferred.width : constraints.preferred.height;
+ if (!preferredSidebarSize)
+ preferredSidebarSize = WebInspector.SplitView.MinPadding;
+ preferredSidebarSize *= zoomFactor;
+ // Allow sidebar to be less than preferred by explicit user action.
+ if (sidebarSize < preferredSidebarSize)
+ preferredSidebarSize = Math.max(sidebarSize, minSidebarSize);
+
+ constraints = this._mainView.constraints();
+ var minMainSize = this.isVertical() ? constraints.minimum.width : constraints.minimum.height;
+ if (!minMainSize)
+ minMainSize = WebInspector.SplitView.MinPadding;
+ minMainSize *= zoomFactor;
+
+ var preferredMainSize = this.isVertical() ? constraints.preferred.width : constraints.preferred.height;
+ if (!preferredMainSize)
+ preferredMainSize = WebInspector.SplitView.MinPadding;
+ preferredMainSize *= zoomFactor;
+ var savedMainSize = this.isVertical() ? this._savedVerticalMainSize : this._savedHorizontalMainSize;
+ if (typeof savedMainSize !== "undefined")
+ preferredMainSize = Math.min(preferredMainSize, savedMainSize * zoomFactor);
+ if (userAction)
+ preferredMainSize = minMainSize;
+
+ // Enough space for preferred.
+ var totalPreferred = preferredMainSize + preferredSidebarSize;
+ if (totalPreferred <= totalSize)
+ return Number.constrain(sidebarSize, preferredSidebarSize, totalSize - preferredMainSize);
+
+ // Enough space for minimum.
+ if (minMainSize + minSidebarSize <= totalSize) {
+ var delta = totalPreferred - totalSize;
+ var sidebarDelta = delta * preferredSidebarSize / totalPreferred;
+ sidebarSize = preferredSidebarSize - sidebarDelta;
+ return Number.constrain(sidebarSize, minSidebarSize, totalSize - minMainSize);
+ }
+
+ // Not enough space even for minimum sizes.
+ return Math.max(0, totalSize - minMainSize);
+ },
+
+ wasShown: function()
+ {
+ this._forceUpdateLayout();
+ WebInspector.zoomManager.addEventListener(WebInspector.ZoomManager.Events.ZoomChanged, this._onZoomChanged, this);
+ },
+
+ willHide: function()
+ {
+ WebInspector.zoomManager.removeEventListener(WebInspector.ZoomManager.Events.ZoomChanged, this._onZoomChanged, this);
+ },
+
+ onResize: function()
+ {
+ this._updateLayout();
+ },
+
+ onLayout: function()
+ {
+ this._updateLayout();
+ },
+
+ /**
+ * @return {!Constraints}
+ */
+ calculateConstraints: function()
+ {
+ if (this._showMode === WebInspector.SplitView.ShowMode.OnlyMain)
+ return this._mainView.constraints();
+ if (this._showMode === WebInspector.SplitView.ShowMode.OnlySidebar)
+ return this._sidebarView.constraints();
+
+ var mainConstraints = this._mainView.constraints();
+ var sidebarConstraints = this._sidebarView.constraints();
+ var min = WebInspector.SplitView.MinPadding;
+ if (this._isVertical) {
+ mainConstraints = mainConstraints.widthToMax(min);
+ sidebarConstraints = sidebarConstraints.widthToMax(min);
+ return mainConstraints.addWidth(sidebarConstraints).heightToMax(sidebarConstraints);
+ } else {
+ mainConstraints = mainConstraints.heightToMax(min);
+ sidebarConstraints = sidebarConstraints.heightToMax(min);
+ return mainConstraints.widthToMax(sidebarConstraints).addHeight(sidebarConstraints);
+ }
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _onResizeStart: function(event)
+ {
+ this._resizeStartSize = this._sidebarSize;
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _onResizeUpdate: function(event)
+ {
+ var cssOffset = event.data.currentPosition - event.data.startPosition;
+ var dipOffset = cssOffset * WebInspector.zoomManager.zoomFactor();
+ var newSize = this._secondIsSidebar ? this._resizeStartSize - dipOffset : this._resizeStartSize + dipOffset;
+ var constrainedSize = this._applyConstraints(newSize, true);
+ this._savedSidebarSize = constrainedSize;
+ this._saveSetting();
+ this._innerSetSidebarSize(constrainedSize, false, true);
+ if (this.isVertical())
+ this._savedVerticalMainSize = this._totalSizeDIP() - this._sidebarSize;
+ else
+ this._savedHorizontalMainSize = this._totalSizeDIP() - this._sidebarSize;
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _onResizeEnd: function(event)
+ {
+ delete this._resizeStartSize;
+ },
+
+ hideDefaultResizer: function()
+ {
+ this.uninstallResizer(this._resizerElement);
+ },
+
+ /**
+ * @param {!Element} resizerElement
+ */
+ installResizer: function(resizerElement)
+ {
+ this._resizerWidget.addElement(resizerElement);
+ },
+
+ /**
+ * @param {!Element} resizerElement
+ */
+ uninstallResizer: function(resizerElement)
+ {
+ this._resizerWidget.removeElement(resizerElement);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ hasCustomResizer: function()
+ {
+ var elements = this._resizerWidget.elements();
+ return elements.length > 1 || (elements.length == 1 && elements[0] !== this._resizerElement);
+ },
+
+ /**
+ * @param {!Element} resizer
+ * @param {boolean} on
+ */
+ toggleResizer: function(resizer, on)
+ {
+ if (on)
+ this.installResizer(resizer);
+ else
+ this.uninstallResizer(resizer);
+ },
+
+ /**
+ * @return {?WebInspector.Setting}
+ */
+ _setting: function()
+ {
+ if (!this._settingName)
+ return null;
+
+ if (!WebInspector.settings[this._settingName])
+ WebInspector.settings[this._settingName] = WebInspector.settings.createSetting(this._settingName, {});
+
+ return WebInspector.settings[this._settingName];
+ },
+
+ /**
+ * @return {?WebInspector.SplitView.SettingForOrientation}
+ */
+ _settingForOrientation: function()
+ {
+ var state = this._setting() ? this._setting().get() : {};
+ return this._isVertical ? state.vertical : state.horizontal;
+ },
+
+ /**
+ * @return {number}
+ */
+ _preferredSidebarSize: function()
+ {
+ var size = this._savedSidebarSize;
+ if (!size) {
+ size = this._isVertical ? this._defaultSidebarWidth : this._defaultSidebarHeight;
+ // If we have default value in percents, calculate it on first use.
+ if (0 < size && size < 1)
+ size *= this._totalSizeDIP();
+ }
+ return size;
+ },
+
+ _restoreSidebarSizeFromSettings: function()
+ {
+ var settingForOrientation = this._settingForOrientation();
+ this._savedSidebarSize = settingForOrientation ? settingForOrientation.size : 0;
+ },
+
+ _restoreAndApplyShowModeFromSettings: function()
+ {
+ var orientationState = this._settingForOrientation();
+ this._savedShowMode = orientationState ? orientationState.showMode : WebInspector.SplitView.ShowMode.Both;
+ this._showMode = this._savedShowMode;
+
+ switch (this._savedShowMode) {
+ case WebInspector.SplitView.ShowMode.Both:
+ this.showBoth();
+ break;
+ case WebInspector.SplitView.ShowMode.OnlyMain:
+ this.hideSidebar();
+ break;
+ case WebInspector.SplitView.ShowMode.OnlySidebar:
+ this.hideMain();
+ break;
+ }
+ },
+
+ _saveShowModeToSettings: function()
+ {
+ this._savedShowMode = this._showMode;
+ this._saveSetting();
+ },
+
+ _saveSetting: function()
+ {
+ var setting = this._setting();
+ if (!setting)
+ return;
+ var state = setting.get();
+ var orientationState = (this._isVertical ? state.vertical : state.horizontal) || {};
+
+ orientationState.size = this._savedSidebarSize;
+ if (this._shouldSaveShowMode)
+ orientationState.showMode = this._savedShowMode;
+
+ if (this._isVertical)
+ state.vertical = orientationState;
+ else
+ state.horizontal = orientationState;
+ setting.set(state);
+ },
+
+ _forceUpdateLayout: function()
+ {
+ // Force layout even if sidebar size does not change.
+ this._sidebarSize = -1;
+ this._updateLayout();
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _onZoomChanged: function(event)
+ {
+ this._forceUpdateLayout();
+ },
+
+ /**
+ * @param {string} title
+ * @param {string} className
+ * @return {!WebInspector.StatusBarButton}
+ */
+ createShowHideSidebarButton: function(title, className)
+ {
+ console.assert(this.isVertical(), "Buttons for split view with horizontal split are not supported yet.");
+
+ this._showHideSidebarButtonTitle = WebInspector.UIString(title);
+ this._showHideSidebarButton = new WebInspector.StatusBarButton("", "sidebar-show-hide-button " + className, 3);
+ this._showHideSidebarButton.addEventListener("click", buttonClicked.bind(this));
+ this._updateShowHideSidebarButton();
+
+ /**
+ * @this {WebInspector.SplitView}
+ * @param {!WebInspector.Event} event
+ */
+ function buttonClicked(event)
+ {
+ if (this._showMode !== WebInspector.SplitView.ShowMode.Both)
+ this.showBoth(true);
+ else
+ this.hideSidebar(true);
+ }
+
+ return this._showHideSidebarButton;
+ },
+
+ _updateShowHideSidebarButton: function()
+ {
+ if (!this._showHideSidebarButton)
+ return;
+ var sidebarHidden = this._showMode === WebInspector.SplitView.ShowMode.OnlyMain;
+ this._showHideSidebarButton.state = sidebarHidden ? "show" : "hide";
+ this._showHideSidebarButton.element.classList.toggle("top-sidebar-show-hide-button", !this.isVertical() && !this.isSidebarSecond());
+ this._showHideSidebarButton.element.classList.toggle("right-sidebar-show-hide-button", this.isVertical() && this.isSidebarSecond());
+ this._showHideSidebarButton.element.classList.toggle("bottom-sidebar-show-hide-button", !this.isVertical() && this.isSidebarSecond());
+ this._showHideSidebarButton.element.classList.toggle("left-sidebar-show-hide-button", this.isVertical() && !this.isSidebarSecond());
+ this._showHideSidebarButton.title = sidebarHidden ? WebInspector.UIString("Show %s", this._showHideSidebarButtonTitle) : WebInspector.UIString("Hide %s", this._showHideSidebarButtonTitle);
+ },
+
+ __proto__: WebInspector.View.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/StackView.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/StackView.js
new file mode 100644
index 00000000000..1b9e4f20406
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/StackView.js
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 Google 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.VBox}
+ * @param {boolean} isVertical
+ */
+WebInspector.StackView = function(isVertical)
+{
+ WebInspector.VBox.call(this);
+ this._isVertical = isVertical;
+ this._currentSplitView = null;
+}
+
+WebInspector.StackView.prototype = {
+ /**
+ * @param {!WebInspector.View} view
+ * @param {string=} sidebarSizeSettingName
+ * @param {number=} defaultSidebarWidth
+ * @param {number=} defaultSidebarHeight
+ * @return {!WebInspector.SplitView}
+ */
+ appendView: function(view, sidebarSizeSettingName, defaultSidebarWidth, defaultSidebarHeight)
+ {
+ var splitView = new WebInspector.SplitView(this._isVertical, true, sidebarSizeSettingName, defaultSidebarWidth, defaultSidebarHeight);
+ view.show(splitView.mainElement());
+ splitView.hideSidebar();
+
+ if (!this._currentSplitView) {
+ splitView.show(this.element);
+ } else {
+ splitView.show(this._currentSplitView.sidebarElement());
+ this._currentSplitView.showBoth();
+ }
+
+ this._currentSplitView = splitView;
+ return splitView;
+ },
+
+ detachChildViews: function()
+ {
+ WebInspector.View.prototype.detachChildViews.call(this);
+ this._currentSplitView = null;
+ },
+
+ __proto__: WebInspector.VBox.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/StatusBarButton.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/StatusBarButton.js
new file mode 100644
index 00000000000..cc0128d736d
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/StatusBarButton.js
@@ -0,0 +1,688 @@
+/*
+ * Copyright (C) 2009 Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @param {string} elementType
+ */
+WebInspector.StatusBarItem = function(elementType)
+{
+ this.element = document.createElement(elementType);
+ this._enabled = true;
+ this._visible = true;
+}
+
+WebInspector.StatusBarItem.prototype = {
+ /**
+ * @param {boolean} value
+ */
+ setEnabled: function(value)
+ {
+ if (this._enabled === value)
+ return;
+ this._enabled = value;
+ this._applyEnabledState();
+ },
+
+ /**
+ * @protected
+ */
+ _applyEnabledState: function()
+ {
+ this.element.disabled = !this._enabled;
+ },
+
+ get visible()
+ {
+ return this._visible;
+ },
+
+ set visible(x)
+ {
+ if (this._visible === x)
+ return;
+ this.element.classList.toggle("hidden", !x);
+ this._visible = x;
+ },
+
+ __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.StatusBarItem}
+ * @param {string} text
+ * @param {string=} className
+ */
+WebInspector.StatusBarText = function(text, className)
+{
+ WebInspector.StatusBarItem.call(this, "span");
+ this.element.className = "status-bar-item status-bar-text";
+ if (className)
+ this.element.classList.add(className);
+ this.element.textContent = text;
+}
+
+WebInspector.StatusBarText.prototype = {
+ /**
+ * @param {string} text
+ */
+ setText: function(text)
+ {
+ this.element.textContent = text;
+ },
+
+ __proto__: WebInspector.StatusBarItem.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.StatusBarItem}
+ * @param {string=} placeholder
+ * @param {number=} width
+ */
+WebInspector.StatusBarInput = function(placeholder, width)
+{
+ WebInspector.StatusBarItem.call(this, "input");
+ this.element.className = "status-bar-item";
+ this.element.addEventListener("input", this._onChangeCallback.bind(this), false);
+ if (width)
+ this.element.style.width = width + "px";
+ if (placeholder)
+ this.element.setAttribute("placeholder", placeholder);
+ this._value = "";
+}
+
+WebInspector.StatusBarInput.Event = {
+ TextChanged: "TextChanged"
+};
+
+WebInspector.StatusBarInput.prototype = {
+ /**
+ * @param {string} value
+ */
+ setValue: function(value)
+ {
+ this._value = value;
+ this.element.value = value;
+ },
+
+ /**
+ * @return {string}
+ */
+ value: function()
+ {
+ return this.element.value;
+ },
+
+ _onChangeCallback: function()
+ {
+ this.dispatchEventToListeners(WebInspector.StatusBarInput.Event.TextChanged, this.element.value);
+ },
+
+ __proto__: WebInspector.StatusBarItem.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.StatusBarItem}
+ * @param {string} title
+ * @param {string} className
+ * @param {number=} states
+ */
+WebInspector.StatusBarButton = function(title, className, states)
+{
+ WebInspector.StatusBarItem.call(this, "button");
+ this.element.className = className + " status-bar-item";
+ this.element.addEventListener("click", this._clicked.bind(this), false);
+
+ this.glyph = document.createElement("div");
+ this.glyph.className = "glyph";
+ this.element.appendChild(this.glyph);
+
+ this.glyphShadow = document.createElement("div");
+ this.glyphShadow.className = "glyph shadow";
+ this.element.appendChild(this.glyphShadow);
+
+ this.states = states;
+ if (!states)
+ this.states = 2;
+
+ if (states == 2)
+ this._state = false;
+ else
+ this._state = 0;
+
+ this.title = title;
+ this.className = className;
+}
+
+WebInspector.StatusBarButton.prototype = {
+ _clicked: function()
+ {
+ this.dispatchEventToListeners("click");
+ if (this._longClickInterval) {
+ clearInterval(this._longClickInterval);
+ delete this._longClickInterval;
+ }
+ },
+
+ /**
+ * @override
+ */
+ _applyEnabledState: function()
+ {
+ this.element.disabled = !this._enabled;
+ if (this._longClickInterval) {
+ clearInterval(this._longClickInterval);
+ delete this._longClickInterval;
+ }
+ },
+
+ /**
+ * @return {boolean}
+ */
+ enabled: function()
+ {
+ return this._enabled;
+ },
+
+ get title()
+ {
+ return this._title;
+ },
+
+ set title(x)
+ {
+ if (this._title === x)
+ return;
+ this._title = x;
+ this.element.title = x;
+ },
+
+ get state()
+ {
+ return this._state;
+ },
+
+ set state(x)
+ {
+ if (this._state === x)
+ return;
+
+ if (this.states === 2)
+ this.element.classList.toggle("toggled-on", x);
+ else {
+ this.element.classList.remove("toggled-" + this._state);
+ if (x !== 0)
+ this.element.classList.add("toggled-" + x);
+ }
+ this._state = x;
+ },
+
+ get toggled()
+ {
+ if (this.states !== 2)
+ throw("Only used toggled when there are 2 states, otherwise, use state");
+ return this.state;
+ },
+
+ set toggled(x)
+ {
+ if (this.states !== 2)
+ throw("Only used toggled when there are 2 states, otherwise, use state");
+ this.state = x;
+ },
+
+ makeLongClickEnabled: function()
+ {
+ var boundMouseDown = mouseDown.bind(this);
+ var boundMouseUp = mouseUp.bind(this);
+
+ this.element.addEventListener("mousedown", boundMouseDown, false);
+ this.element.addEventListener("mouseout", boundMouseUp, false);
+ this.element.addEventListener("mouseup", boundMouseUp, false);
+
+ var longClicks = 0;
+
+ this._longClickData = { mouseUp: boundMouseUp, mouseDown: boundMouseDown };
+
+ /**
+ * @param {?Event} e
+ * @this {WebInspector.StatusBarButton}
+ */
+ function mouseDown(e)
+ {
+ if (e.which !== 1)
+ return;
+ longClicks = 0;
+ this._longClickInterval = setInterval(longClicked.bind(this), 200);
+ }
+
+ /**
+ * @param {?Event} e
+ * @this {WebInspector.StatusBarButton}
+ */
+ function mouseUp(e)
+ {
+ if (e.which !== 1)
+ return;
+ if (this._longClickInterval) {
+ clearInterval(this._longClickInterval);
+ delete this._longClickInterval;
+ }
+ }
+
+ /**
+ * @this {WebInspector.StatusBarButton}
+ */
+ function longClicked()
+ {
+ ++longClicks;
+ this.dispatchEventToListeners(longClicks === 1 ? "longClickDown" : "longClickPress");
+ }
+ },
+
+ unmakeLongClickEnabled: function()
+ {
+ if (!this._longClickData)
+ return;
+ this.element.removeEventListener("mousedown", this._longClickData.mouseDown, false);
+ this.element.removeEventListener("mouseout", this._longClickData.mouseUp, false);
+ this.element.removeEventListener("mouseup", this._longClickData.mouseUp, false);
+ delete this._longClickData;
+ },
+
+ /**
+ * @param {?function():!Array.<!WebInspector.StatusBarButton>} buttonsProvider
+ */
+ setLongClickOptionsEnabled: function(buttonsProvider)
+ {
+ if (buttonsProvider) {
+ if (!this._longClickOptionsData) {
+ this.makeLongClickEnabled();
+
+ this.longClickGlyph = document.createElement("div");
+ this.longClickGlyph.className = "fill long-click-glyph";
+ this.element.appendChild(this.longClickGlyph);
+
+ this.longClickGlyphShadow = document.createElement("div");
+ this.longClickGlyphShadow.className = "fill long-click-glyph shadow";
+ this.element.appendChild(this.longClickGlyphShadow);
+
+ var longClickDownListener = this._showOptions.bind(this);
+ this.addEventListener("longClickDown", longClickDownListener, this);
+
+ this._longClickOptionsData = {
+ glyphElement: this.longClickGlyph,
+ glyphShadowElement: this.longClickGlyphShadow,
+ longClickDownListener: longClickDownListener
+ };
+ }
+ this._longClickOptionsData.buttonsProvider = buttonsProvider;
+ } else {
+ if (!this._longClickOptionsData)
+ return;
+ this.element.removeChild(this._longClickOptionsData.glyphElement);
+ this.element.removeChild(this._longClickOptionsData.glyphShadowElement);
+
+ this.removeEventListener("longClickDown", this._longClickOptionsData.longClickDownListener, this);
+ delete this._longClickOptionsData;
+
+ this.unmakeLongClickEnabled();
+ }
+ },
+
+ _showOptions: function()
+ {
+ var buttons = this._longClickOptionsData.buttonsProvider();
+ var mainButtonClone = new WebInspector.StatusBarButton(this.title, this.className, this.states);
+ mainButtonClone.addEventListener("click", this._clicked, this);
+ mainButtonClone.state = this.state;
+ buttons.push(mainButtonClone);
+
+ document.documentElement.addEventListener("mouseup", mouseUp, false);
+
+ var optionsGlassPane = new WebInspector.GlassPane();
+ var optionsBarElement = optionsGlassPane.element.createChild("div", "alternate-status-bar-buttons-bar");
+ const buttonHeight = 23;
+
+ var hostButtonPosition = this.element.totalOffset();
+
+ var topNotBottom = hostButtonPosition.top + buttonHeight * buttons.length < document.documentElement.offsetHeight;
+
+ if (topNotBottom)
+ buttons = buttons.reverse();
+
+ optionsBarElement.style.height = (buttonHeight * buttons.length) + "px";
+ if (topNotBottom)
+ optionsBarElement.style.top = (hostButtonPosition.top + 1) + "px";
+ else
+ optionsBarElement.style.top = (hostButtonPosition.top - (buttonHeight * (buttons.length - 1))) + "px";
+ optionsBarElement.style.left = (hostButtonPosition.left + 1) + "px";
+
+ for (var i = 0; i < buttons.length; ++i) {
+ buttons[i].element.addEventListener("mousemove", mouseOver, false);
+ buttons[i].element.addEventListener("mouseout", mouseOut, false);
+ optionsBarElement.appendChild(buttons[i].element);
+ }
+ var hostButtonIndex = topNotBottom ? 0 : buttons.length - 1;
+ buttons[hostButtonIndex].element.classList.add("emulate-active");
+
+ function mouseOver(e)
+ {
+ if (e.which !== 1)
+ return;
+ var buttonElement = e.target.enclosingNodeOrSelfWithClass("status-bar-item");
+ buttonElement.classList.add("emulate-active");
+ }
+
+ function mouseOut(e)
+ {
+ if (e.which !== 1)
+ return;
+ var buttonElement = e.target.enclosingNodeOrSelfWithClass("status-bar-item");
+ buttonElement.classList.remove("emulate-active");
+ }
+
+ function mouseUp(e)
+ {
+ if (e.which !== 1)
+ return;
+ optionsGlassPane.dispose();
+ document.documentElement.removeEventListener("mouseup", mouseUp, false);
+
+ for (var i = 0; i < buttons.length; ++i) {
+ if (buttons[i].element.classList.contains("emulate-active")) {
+ buttons[i].element.classList.remove("emulate-active");
+ buttons[i]._clicked();
+ break;
+ }
+ }
+ }
+ },
+
+ __proto__: WebInspector.StatusBarItem.prototype
+}
+
+/**
+ * @interface
+ */
+WebInspector.StatusBarButton.Provider = function()
+{
+}
+
+WebInspector.StatusBarButton.Provider.prototype = {
+ /**
+ * @return {?WebInspector.StatusBarButton}
+ */
+ button: function() {}
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.StatusBarItem}
+ * @param {?function(?Event)} changeHandler
+ * @param {string=} className
+ */
+WebInspector.StatusBarComboBox = function(changeHandler, className)
+{
+ WebInspector.StatusBarItem.call(this, "span");
+ this.element.className = "status-bar-select-container";
+
+ this._selectElement = this.element.createChild("select", "status-bar-item");
+ this.element.createChild("div", "status-bar-select-arrow");
+ if (changeHandler)
+ this._selectElement.addEventListener("change", changeHandler, false);
+ if (className)
+ this._selectElement.classList.add(className);
+}
+
+WebInspector.StatusBarComboBox.prototype = {
+ /**
+ * @return {!Element}
+ */
+ selectElement: function()
+ {
+ return this._selectElement;
+ },
+
+ /**
+ * @return {number}
+ */
+ size: function()
+ {
+ return this._selectElement.childElementCount;
+ },
+
+ /**
+ * @param {!Element} option
+ */
+ addOption: function(option)
+ {
+ this._selectElement.appendChild(option);
+ },
+
+ /**
+ * @param {string} label
+ * @param {string=} title
+ * @param {string=} value
+ * @return {!Element}
+ */
+ createOption: function(label, title, value)
+ {
+ var option = this._selectElement.createChild("option");
+ option.text = label;
+ if (title)
+ option.title = title;
+ if (typeof value !== "undefined")
+ option.value = value;
+ return option;
+ },
+
+ /**
+ * @override
+ */
+ _applyEnabledState: function()
+ {
+ this._selectElement.disabled = !this._enabled;
+ },
+
+ /**
+ * @param {!Element} option
+ */
+ removeOption: function(option)
+ {
+ this._selectElement.removeChild(option);
+ },
+
+ removeOptions: function()
+ {
+ this._selectElement.removeChildren();
+ },
+
+ /**
+ * @return {?Element}
+ */
+ selectedOption: function()
+ {
+ if (this._selectElement.selectedIndex >= 0)
+ return this._selectElement[this._selectElement.selectedIndex];
+ return null;
+ },
+
+ /**
+ * @param {!Element} option
+ */
+ select: function(option)
+ {
+ this._selectElement.selectedIndex = Array.prototype.indexOf.call(/** @type {?} */ (this._selectElement), option);
+ },
+
+ /**
+ * @param {number} index
+ */
+ setSelectedIndex: function(index)
+ {
+ this._selectElement.selectedIndex = index;
+ },
+
+ /**
+ * @return {number}
+ */
+ selectedIndex: function()
+ {
+ return this._selectElement.selectedIndex;
+ },
+
+ __proto__: WebInspector.StatusBarItem.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.StatusBarItem}
+ * @param {string} title
+ */
+WebInspector.StatusBarCheckbox = function(title)
+{
+ WebInspector.StatusBarItem.call(this, "label");
+ this.element.classList.add("status-bar-item", "checkbox");
+ this.inputElement = this.element.createChild("input");
+ this.inputElement.type = "checkbox";
+ this.element.createTextChild(title);
+}
+
+WebInspector.StatusBarCheckbox.prototype = {
+ /**
+ * @return {boolean}
+ */
+ checked: function()
+ {
+ return this.inputElement.checked;
+ },
+
+ __proto__: WebInspector.StatusBarItem.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.StatusBarButton}
+ * @param {string} className
+ * @param {!Array.<string>} states
+ * @param {!Array.<string>} titles
+ * @param {string} initialState
+ * @param {!WebInspector.Setting} currentStateSetting
+ * @param {!WebInspector.Setting} lastStateSetting
+ * @param {?function(string)} stateChangedCallback
+ */
+WebInspector.StatusBarStatesSettingButton = function(className, states, titles, initialState, currentStateSetting, lastStateSetting, stateChangedCallback)
+{
+ WebInspector.StatusBarButton.call(this, "", className, states.length);
+
+ var onClickBound = this._onClick.bind(this);
+ this.addEventListener("click", onClickBound, this);
+
+ this._states = states;
+ this._buttons = [];
+ for (var index = 0; index < states.length; index++) {
+ var button = new WebInspector.StatusBarButton(titles[index], className, states.length);
+ button.state = this._states[index];
+ button.addEventListener("click", onClickBound, this);
+ this._buttons.push(button);
+ }
+
+ this._currentStateSetting = currentStateSetting;
+ this._lastStateSetting = lastStateSetting;
+ this._stateChangedCallback = stateChangedCallback;
+ this.setLongClickOptionsEnabled(this._createOptions.bind(this));
+
+ this._currentState = null;
+ this.toggleState(initialState);
+}
+
+WebInspector.StatusBarStatesSettingButton.prototype = {
+ /**
+ * @param {!WebInspector.Event} e
+ */
+ _onClick: function(e)
+ {
+ this.toggleState(e.target.state);
+ },
+
+ /**
+ * @param {string} state
+ */
+ toggleState: function(state)
+ {
+ if (this._currentState === state)
+ return;
+
+ if (this._currentState)
+ this._lastStateSetting.set(this._currentState);
+ this._currentState = state;
+ this._currentStateSetting.set(this._currentState);
+
+ if (this._stateChangedCallback)
+ this._stateChangedCallback(state);
+
+ var defaultState = this._defaultState();
+ this.state = defaultState;
+ this.title = this._buttons[this._states.indexOf(defaultState)].title;
+ },
+
+ /**
+ * @return {string}
+ */
+ _defaultState: function()
+ {
+ var lastState = this._lastStateSetting.get();
+ if (lastState && this._states.indexOf(lastState) >= 0 && lastState != this._currentState)
+ return lastState;
+ if (this._states.length > 1 && this._currentState === this._states[0])
+ return this._states[1];
+ return this._states[0];
+ },
+
+ /**
+ * @return {!Array.<!WebInspector.StatusBarButton>}
+ */
+ _createOptions: function()
+ {
+ var options = [];
+ for (var index = 0; index < this._states.length; index++) {
+ if (this._states[index] !== this.state && this._states[index] !== this._currentState)
+ options.push(this._buttons[index]);
+ }
+ return options;
+ },
+
+ __proto__: WebInspector.StatusBarButton.prototype
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js
new file mode 100644
index 00000000000..2f805dfc080
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2013 Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+/**
+ * @interface
+ */
+WebInspector.SuggestBoxDelegate = function()
+{
+}
+
+WebInspector.SuggestBoxDelegate.prototype = {
+ /**
+ * @param {string} suggestion
+ * @param {boolean=} isIntermediateSuggestion
+ */
+ applySuggestion: function(suggestion, isIntermediateSuggestion) { },
+
+ /**
+ * acceptSuggestion will be always called after call to applySuggestion with isIntermediateSuggestion being equal to false.
+ */
+ acceptSuggestion: function() { },
+}
+
+/**
+ * @constructor
+ * @param {!WebInspector.SuggestBoxDelegate} suggestBoxDelegate
+ * @param {number=} maxItemsHeight
+ */
+WebInspector.SuggestBox = function(suggestBoxDelegate, maxItemsHeight)
+{
+ this._suggestBoxDelegate = suggestBoxDelegate;
+ this._length = 0;
+ this._selectedIndex = -1;
+ this._selectedElement = null;
+ this._maxItemsHeight = maxItemsHeight;
+ this._bodyElement = document.body;
+ this._maybeHideBound = this._maybeHide.bind(this);
+ this._element = document.createElement("div");
+ this._element.className = "suggest-box";
+ this._element.addEventListener("mousedown", this._onBoxMouseDown.bind(this), true);
+}
+
+WebInspector.SuggestBox.prototype = {
+ /**
+ * @return {boolean}
+ */
+ visible: function()
+ {
+ return !!this._element.parentElement;
+ },
+
+ /**
+ * @param {!AnchorBox} anchorBox
+ */
+ setPosition: function(anchorBox)
+ {
+ this._updateBoxPosition(anchorBox);
+ },
+
+ /**
+ * @param {!AnchorBox} anchorBox
+ */
+ _updateBoxPosition: function(anchorBox)
+ {
+ console.assert(this._overlay);
+ if (this._lastAnchorBox && this._lastAnchorBox.equals(anchorBox))
+ return;
+ this._lastAnchorBox = anchorBox;
+
+ // Position relative to main DevTools element.
+ var container = WebInspector.Dialog.modalHostView().element;
+ anchorBox = anchorBox.relativeToElement(container);
+ var totalWidth = container.offsetWidth;
+ var totalHeight = container.offsetHeight;
+ var aboveHeight = anchorBox.y;
+ var underHeight = totalHeight - anchorBox.y - anchorBox.height;
+
+ var rowHeight = 17;
+ const spacer = 6;
+
+ var maxHeight = this._maxItemsHeight ? this._maxItemsHeight * rowHeight : Math.max(underHeight, aboveHeight) - spacer;
+ var under = underHeight >= aboveHeight;
+ this._leftSpacerElement.style.flexBasis = anchorBox.x + "px";
+
+ this._overlay.element.classList.toggle("under-anchor", under);
+
+ if (under) {
+ this._bottomSpacerElement.style.flexBasis = "auto";
+ this._topSpacerElement.style.flexBasis = (anchorBox.y + anchorBox.height) + "px";
+ } else {
+ this._bottomSpacerElement.style.flexBasis = (totalHeight - anchorBox.y) + "px";
+ this._topSpacerElement.style.flexBasis = "auto";
+ }
+ this._element.style.maxHeight = maxHeight + "px";
+ },
+
+ /**
+ * @param {?Event} event
+ */
+ _onBoxMouseDown: function(event)
+ {
+ if (this._hideTimeoutId) {
+ window.clearTimeout(this._hideTimeoutId);
+ delete this._hideTimeoutId;
+ }
+ event.preventDefault();
+ },
+
+ _maybeHide: function()
+ {
+ if (!this._hideTimeoutId)
+ this._hideTimeoutId = window.setTimeout(this.hide.bind(this), 0);
+ },
+
+ _show: function()
+ {
+ if (this.visible())
+ return;
+ this._overlay = new WebInspector.SuggestBox.Overlay();
+ this._bodyElement.addEventListener("mousedown", this._maybeHideBound, true);
+
+ this._leftSpacerElement = this._overlay.element.createChild("div", "suggest-box-left-spacer");
+ this._horizontalElement = this._overlay.element.createChild("div", "suggest-box-horizontal");
+ this._topSpacerElement = this._horizontalElement.createChild("div", "suggest-box-top-spacer");
+ this._horizontalElement.appendChild(this._element);
+ this._bottomSpacerElement = this._horizontalElement.createChild("div", "suggest-box-bottom-spacer");
+ },
+
+ hide: function()
+ {
+ if (!this.visible())
+ return;
+
+ this._bodyElement.removeEventListener("mousedown", this._maybeHideBound, true);
+ this._element.remove();
+ this._overlay.dispose();
+ delete this._overlay;
+ delete this._selectedElement;
+ this._selectedIndex = -1;
+ delete this._lastAnchorBox;
+ },
+
+ removeFromElement: function()
+ {
+ this.hide();
+ },
+
+ /**
+ * @param {boolean=} isIntermediateSuggestion
+ */
+ _applySuggestion: function(isIntermediateSuggestion)
+ {
+ if (!this.visible() || !this._selectedElement)
+ return false;
+
+ var suggestion = this._selectedElement.textContent;
+ if (!suggestion)
+ return false;
+
+ this._suggestBoxDelegate.applySuggestion(suggestion, isIntermediateSuggestion);
+ return true;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ acceptSuggestion: function()
+ {
+ var result = this._applySuggestion();
+ this.hide();
+ if (!result)
+ return false;
+
+ this._suggestBoxDelegate.acceptSuggestion();
+
+ return true;
+ },
+
+ /**
+ * @param {number} shift
+ * @param {boolean=} isCircular
+ * @return {boolean} is changed
+ */
+ _selectClosest: function(shift, isCircular)
+ {
+ if (!this._length)
+ return false;
+
+ if (this._selectedIndex === -1 && shift < 0)
+ shift += 1;
+
+ var index = this._selectedIndex + shift;
+
+ if (isCircular)
+ index = (this._length + index) % this._length;
+ else
+ index = Number.constrain(index, 0, this._length - 1);
+
+ this._selectItem(index, true);
+ this._applySuggestion(true);
+ return true;
+ },
+
+ /**
+ * @param {?Event} event
+ */
+ _onItemMouseDown: function(event)
+ {
+ this._selectedElement = event.currentTarget;
+ this.acceptSuggestion();
+ event.consume(true);
+ },
+
+ /**
+ * @param {string} prefix
+ * @param {string} text
+ */
+ _createItemElement: function(prefix, text)
+ {
+ var element = document.createElement("div");
+ element.className = "suggest-box-content-item source-code";
+ element.tabIndex = -1;
+ if (prefix && prefix.length && !text.indexOf(prefix)) {
+ var prefixElement = element.createChild("span", "prefix");
+ prefixElement.textContent = prefix;
+ var suffixElement = element.createChild("span", "suffix");
+ suffixElement.textContent = text.substring(prefix.length);
+ } else {
+ var suffixElement = element.createChild("span", "suffix");
+ suffixElement.textContent = text;
+ }
+ element.createChild("span", "spacer");
+ element.addEventListener("mousedown", this._onItemMouseDown.bind(this), false);
+ return element;
+ },
+
+ /**
+ * @param {!Array.<string>} items
+ * @param {string} userEnteredText
+ */
+ _updateItems: function(items, userEnteredText)
+ {
+ this._length = items.length;
+ this._element.removeChildren();
+ delete this._selectedElement;
+
+ for (var i = 0; i < items.length; ++i) {
+ var item = items[i];
+ var currentItemElement = this._createItemElement(userEnteredText, item);
+ this._element.appendChild(currentItemElement);
+ }
+ },
+
+ /**
+ * @param {number} index
+ * @param {boolean} scrollIntoView
+ */
+ _selectItem: function(index, scrollIntoView)
+ {
+ if (this._selectedElement)
+ this._selectedElement.classList.remove("selected");
+
+ this._selectedIndex = index;
+ if (index < 0)
+ return;
+
+ this._selectedElement = this._element.children[index];
+ this._selectedElement.classList.add("selected");
+
+ if (scrollIntoView)
+ this._selectedElement.scrollIntoViewIfNeeded(false);
+ },
+
+ /**
+ * @param {!Array.<string>} completions
+ * @param {boolean} canShowForSingleItem
+ * @param {string} userEnteredText
+ */
+ _canShowBox: function(completions, canShowForSingleItem, userEnteredText)
+ {
+ if (!completions || !completions.length)
+ return false;
+
+ if (completions.length > 1)
+ return true;
+
+ // Do not show a single suggestion if it is the same as user-entered prefix, even if allowed to show single-item suggest boxes.
+ return canShowForSingleItem && completions[0] !== userEnteredText;
+ },
+
+ _ensureRowCountPerViewport: function()
+ {
+ if (this._rowCountPerViewport)
+ return;
+ if (!this._element.firstChild)
+ return;
+
+ this._rowCountPerViewport = Math.floor(this._element.offsetHeight / this._element.firstChild.offsetHeight);
+ },
+
+ /**
+ * @param {!AnchorBox} anchorBox
+ * @param {!Array.<string>} completions
+ * @param {number} selectedIndex
+ * @param {boolean} canShowForSingleItem
+ * @param {string} userEnteredText
+ */
+ updateSuggestions: function(anchorBox, completions, selectedIndex, canShowForSingleItem, userEnteredText)
+ {
+ if (this._canShowBox(completions, canShowForSingleItem, userEnteredText)) {
+ this._updateItems(completions, userEnteredText);
+ this._show();
+ this._updateBoxPosition(anchorBox);
+ this._selectItem(selectedIndex, selectedIndex > 0);
+ delete this._rowCountPerViewport;
+ } else
+ this.hide();
+ },
+
+ /**
+ * @param {!KeyboardEvent} event
+ * @return {boolean}
+ */
+ keyPressed: function(event)
+ {
+ switch (event.keyIdentifier) {
+ case "Up":
+ return this.upKeyPressed();
+ case "Down":
+ return this.downKeyPressed();
+ case "PageUp":
+ return this.pageUpKeyPressed();
+ case "PageDown":
+ return this.pageDownKeyPressed();
+ case "Enter":
+ return this.enterKeyPressed();
+ }
+ return false;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ upKeyPressed: function()
+ {
+ return this._selectClosest(-1, true);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ downKeyPressed: function()
+ {
+ return this._selectClosest(1, true);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ pageUpKeyPressed: function()
+ {
+ this._ensureRowCountPerViewport();
+ return this._selectClosest(-this._rowCountPerViewport, false);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ pageDownKeyPressed: function()
+ {
+ this._ensureRowCountPerViewport();
+ return this._selectClosest(this._rowCountPerViewport, false);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ enterKeyPressed: function()
+ {
+ var hasSelectedItem = !!this._selectedElement;
+ this.acceptSuggestion();
+
+ // Report the event as non-handled if there is no selected item,
+ // to commit the input or handle it otherwise.
+ return hasSelectedItem;
+ }
+}
+
+/**
+ * @constructor
+ */
+WebInspector.SuggestBox.Overlay = function()
+{
+ this.element = document.createElement("div");
+ this.element.classList.add("suggest-box-overlay");
+ this._resize();
+ document.body.appendChild(this.element);
+}
+
+WebInspector.SuggestBox.Overlay.prototype = {
+ _resize: function()
+ {
+ var container = WebInspector.Dialog.modalHostView().element;
+ var containerBox = container.boxInWindow(container.ownerDocument.defaultView);
+
+ this.element.style.left = containerBox.x + "px";
+ this.element.style.top = containerBox.y + "px";
+ this.element.style.height = containerBox.height + "px";
+ this.element.style.width = containerBox.width + "px";
+ },
+
+ dispose: function()
+ {
+ this.element.remove();
+ }
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/TabbedPane.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/TabbedPane.js
new file mode 100644
index 00000000000..9d41fcb8094
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/TabbedPane.js
@@ -0,0 +1,1180 @@
+/*
+ * Copyright (C) 2010 Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+/**
+ * @extends {WebInspector.VBox}
+ * @constructor
+ */
+WebInspector.TabbedPane = function()
+{
+ WebInspector.VBox.call(this);
+ this.element.classList.add("tabbed-pane");
+ this.element.tabIndex = -1;
+ this._headerElement = this.element.createChild("div", "tabbed-pane-header");
+ this._headerContentsElement = this._headerElement.createChild("div", "tabbed-pane-header-contents");
+ this._tabsElement = this._headerContentsElement.createChild("div", "tabbed-pane-header-tabs");
+ this._contentElement = this.element.createChild("div", "tabbed-pane-content scroll-target");
+ /** @type {!Array.<!WebInspector.TabbedPaneTab>} */
+ this._tabs = [];
+ /** @type {!Array.<!WebInspector.TabbedPaneTab>} */
+ this._tabsHistory = [];
+ /** @type {!Object.<string, !WebInspector.TabbedPaneTab>} */
+ this._tabsById = {};
+
+ this._dropDownButton = this._createDropDownButton();
+ WebInspector.zoomManager.addEventListener(WebInspector.ZoomManager.Events.ZoomChanged, this._zoomChanged, this);
+}
+
+WebInspector.TabbedPane.EventTypes = {
+ TabSelected: "TabSelected",
+ TabClosed: "TabClosed"
+}
+
+WebInspector.TabbedPane.prototype = {
+ /**
+ * @return {?WebInspector.View}
+ */
+ get visibleView()
+ {
+ return this._currentTab ? this._currentTab.view : null;
+ },
+
+ /**
+ * @return {!Array.<!WebInspector.View>}
+ */
+ tabViews: function()
+ {
+ /**
+ * @param {!WebInspector.TabbedPaneTab} tab
+ * @return {!WebInspector.View}
+ */
+ function tabToView(tab)
+ {
+ return tab.view;
+ }
+ return this._tabs.map(tabToView);
+ },
+
+ /**
+ * @return {?string}
+ */
+ get selectedTabId()
+ {
+ return this._currentTab ? this._currentTab.id : null;
+ },
+
+ /**
+ * @type {boolean} shrinkableTabs
+ */
+ set shrinkableTabs(shrinkableTabs)
+ {
+ this._shrinkableTabs = shrinkableTabs;
+ },
+
+ /**
+ * @type {boolean} verticalTabLayout
+ */
+ set verticalTabLayout(verticalTabLayout)
+ {
+ this._verticalTabLayout = verticalTabLayout;
+ this.invalidateConstraints();
+ },
+
+ /**
+ * @type {boolean} closeableTabs
+ */
+ set closeableTabs(closeableTabs)
+ {
+ this._closeableTabs = closeableTabs;
+ },
+
+ /**
+ * @param {boolean} retainTabOrder
+ * @param {function(string, string):number=} tabOrderComparator
+ */
+ setRetainTabOrder: function(retainTabOrder, tabOrderComparator)
+ {
+ this._retainTabOrder = retainTabOrder;
+ this._tabOrderComparator = tabOrderComparator;
+ },
+
+ /**
+ * @return {?Element}
+ */
+ defaultFocusedElement: function()
+ {
+ return this.visibleView ? this.visibleView.defaultFocusedElement() : null;
+ },
+
+ focus: function()
+ {
+ if (this.visibleView)
+ this.visibleView.focus();
+ else
+ this.element.focus();
+ },
+
+ /**
+ * @return {!Element}
+ */
+ headerElement: function()
+ {
+ return this._headerElement;
+ },
+
+ /**
+ * @param {string} id
+ * @return {boolean}
+ */
+ isTabCloseable: function(id)
+ {
+ var tab = this._tabsById[id];
+ return tab ? tab.isCloseable() : false;
+ },
+
+ /**
+ * @param {!WebInspector.TabbedPaneTabDelegate} delegate
+ */
+ setTabDelegate: function(delegate)
+ {
+ var tabs = this._tabs.slice();
+ for (var i = 0; i < tabs.length; ++i)
+ tabs[i].setDelegate(delegate);
+ this._delegate = delegate;
+ },
+
+ /**
+ * @param {string} id
+ * @param {string} tabTitle
+ * @param {!WebInspector.View} view
+ * @param {string=} tabTooltip
+ * @param {boolean=} userGesture
+ * @param {boolean=} isCloseable
+ */
+ appendTab: function(id, tabTitle, view, tabTooltip, userGesture, isCloseable)
+ {
+ isCloseable = typeof isCloseable === "boolean" ? isCloseable : this._closeableTabs;
+ var tab = new WebInspector.TabbedPaneTab(this, id, tabTitle, isCloseable, view, tabTooltip);
+ tab.setDelegate(this._delegate);
+ this._tabsById[id] = tab;
+
+ /**
+ * @param {!WebInspector.TabbedPaneTab} tab1
+ * @param {!WebInspector.TabbedPaneTab} tab2
+ * @this {WebInspector.TabbedPane}
+ * @return {number}
+ */
+ function comparator(tab1, tab2)
+ {
+ return this._tabOrderComparator(tab1.id, tab2.id);
+ }
+
+ if (this._retainTabOrder && this._tabOrderComparator)
+ this._tabs.splice(insertionIndexForObjectInListSortedByFunction(tab, this._tabs, comparator.bind(this)), 0, tab);
+ else
+ this._tabs.push(tab);
+
+ this._tabsHistory.push(tab);
+
+ if (this._tabsHistory[0] === tab && this.isShowing())
+ this.selectTab(tab.id, userGesture);
+
+ this._updateTabElements();
+ },
+
+ /**
+ * @param {string} id
+ * @param {boolean=} userGesture
+ */
+ closeTab: function(id, userGesture)
+ {
+ this.closeTabs([id], userGesture);
+ },
+
+ /**
+ * @param {!Array.<string>} ids
+ * @param {boolean=} userGesture
+ */
+ closeTabs: function(ids, userGesture)
+ {
+ var focused = this.hasFocus();
+ for (var i = 0; i < ids.length; ++i)
+ this._innerCloseTab(ids[i], userGesture);
+ this._updateTabElements();
+ if (this._tabsHistory.length)
+ this.selectTab(this._tabsHistory[0].id, false);
+ if (focused)
+ this.focus();
+ },
+
+ /**
+ * @param {string} id
+ * @param {boolean=} userGesture
+ */
+ _innerCloseTab: function(id, userGesture)
+ {
+ if (!this._tabsById[id])
+ return;
+ if (userGesture && !this._tabsById[id]._closeable)
+ return;
+ if (this._currentTab && this._currentTab.id === id)
+ this._hideCurrentTab();
+
+ var tab = this._tabsById[id];
+ delete this._tabsById[id];
+
+ this._tabsHistory.splice(this._tabsHistory.indexOf(tab), 1);
+ this._tabs.splice(this._tabs.indexOf(tab), 1);
+ if (tab._shown)
+ this._hideTabElement(tab);
+
+ var eventData = { tabId: id, view: tab.view, isUserGesture: userGesture };
+ this.dispatchEventToListeners(WebInspector.TabbedPane.EventTypes.TabClosed, eventData);
+ return true;
+ },
+
+ /**
+ * @param {string} tabId
+ * @return {boolean}
+ */
+ hasTab: function(tabId)
+ {
+ return !!this._tabsById[tabId];
+ },
+
+ /**
+ * @return {!Array.<string>}
+ */
+ allTabs: function()
+ {
+ var result = [];
+ var tabs = this._tabs.slice();
+ for (var i = 0; i < tabs.length; ++i)
+ result.push(tabs[i].id);
+ return result;
+ },
+
+ /**
+ * @param {string} id
+ * @return {!Array.<string>}
+ */
+ otherTabs: function(id)
+ {
+ var result = [];
+ var tabs = this._tabs.slice();
+ for (var i = 0; i < tabs.length; ++i) {
+ if (tabs[i].id !== id)
+ result.push(tabs[i].id);
+ }
+ return result;
+ },
+
+ /**
+ * @param {string} id
+ * @param {boolean=} userGesture
+ * @return {boolean}
+ */
+ selectTab: function(id, userGesture)
+ {
+ var focused = this.hasFocus();
+ var tab = this._tabsById[id];
+ if (!tab)
+ return false;
+ if (this._currentTab && this._currentTab.id === id)
+ return true;
+
+ this._hideCurrentTab();
+ this._showTab(tab);
+ this._currentTab = tab;
+
+ this._tabsHistory.splice(this._tabsHistory.indexOf(tab), 1);
+ this._tabsHistory.splice(0, 0, tab);
+
+ this._updateTabElements();
+ if (focused)
+ this.focus();
+
+ var eventData = { tabId: id, view: tab.view, isUserGesture: userGesture };
+ this.dispatchEventToListeners(WebInspector.TabbedPane.EventTypes.TabSelected, eventData);
+ return true;
+ },
+
+ /**
+ * @param {number} tabsCount
+ * @return {!Array.<string>}
+ */
+ lastOpenedTabIds: function(tabsCount)
+ {
+ function tabToTabId(tab) {
+ return tab.id;
+ }
+
+ return this._tabsHistory.slice(0, tabsCount).map(tabToTabId);
+ },
+
+ /**
+ * @param {string} id
+ * @param {string} iconClass
+ * @param {string=} iconTooltip
+ */
+ setTabIcon: function(id, iconClass, iconTooltip)
+ {
+ var tab = this._tabsById[id];
+ if (tab._setIconClass(iconClass, iconTooltip))
+ this._updateTabElements();
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _zoomChanged: function(event)
+ {
+ for (var i = 0; i < this._tabs.length; ++i)
+ delete this._tabs[i]._measuredWidth;
+ if (this.isShowing())
+ this._updateTabElements();
+ },
+
+ /**
+ * @param {string} id
+ * @param {string} tabTitle
+ */
+ changeTabTitle: function(id, tabTitle)
+ {
+ var tab = this._tabsById[id];
+ if (tab.title === tabTitle)
+ return;
+ tab.title = tabTitle;
+ this._updateTabElements();
+ },
+
+ /**
+ * @param {string} id
+ * @param {!WebInspector.View} view
+ */
+ changeTabView: function(id, view)
+ {
+ var tab = this._tabsById[id];
+ if (this._currentTab && this._currentTab.id === tab.id) {
+ if (tab.view !== view)
+ this._hideTab(tab);
+ tab.view = view;
+ this._showTab(tab);
+ } else
+ tab.view = view;
+ },
+
+ /**
+ * @param {string} id
+ * @param {string=} tabTooltip
+ */
+ changeTabTooltip: function(id, tabTooltip)
+ {
+ var tab = this._tabsById[id];
+ tab.tooltip = tabTooltip;
+ },
+
+ onResize: function()
+ {
+ this._updateTabElements();
+ },
+
+ headerResized: function()
+ {
+ this._updateTabElements();
+ },
+
+ wasShown: function()
+ {
+ var effectiveTab = this._currentTab || this._tabsHistory[0];
+ if (effectiveTab)
+ this.selectTab(effectiveTab.id);
+ },
+
+ /**
+ * @return {!Constraints}
+ */
+ calculateConstraints: function()
+ {
+ var constraints = WebInspector.VBox.prototype.calculateConstraints.call(this);
+ var minContentConstraints = new Constraints(new Size(0, 0), new Size(50, 50));
+ constraints = constraints.widthToMax(minContentConstraints).heightToMax(minContentConstraints);
+ if (this._verticalTabLayout)
+ constraints = constraints.addWidth(new Constraints(new Size(this._headerElement.offsetWidth, 0)));
+ else
+ constraints = constraints.addHeight(new Constraints(new Size(0, this._headerElement.offsetHeight)));
+ return constraints;
+ },
+
+ _updateTabElements: function()
+ {
+ WebInspector.invokeOnceAfterBatchUpdate(this, this._innerUpdateTabElements);
+ },
+
+ /**
+ * @param {string} text
+ */
+ setPlaceholderText: function(text)
+ {
+ this._noTabsMessage = text;
+ },
+
+ _innerUpdateTabElements: function()
+ {
+ if (!this.isShowing())
+ return;
+
+ if (!this._tabs.length) {
+ this._contentElement.classList.add("has-no-tabs");
+ if (this._noTabsMessage && !this._noTabsMessageElement) {
+ this._noTabsMessageElement = this._contentElement.createChild("div", "tabbed-pane-placeholder fill");
+ this._noTabsMessageElement.textContent = this._noTabsMessage;
+ }
+ } else {
+ this._contentElement.classList.remove("has-no-tabs");
+ if (this._noTabsMessageElement) {
+ this._noTabsMessageElement.remove();
+ delete this._noTabsMessageElement;
+ }
+ }
+
+ if (!this._measuredDropDownButtonWidth)
+ this._measureDropDownButton();
+
+ this._updateWidths();
+ this._updateTabsDropDown();
+ },
+
+ /**
+ * @param {number} index
+ * @param {!WebInspector.TabbedPaneTab} tab
+ */
+ _showTabElement: function(index, tab)
+ {
+ if (index >= this._tabsElement.children.length)
+ this._tabsElement.appendChild(tab.tabElement);
+ else
+ this._tabsElement.insertBefore(tab.tabElement, this._tabsElement.children[index]);
+ tab._shown = true;
+ },
+
+ /**
+ * @param {!WebInspector.TabbedPaneTab} tab
+ */
+ _hideTabElement: function(tab)
+ {
+ this._tabsElement.removeChild(tab.tabElement);
+ tab._shown = false;
+ },
+
+ _createDropDownButton: function()
+ {
+ var dropDownContainer = document.createElement("div");
+ dropDownContainer.classList.add("tabbed-pane-header-tabs-drop-down-container");
+ var dropDownButton = dropDownContainer.createChild("div", "tabbed-pane-header-tabs-drop-down");
+ dropDownButton.appendChild(document.createTextNode("\u00bb"));
+
+ this._dropDownMenu = new WebInspector.DropDownMenu();
+ this._dropDownMenu.addEventListener(WebInspector.DropDownMenu.Events.ItemSelected, this._dropDownMenuItemSelected, this);
+ dropDownButton.appendChild(this._dropDownMenu.element);
+
+ return dropDownContainer;
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _dropDownMenuItemSelected: function(event)
+ {
+ var tabId = /** @type {string} */ (event.data);
+ this.selectTab(tabId, true);
+ },
+
+ _totalWidth: function()
+ {
+ return this._headerContentsElement.getBoundingClientRect().width;
+ },
+
+ _updateTabsDropDown: function()
+ {
+ var tabsToShowIndexes = this._tabsToShowIndexes(this._tabs, this._tabsHistory, this._totalWidth(), this._measuredDropDownButtonWidth);
+
+ for (var i = 0; i < this._tabs.length; ++i) {
+ if (this._tabs[i]._shown && tabsToShowIndexes.indexOf(i) === -1)
+ this._hideTabElement(this._tabs[i]);
+ }
+ for (var i = 0; i < tabsToShowIndexes.length; ++i) {
+ var tab = this._tabs[tabsToShowIndexes[i]];
+ if (!tab._shown)
+ this._showTabElement(i, tab);
+ }
+
+ this._populateDropDownFromIndex();
+ },
+
+ _populateDropDownFromIndex: function()
+ {
+ if (this._dropDownButton.parentElement)
+ this._headerContentsElement.removeChild(this._dropDownButton);
+
+ this._dropDownMenu.clear();
+
+ var tabsToShow = [];
+ for (var i = 0; i < this._tabs.length; ++i) {
+ if (!this._tabs[i]._shown)
+ tabsToShow.push(this._tabs[i]);
+ continue;
+ }
+
+ function compareFunction(tab1, tab2)
+ {
+ return tab1.title.localeCompare(tab2.title);
+ }
+ if (!this._retainTabOrder)
+ tabsToShow.sort(compareFunction);
+
+ var selectedId = null;
+ for (var i = 0; i < tabsToShow.length; ++i) {
+ var tab = tabsToShow[i];
+ this._dropDownMenu.addItem(tab.id, tab.title);
+ if (this._tabsHistory[0] === tab)
+ selectedId = tab.id;
+ }
+ if (tabsToShow.length) {
+ this._headerContentsElement.appendChild(this._dropDownButton);
+ this._dropDownMenu.selectItem(selectedId);
+ }
+ },
+
+ _measureDropDownButton: function()
+ {
+ this._dropDownButton.classList.add("measuring");
+ this._headerContentsElement.appendChild(this._dropDownButton);
+ this._measuredDropDownButtonWidth = this._dropDownButton.getBoundingClientRect().width;
+ this._headerContentsElement.removeChild(this._dropDownButton);
+ this._dropDownButton.classList.remove("measuring");
+ },
+
+ _updateWidths: function()
+ {
+ var measuredWidths = this._measureWidths();
+ var maxWidth = this._shrinkableTabs ? this._calculateMaxWidth(measuredWidths.slice(), this._totalWidth()) : Number.MAX_VALUE;
+
+ var i = 0;
+ for (var tabId in this._tabs) {
+ var tab = this._tabs[tabId];
+ tab.setWidth(this._verticalTabLayout ? -1 : Math.min(maxWidth, measuredWidths[i++]));
+ }
+ },
+
+ _measureWidths: function()
+ {
+ // Add all elements to measure into this._tabsElement
+ this._tabsElement.style.setProperty("width", "2000px");
+ var measuringTabElements = [];
+ for (var tabId in this._tabs) {
+ var tab = this._tabs[tabId];
+ if (typeof tab._measuredWidth === "number")
+ continue;
+ var measuringTabElement = tab._createTabElement(true);
+ measuringTabElement.__tab = tab;
+ measuringTabElements.push(measuringTabElement);
+ this._tabsElement.appendChild(measuringTabElement);
+ }
+
+ // Perform measurement
+ for (var i = 0; i < measuringTabElements.length; ++i)
+ measuringTabElements[i].__tab._measuredWidth = measuringTabElements[i].getBoundingClientRect().width;
+
+ // Nuke elements from the UI
+ for (var i = 0; i < measuringTabElements.length; ++i)
+ measuringTabElements[i].remove();
+
+ // Combine the results.
+ var measuredWidths = [];
+ for (var tabId in this._tabs)
+ measuredWidths.push(this._tabs[tabId]._measuredWidth);
+ this._tabsElement.style.removeProperty("width");
+
+ return measuredWidths;
+ },
+
+ /**
+ * @param {!Array.<number>} measuredWidths
+ * @param {number} totalWidth
+ */
+ _calculateMaxWidth: function(measuredWidths, totalWidth)
+ {
+ if (!measuredWidths.length)
+ return 0;
+
+ measuredWidths.sort(function(x, y) { return x - y });
+
+ var totalMeasuredWidth = 0;
+ for (var i = 0; i < measuredWidths.length; ++i)
+ totalMeasuredWidth += measuredWidths[i];
+
+ if (totalWidth >= totalMeasuredWidth)
+ return measuredWidths[measuredWidths.length - 1];
+
+ var totalExtraWidth = 0;
+ for (var i = measuredWidths.length - 1; i > 0; --i) {
+ var extraWidth = measuredWidths[i] - measuredWidths[i - 1];
+ totalExtraWidth += (measuredWidths.length - i) * extraWidth;
+
+ if (totalWidth + totalExtraWidth >= totalMeasuredWidth)
+ return measuredWidths[i - 1] + (totalWidth + totalExtraWidth - totalMeasuredWidth) / (measuredWidths.length - i);
+ }
+
+ return totalWidth / measuredWidths.length;
+ },
+
+ /**
+ * @param {!Array.<!WebInspector.TabbedPaneTab>} tabsOrdered
+ * @param {!Array.<!WebInspector.TabbedPaneTab>} tabsHistory
+ * @param {number} totalWidth
+ * @param {number} measuredDropDownButtonWidth
+ * @return {!Array.<number>}
+ */
+ _tabsToShowIndexes: function(tabsOrdered, tabsHistory, totalWidth, measuredDropDownButtonWidth)
+ {
+ var tabsToShowIndexes = [];
+
+ var totalTabsWidth = 0;
+ var tabCount = tabsOrdered.length;
+ for (var i = 0; i < tabCount; ++i) {
+ var tab = this._retainTabOrder ? tabsOrdered[i] : tabsHistory[i];
+ totalTabsWidth += tab.width();
+ var minimalRequiredWidth = totalTabsWidth;
+ if (i !== tabCount - 1)
+ minimalRequiredWidth += measuredDropDownButtonWidth;
+ if (!this._verticalTabLayout && minimalRequiredWidth > totalWidth)
+ break;
+ tabsToShowIndexes.push(tabsOrdered.indexOf(tab));
+ }
+
+ tabsToShowIndexes.sort(function(x, y) { return x - y });
+
+ return tabsToShowIndexes;
+ },
+
+ _hideCurrentTab: function()
+ {
+ if (!this._currentTab)
+ return;
+
+ this._hideTab(this._currentTab);
+ delete this._currentTab;
+ },
+
+ /**
+ * @param {!WebInspector.TabbedPaneTab} tab
+ */
+ _showTab: function(tab)
+ {
+ tab.tabElement.classList.add("selected");
+ tab.view.show(this._contentElement);
+ },
+
+ /**
+ * @param {!WebInspector.TabbedPaneTab} tab
+ */
+ _hideTab: function(tab)
+ {
+ tab.tabElement.classList.remove("selected");
+ tab.view.detach();
+ },
+
+ /**
+ * @return {!Array.<!Element>}
+ */
+ elementsToRestoreScrollPositionsFor: function()
+ {
+ return [ this._contentElement ];
+ },
+
+ /**
+ * @param {!WebInspector.TabbedPaneTab} tab
+ * @param {number} index
+ */
+ _insertBefore: function(tab, index)
+ {
+ this._tabsElement.insertBefore(tab._tabElement, this._tabsElement.childNodes[index]);
+ var oldIndex = this._tabs.indexOf(tab);
+ this._tabs.splice(oldIndex, 1);
+ if (oldIndex < index)
+ --index;
+ this._tabs.splice(index, 0, tab);
+ },
+
+ __proto__: WebInspector.VBox.prototype
+}
+
+/**
+ * @constructor
+ * @param {!WebInspector.TabbedPane} tabbedPane
+ * @param {string} id
+ * @param {string} title
+ * @param {boolean} closeable
+ * @param {!WebInspector.View} view
+ * @param {string=} tooltip
+ */
+WebInspector.TabbedPaneTab = function(tabbedPane, id, title, closeable, view, tooltip)
+{
+ this._closeable = closeable;
+ this._tabbedPane = tabbedPane;
+ this._id = id;
+ this._title = title;
+ this._tooltip = tooltip;
+ this._view = view;
+ this._shown = false;
+ /** @type {number} */ this._measuredWidth;
+ /** @type {!Element|undefined} */ this._tabElement;
+}
+
+WebInspector.TabbedPaneTab.prototype = {
+ /**
+ * @return {string}
+ */
+ get id()
+ {
+ return this._id;
+ },
+
+ /**
+ * @return {string}
+ */
+ get title()
+ {
+ return this._title;
+ },
+
+ set title(title)
+ {
+ if (title === this._title)
+ return;
+ this._title = title;
+ if (this._titleElement)
+ this._titleElement.textContent = title;
+ delete this._measuredWidth;
+ },
+
+ /**
+ * @return {string}
+ */
+ iconClass: function()
+ {
+ return this._iconClass;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isCloseable: function()
+ {
+ return this._closeable;
+ },
+
+ /**
+ * @param {string} iconClass
+ * @param {string} iconTooltip
+ * @return {boolean}
+ */
+ _setIconClass: function(iconClass, iconTooltip)
+ {
+ if (iconClass === this._iconClass && iconTooltip === this._iconTooltip)
+ return false;
+ this._iconClass = iconClass;
+ this._iconTooltip = iconTooltip;
+ if (this._iconElement)
+ this._iconElement.remove();
+ if (this._iconClass && this._tabElement)
+ this._iconElement = this._createIconElement(this._tabElement, this._titleElement);
+ delete this._measuredWidth;
+ return true;
+ },
+
+ /**
+ * @return {!WebInspector.View}
+ */
+ get view()
+ {
+ return this._view;
+ },
+
+ set view(view)
+ {
+ this._view = view;
+ },
+
+ /**
+ * @return {string|undefined}
+ */
+ get tooltip()
+ {
+ return this._tooltip;
+ },
+
+ set tooltip(tooltip)
+ {
+ this._tooltip = tooltip;
+ if (this._titleElement)
+ this._titleElement.title = tooltip || "";
+ },
+
+ /**
+ * @return {!Element}
+ */
+ get tabElement()
+ {
+ if (!this._tabElement)
+ this._tabElement = this._createTabElement(false);
+
+ return this._tabElement;
+ },
+
+ /**
+ * @return {number}
+ */
+ width: function()
+ {
+ return this._width;
+ },
+
+ /**
+ * @param {number} width
+ */
+ setWidth: function(width)
+ {
+ this.tabElement.style.width = width === -1 ? "" : (width + "px");
+ this._width = width;
+ },
+
+ /**
+ * @param {!WebInspector.TabbedPaneTabDelegate} delegate
+ */
+ setDelegate: function(delegate)
+ {
+ this._delegate = delegate;
+ },
+
+ _createIconElement: function(tabElement, titleElement)
+ {
+ var iconElement = document.createElement("span");
+ iconElement.className = "tabbed-pane-header-tab-icon " + this._iconClass;
+ if (this._iconTooltip)
+ iconElement.title = this._iconTooltip;
+ tabElement.insertBefore(iconElement, titleElement);
+ return iconElement;
+ },
+
+ /**
+ * @param {boolean} measuring
+ * @return {!Element}
+ */
+ _createTabElement: function(measuring)
+ {
+ var tabElement = document.createElement("div");
+ tabElement.classList.add("tabbed-pane-header-tab");
+ tabElement.id = "tab-" + this._id;
+ tabElement.tabIndex = -1;
+ tabElement.selectTabForTest = this._tabbedPane.selectTab.bind(this._tabbedPane, this.id, true);
+
+ var titleElement = tabElement.createChild("span", "tabbed-pane-header-tab-title");
+ titleElement.textContent = this.title;
+ titleElement.title = this.tooltip || "";
+ if (this._iconClass)
+ this._createIconElement(tabElement, titleElement);
+ if (!measuring)
+ this._titleElement = titleElement;
+
+ if (this._closeable)
+ tabElement.createChild("div", "close-button-gray");
+
+ if (measuring) {
+ tabElement.classList.add("measuring");
+ } else {
+ tabElement.addEventListener("click", this._tabClicked.bind(this), false);
+ tabElement.addEventListener("mousedown", this._tabMouseDown.bind(this), false);
+ tabElement.addEventListener("mouseup", this._tabMouseUp.bind(this), false);
+
+ if (this._closeable) {
+ tabElement.addEventListener("contextmenu", this._tabContextMenu.bind(this), false);
+ WebInspector.installDragHandle(tabElement, this._startTabDragging.bind(this), this._tabDragging.bind(this), this._endTabDragging.bind(this), "pointer");
+ }
+ }
+
+ return tabElement;
+ },
+
+ /**
+ * @param {?Event} event
+ */
+ _tabClicked: function(event)
+ {
+ var middleButton = event.button === 1;
+ var shouldClose = this._closeable && (middleButton || event.target.classList.contains("close-button-gray"));
+ if (!shouldClose) {
+ this._tabbedPane.focus();
+ return;
+ }
+ this._closeTabs([this.id]);
+ event.consume(true);
+ },
+
+ /**
+ * @param {?Event} event
+ */
+ _tabMouseDown: function(event)
+ {
+ if (event.target.classList.contains("close-button-gray") || event.button === 1)
+ return;
+ this._tabbedPane.selectTab(this.id, true);
+ },
+
+ /**
+ * @param {?Event} event
+ */
+ _tabMouseUp: function(event)
+ {
+ // This is needed to prevent middle-click pasting on linux when tabs are clicked.
+ if (event.button === 1)
+ event.consume(true);
+ },
+
+ /**
+ * @param {!Array.<string>} ids
+ */
+ _closeTabs: function(ids)
+ {
+ if (this._delegate) {
+ this._delegate.closeTabs(this._tabbedPane, ids);
+ return;
+ }
+ this._tabbedPane.closeTabs(ids, true);
+ },
+
+ _tabContextMenu: function(event)
+ {
+ /**
+ * @this {WebInspector.TabbedPaneTab}
+ */
+ function close()
+ {
+ this._closeTabs([this.id]);
+ }
+
+ /**
+ * @this {WebInspector.TabbedPaneTab}
+ */
+ function closeOthers()
+ {
+ this._closeTabs(this._tabbedPane.otherTabs(this.id));
+ }
+
+ /**
+ * @this {WebInspector.TabbedPaneTab}
+ */
+ function closeAll()
+ {
+ this._closeTabs(this._tabbedPane.allTabs());
+ }
+
+ var contextMenu = new WebInspector.ContextMenu(event);
+ contextMenu.appendItem(WebInspector.UIString("Close"), close.bind(this));
+ contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Close others" : "Close Others"), closeOthers.bind(this));
+ contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Close all" : "Close All"), closeAll.bind(this));
+ contextMenu.show();
+ },
+
+ /**
+ * @param {!Event} event
+ * @return {boolean}
+ */
+ _startTabDragging: function(event)
+ {
+ if (event.target.classList.contains("close-button-gray"))
+ return false;
+ this._dragStartX = event.pageX;
+ return true;
+ },
+
+ /**
+ * @param {!Event} event
+ */
+ _tabDragging: function(event)
+ {
+ var tabElements = this._tabbedPane._tabsElement.childNodes;
+ for (var i = 0; i < tabElements.length; ++i) {
+ var tabElement = tabElements[i];
+ if (tabElement === this._tabElement)
+ continue;
+
+ var intersects = tabElement.offsetLeft + tabElement.clientWidth > this._tabElement.offsetLeft &&
+ this._tabElement.offsetLeft + this._tabElement.clientWidth > tabElement.offsetLeft;
+ if (!intersects)
+ continue;
+
+ if (Math.abs(event.pageX - this._dragStartX) < tabElement.clientWidth / 2 + 5)
+ break;
+
+ if (event.pageX - this._dragStartX > 0) {
+ tabElement = tabElement.nextSibling;
+ ++i;
+ }
+
+ var oldOffsetLeft = this._tabElement.offsetLeft;
+ this._tabbedPane._insertBefore(this, i);
+ this._dragStartX += this._tabElement.offsetLeft - oldOffsetLeft;
+ break;
+ }
+
+ if (!this._tabElement.previousSibling && event.pageX - this._dragStartX < 0) {
+ this._tabElement.style.setProperty("left", "0px");
+ return;
+ }
+ if (!this._tabElement.nextSibling && event.pageX - this._dragStartX > 0) {
+ this._tabElement.style.setProperty("left", "0px");
+ return;
+ }
+
+ this._tabElement.style.setProperty("position", "relative");
+ this._tabElement.style.setProperty("left", (event.pageX - this._dragStartX) + "px");
+ },
+
+ /**
+ * @param {!Event} event
+ */
+ _endTabDragging: function(event)
+ {
+ this._tabElement.style.removeProperty("position");
+ this._tabElement.style.removeProperty("left");
+ delete this._dragStartX;
+ }
+}
+
+/**
+ * @interface
+ */
+WebInspector.TabbedPaneTabDelegate = function()
+{
+}
+
+WebInspector.TabbedPaneTabDelegate.prototype = {
+ /**
+ * @param {!WebInspector.TabbedPane} tabbedPane
+ * @param {!Array.<string>} ids
+ */
+ closeTabs: function(tabbedPane, ids) { }
+}
+
+/**
+ * @constructor
+ * @param {!WebInspector.TabbedPane} tabbedPane
+ * @param {string} extensionPoint
+ * @param {function(string, !WebInspector.View)=} viewCallback
+ */
+WebInspector.ExtensibleTabbedPaneController = function(tabbedPane, extensionPoint, viewCallback)
+{
+ this._tabbedPane = tabbedPane;
+ this._extensionPoint = extensionPoint;
+ this._viewCallback = viewCallback;
+
+ this._tabbedPane.setRetainTabOrder(true, WebInspector.moduleManager.orderComparator(extensionPoint, "name", "order"));
+ this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
+ /** @type {!StringMap.<?WebInspector.View>} */
+ this._views = new StringMap();
+ this._initialize();
+}
+
+WebInspector.ExtensibleTabbedPaneController.prototype = {
+ _initialize: function()
+ {
+ this._extensions = {};
+ var extensions = WebInspector.moduleManager.extensions(this._extensionPoint);
+
+ for (var i = 0; i < extensions.length; ++i) {
+ var descriptor = extensions[i].descriptor();
+ var id = descriptor["name"];
+ var title = WebInspector.UIString(descriptor["title"]);
+ var settingName = descriptor["setting"];
+ var setting = settingName ? /** @type {!WebInspector.Setting|undefined} */ (WebInspector.settings[settingName]) : null;
+
+ this._extensions[id] = extensions[i];
+
+ if (setting) {
+ setting.addChangeListener(this._toggleSettingBasedView.bind(this, id, title, setting));
+ if (setting.get())
+ this._tabbedPane.appendTab(id, title, new WebInspector.View());
+ } else {
+ this._tabbedPane.appendTab(id, title, new WebInspector.View());
+ }
+ }
+ },
+
+ /**
+ * @param {string} id
+ * @param {string} title
+ * @param {!WebInspector.Setting} setting
+ */
+ _toggleSettingBasedView: function(id, title, setting)
+ {
+ this._tabbedPane.closeTab(id);
+ if (setting.get())
+ this._tabbedPane.appendTab(id, title, new WebInspector.View());
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _tabSelected: function(event)
+ {
+ var tabId = this._tabbedPane.selectedTabId;
+ if (!tabId)
+ return;
+ var view = this._viewForId(tabId);
+ if (view)
+ this._tabbedPane.changeTabView(tabId, view);
+ },
+
+ /**
+ * @return {?WebInspector.View}
+ */
+ _viewForId: function(id)
+ {
+ if (this._views.contains(id))
+ return /** @type {!WebInspector.View} */ (this._views.get(id));
+ var view = this._extensions[id] ? /** @type {!WebInspector.View} */ (this._extensions[id].instance()) : null;
+ this._views.put(id, view);
+ if (this._viewCallback && view)
+ this._viewCallback(id, view);
+ return view;
+ }
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/TextEditor.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/TextEditor.js
new file mode 100644
index 00000000000..ee36f9ec4f8
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/TextEditor.js
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2010 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+/**
+ * @interface
+ */
+WebInspector.TextEditor = function() { };
+
+WebInspector.TextEditor.Events = {
+ GutterClick: "gutterClick"
+};
+
+/** @typedef {{lineNumber: number, event: !Event}} */
+WebInspector.TextEditor.GutterClickEventData;
+
+WebInspector.TextEditor.prototype = {
+
+ undo: function() { },
+
+ redo: function() { },
+
+ /**
+ * @return {boolean}
+ */
+ isClean: function() { },
+
+ markClean: function() { },
+
+ /**
+ * @return {string}
+ */
+ indent: function() { },
+
+ /**
+ * @param {number} lineNumber
+ * @param {number} column
+ * @return {?{x: number, y: number, height: number}}
+ */
+ cursorPositionToCoordinates: function(lineNumber, column) { return null; },
+
+ /**
+ * @param {number} x
+ * @param {number} y
+ * @return {?WebInspector.TextRange}
+ */
+ coordinatesToCursorPosition: function(x, y) { return null; },
+
+ /**
+ * @param {number} lineNumber
+ * @param {number} column
+ * @return {?{startColumn: number, endColumn: number, type: string}}
+ */
+ tokenAtTextPosition: function(lineNumber, column) { return null; },
+
+ /**
+ * @param {string} mimeType
+ */
+ setMimeType: function(mimeType) { },
+
+ /**
+ * @param {boolean} readOnly
+ */
+ setReadOnly: function(readOnly) { },
+
+ /**
+ * @return {boolean}
+ */
+ readOnly: function() { },
+
+ /**
+ * @return {!Element}
+ */
+ defaultFocusedElement: function() { },
+
+ /**
+ * @param {!WebInspector.TextRange} range
+ * @param {string} cssClass
+ * @return {!Object}
+ */
+ highlightRange: function(range, cssClass) { },
+
+ /**
+ * @param {!Object} highlightDescriptor
+ */
+ removeHighlight: function(highlightDescriptor) { },
+
+ /**
+ * @param {number} lineNumber
+ * @param {boolean} disabled
+ * @param {boolean} conditional
+ */
+ addBreakpoint: function(lineNumber, disabled, conditional) { },
+
+ /**
+ * @param {number} lineNumber
+ */
+ removeBreakpoint: function(lineNumber) { },
+
+ /**
+ * @param {number} lineNumber
+ */
+ setExecutionLine: function(lineNumber) { },
+
+ clearExecutionLine: function() { },
+
+ /**
+ * @param {number} lineNumber
+ * @param {!Element} element
+ */
+ addDecoration: function(lineNumber, element) { },
+
+ /**
+ * @param {number} lineNumber
+ * @param {!Element} element
+ */
+ removeDecoration: function(lineNumber, element) { },
+
+ /**
+ * @param {!RegExp} regex
+ * @param {?WebInspector.TextRange} range
+ */
+ highlightSearchResults: function(regex, range) { },
+
+ /**
+ * @param {number} lineNumber
+ * @param {number=} columnNumber
+ * @param {boolean=} shouldHighlight
+ */
+ revealPosition: function(lineNumber, columnNumber, shouldHighlight) { },
+
+ clearPositionHighlight: function() { },
+
+ /**
+ * @return {!Array.<!Element>}
+ */
+ elementsToRestoreScrollPositionsFor: function() { },
+
+ /**
+ * @param {!WebInspector.TextEditor} textEditor
+ */
+ inheritScrollPositions: function(textEditor) { },
+
+ beginUpdates: function() { },
+
+ endUpdates: function() { },
+
+ onResize: function() { },
+
+ /**
+ * @param {!WebInspector.TextRange} range
+ * @param {string} text
+ * @return {!WebInspector.TextRange}
+ */
+ editRange: function(range, text) { },
+
+ /**
+ * @param {number} lineNumber
+ */
+ scrollToLine: function(lineNumber) { },
+
+ /**
+ * @return {number}
+ */
+ firstVisibleLine: function() { },
+
+ /**
+ * @return {number}
+ */
+ lastVisibleLine: function() { },
+
+ /**
+ * @return {!WebInspector.TextRange}
+ */
+ selection: function() { },
+
+ /**
+ * @return {!Array.<!WebInspector.TextRange>}
+ */
+ selections: function() { },
+
+ /**
+ * @return {?WebInspector.TextRange}
+ */
+ lastSelection: function() { },
+
+ /**
+ * @param {!WebInspector.TextRange} textRange
+ */
+ setSelection: function(textRange) { },
+
+ /**
+ * @param {!WebInspector.TextRange} range
+ * @return {string}
+ */
+ copyRange: function(range) { },
+
+ /**
+ * @param {string} text
+ */
+ setText: function(text) { },
+
+ /**
+ * @return {string}
+ */
+ text: function() { },
+
+ /**
+ * @return {!WebInspector.TextRange}
+ */
+ range: function() { },
+
+ /**
+ * @param {number} lineNumber
+ * @return {string}
+ */
+ line: function(lineNumber) { },
+
+ /**
+ * @return {number}
+ */
+ get linesCount() { },
+
+ /**
+ * @param {number} line
+ * @param {string} name
+ * @param {?Object} value
+ */
+ setAttribute: function(line, name, value) { },
+
+ /**
+ * @param {number} line
+ * @param {string} name
+ * @return {?Object} value
+ */
+ getAttribute: function(line, name) { },
+
+ /**
+ * @param {number} line
+ * @param {string} name
+ */
+ removeAttribute: function(line, name) { },
+
+ wasShown: function() { },
+
+ willHide: function() { },
+
+ /**
+ * @param {?WebInspector.CompletionDictionary} dictionary
+ */
+ setCompletionDictionary: function(dictionary) { },
+
+ /**
+ * @param {number} lineNumber
+ * @param {number} columnNumber
+ * @return {?WebInspector.TextEditorPositionHandle}
+ */
+ textEditorPositionHandle: function(lineNumber, columnNumber) { },
+
+ dispose: function() { }
+}
+
+/**
+ * @interface
+ */
+WebInspector.TextEditorPositionHandle = function()
+{
+}
+
+WebInspector.TextEditorPositionHandle.prototype = {
+ /**
+ * @return {?{lineNumber: number, columnNumber: number}}
+ */
+ resolve: function() { },
+
+ /**
+ * @param {!WebInspector.TextEditorPositionHandle} positionHandle
+ * @return {boolean}
+ */
+ equal: function(positionHandle) { }
+}
+
+/**
+ * @interface
+ */
+WebInspector.TextEditorDelegate = function()
+{
+}
+
+WebInspector.TextEditorDelegate.prototype = {
+ /**
+ * @param {!WebInspector.TextRange} oldRange
+ * @param {!WebInspector.TextRange} newRange
+ */
+ onTextChanged: function(oldRange, newRange) { },
+
+ /**
+ * @param {!WebInspector.TextRange} textRange
+ */
+ selectionChanged: function(textRange) { },
+
+ /**
+ * @param {number} lineNumber
+ */
+ scrollChanged: function(lineNumber) { },
+
+ editorFocused: function() { },
+
+ /**
+ * @param {!WebInspector.ContextMenu} contextMenu
+ * @param {number} lineNumber
+ */
+ populateLineGutterContextMenu: function(contextMenu, lineNumber) { },
+
+ /**
+ * @param {!WebInspector.ContextMenu} contextMenu
+ * @param {number} lineNumber
+ */
+ populateTextAreaContextMenu: function(contextMenu, lineNumber) { },
+
+ /**
+ * @param {string} hrefValue
+ * @param {boolean} isExternal
+ * @return {!Element}
+ */
+ createLink: function(hrefValue, isExternal) { },
+
+ /**
+ * @param {?WebInspector.TextRange} from
+ * @param {?WebInspector.TextRange} to
+ */
+ onJumpToPosition: function(from, to) { }
+}
+
+/**
+ * @interface
+ */
+WebInspector.TokenizerFactory = function() { }
+
+WebInspector.TokenizerFactory.prototype = {
+ /**
+ * @param {string} mimeType
+ * @return {function(string, function(string, ?string, number, number))}
+ */
+ createTokenizer: function(mimeType) { }
+} \ No newline at end of file
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js
new file mode 100644
index 00000000000..8efa6190b3d
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js
@@ -0,0 +1,908 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2011 Google 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @implements {WebInspector.SuggestBoxDelegate}
+ * @param {function(!Element, !Range, boolean, function(!Array.<string>, number=))} completions
+ * @param {string=} stopCharacters
+ */
+WebInspector.TextPrompt = function(completions, stopCharacters)
+{
+ /**
+ * @type {!Element|undefined}
+ */
+ this._proxyElement;
+ this._proxyElementDisplay = "inline-block";
+ this._loadCompletions = completions;
+ this._completionStopCharacters = stopCharacters || " =:[({;,!+-*/&|^<>.";
+}
+
+WebInspector.TextPrompt.Events = {
+ ItemApplied: "text-prompt-item-applied",
+ ItemAccepted: "text-prompt-item-accepted"
+};
+
+WebInspector.TextPrompt.prototype = {
+ get proxyElement()
+ {
+ return this._proxyElement;
+ },
+
+ /**
+ * @param {boolean} suggestBoxEnabled
+ */
+ setSuggestBoxEnabled: function(suggestBoxEnabled)
+ {
+ this._suggestBoxEnabled = suggestBoxEnabled;
+ },
+
+ renderAsBlock: function()
+ {
+ this._proxyElementDisplay = "block";
+ },
+
+ /**
+ * Clients should never attach any event listeners to the |element|. Instead,
+ * they should use the result of this method to attach listeners for bubbling events.
+ *
+ * @param {!Element} element
+ * @return {!Element}
+ */
+ attach: function(element)
+ {
+ return this._attachInternal(element);
+ },
+
+ /**
+ * Clients should never attach any event listeners to the |element|. Instead,
+ * they should use the result of this method to attach listeners for bubbling events
+ * or the |blurListener| parameter to register a "blur" event listener on the |element|
+ * (since the "blur" event does not bubble.)
+ *
+ * @param {!Element} element
+ * @param {function(!Event)} blurListener
+ * @return {!Element}
+ */
+ attachAndStartEditing: function(element, blurListener)
+ {
+ this._attachInternal(element);
+ this._startEditing(blurListener);
+ return this.proxyElement;
+ },
+
+ /**
+ * @param {!Element} element
+ * @return {!Element}
+ */
+ _attachInternal: function(element)
+ {
+ if (this.proxyElement)
+ throw "Cannot attach an attached TextPrompt";
+ this._element = element;
+
+ this._boundOnKeyDown = this.onKeyDown.bind(this);
+ this._boundOnInput = this.onInput.bind(this);
+ this._boundOnMouseWheel = this.onMouseWheel.bind(this);
+ this._boundSelectStart = this._selectStart.bind(this);
+ this._boundRemoveSuggestionAids = this._removeSuggestionAids.bind(this);
+ this._proxyElement = element.ownerDocument.createElement("span");
+ this._proxyElement.style.display = this._proxyElementDisplay;
+ element.parentElement.insertBefore(this.proxyElement, element);
+ this.proxyElement.appendChild(element);
+ this._element.classList.add("text-prompt");
+ this._element.addEventListener("keydown", this._boundOnKeyDown, false);
+ this._element.addEventListener("input", this._boundOnInput, false);
+ this._element.addEventListener("mousewheel", this._boundOnMouseWheel, false);
+ this._element.addEventListener("selectstart", this._boundSelectStart, false);
+ this._element.addEventListener("blur", this._boundRemoveSuggestionAids, false);
+
+ if (this._suggestBoxEnabled)
+ this._suggestBox = new WebInspector.SuggestBox(this);
+
+ return this.proxyElement;
+ },
+
+ detach: function()
+ {
+ this._removeFromElement();
+ this.proxyElement.parentElement.insertBefore(this._element, this.proxyElement);
+ this.proxyElement.remove();
+ delete this._proxyElement;
+ this._element.classList.remove("text-prompt");
+ WebInspector.restoreFocusFromElement(this._element);
+ },
+
+ /**
+ * @type {string}
+ */
+ get text()
+ {
+ return this._element.textContent;
+ },
+
+ /**
+ * @param {string} x
+ */
+ set text(x)
+ {
+ this._removeSuggestionAids();
+ 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();
+ this._element.scrollIntoView();
+ },
+
+ _removeFromElement: function()
+ {
+ this.clearAutoComplete(true);
+ this._element.removeEventListener("keydown", this._boundOnKeyDown, false);
+ this._element.removeEventListener("input", this._boundOnInput, false);
+ this._element.removeEventListener("selectstart", this._boundSelectStart, false);
+ this._element.removeEventListener("blur", this._boundRemoveSuggestionAids, false);
+ if (this._isEditing)
+ this._stopEditing();
+ if (this._suggestBox)
+ this._suggestBox.removeFromElement();
+ },
+
+ /**
+ * @param {function(!Event)=} blurListener
+ */
+ _startEditing: function(blurListener)
+ {
+ this._isEditing = true;
+ this._element.classList.add("editing");
+ if (blurListener) {
+ this._blurListener = blurListener;
+ this._element.addEventListener("blur", this._blurListener, false);
+ }
+ this._oldTabIndex = this._element.tabIndex;
+ if (this._element.tabIndex < 0)
+ this._element.tabIndex = 0;
+ WebInspector.setCurrentFocusElement(this._element);
+ if (!this.text)
+ this._updateAutoComplete();
+ },
+
+ _stopEditing: function()
+ {
+ this._element.tabIndex = this._oldTabIndex;
+ if (this._blurListener)
+ this._element.removeEventListener("blur", this._blurListener, false);
+ this._element.classList.remove("editing");
+ delete this._isEditing;
+ },
+
+ _removeSuggestionAids: function()
+ {
+ this.clearAutoComplete();
+ this.hideSuggestBox();
+ },
+
+ _selectStart: function()
+ {
+ if (this._selectionTimeout)
+ clearTimeout(this._selectionTimeout);
+
+ this._removeSuggestionAids();
+
+ /**
+ * @this {WebInspector.TextPrompt}
+ */
+ function moveBackIfOutside()
+ {
+ delete this._selectionTimeout;
+ if (!this.isCaretInsidePrompt() && window.getSelection().isCollapsed) {
+ this.moveCaretToEndOfPrompt();
+ this.autoCompleteSoon();
+ }
+ }
+
+ this._selectionTimeout = setTimeout(moveBackIfOutside.bind(this), 100);
+ },
+
+ /**
+ * @param {boolean=} force
+ */
+ _updateAutoComplete: function(force)
+ {
+ this.clearAutoComplete();
+ this.autoCompleteSoon(force);
+ },
+
+ /**
+ * @param {?Event} event
+ */
+ onMouseWheel: function(event)
+ {
+ // Subclasses can implement.
+ },
+
+ /**
+ * @param {?Event} event
+ */
+ onKeyDown: function(event)
+ {
+ var handled = false;
+ delete this._needUpdateAutocomplete;
+
+ switch (event.keyIdentifier) {
+ case "U+0009": // Tab
+ handled = this.tabKeyPressed(event);
+ break;
+ case "Left":
+ case "Home":
+ this._removeSuggestionAids();
+ break;
+ case "Right":
+ case "End":
+ if (this.isCaretAtEndOfPrompt())
+ handled = this.acceptAutoComplete();
+ else
+ this._removeSuggestionAids();
+ break;
+ case "U+001B": // Esc
+ if (this.isSuggestBoxVisible()) {
+ this._removeSuggestionAids();
+ handled = true;
+ }
+ break;
+ case "U+0020": // Space
+ if (event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey) {
+ this._updateAutoComplete(true);
+ handled = true;
+ }
+ break;
+ case "Alt":
+ case "Meta":
+ case "Shift":
+ case "Control":
+ break;
+ }
+
+ if (!handled && this.isSuggestBoxVisible())
+ handled = this._suggestBox.keyPressed(event);
+
+ if (!handled)
+ this._needUpdateAutocomplete = true;
+
+ if (handled)
+ event.consume(true);
+ },
+
+ /**
+ * @param {?Event} event
+ */
+ onInput: function(event)
+ {
+ if (this._needUpdateAutocomplete)
+ this._updateAutoComplete();
+ },
+
+ /**
+ * @return {boolean}
+ */
+ acceptAutoComplete: function()
+ {
+ var result = false;
+ if (this.isSuggestBoxVisible())
+ result = this._suggestBox.acceptSuggestion();
+ if (!result)
+ result = this._acceptSuggestionInternal();
+
+ return result;
+ },
+
+ /**
+ * @param {boolean=} includeTimeout
+ */
+ clearAutoComplete: function(includeTimeout)
+ {
+ if (includeTimeout && this._completeTimeout) {
+ clearTimeout(this._completeTimeout);
+ delete this._completeTimeout;
+ }
+ delete this._waitingForCompletions;
+
+ if (!this.autoCompleteElement)
+ return;
+
+ this.autoCompleteElement.remove();
+ delete this.autoCompleteElement;
+ delete this._userEnteredRange;
+ delete this._userEnteredText;
+ },
+
+ /**
+ * @param {boolean=} force
+ */
+ autoCompleteSoon: function(force)
+ {
+ var immediately = this.isSuggestBoxVisible() || force;
+ if (!this._completeTimeout)
+ this._completeTimeout = setTimeout(this.complete.bind(this, force), immediately ? 0 : 250);
+ },
+
+ /**
+ * @param {boolean=} force
+ * @param {boolean=} reverse
+ */
+ complete: function(force, reverse)
+ {
+ this.clearAutoComplete(true);
+ var selection = window.getSelection();
+ if (!selection.rangeCount)
+ return;
+
+ var selectionRange = selection.getRangeAt(0);
+ var shouldExit;
+
+ if (!force && !this.isCaretAtEndOfPrompt() && !this.isSuggestBoxVisible())
+ shouldExit = true;
+ else if (!selection.isCollapsed)
+ shouldExit = true;
+ else if (!force) {
+ // BUG72018: Do not show suggest box if caret is followed by a non-stop character.
+ var wordSuffixRange = selectionRange.startContainer.rangeOfWord(selectionRange.endOffset, this._completionStopCharacters, this._element, "forward");
+ if (wordSuffixRange.toString().length)
+ shouldExit = true;
+ }
+ if (shouldExit) {
+ this.hideSuggestBox();
+ return;
+ }
+
+ var wordPrefixRange = selectionRange.startContainer.rangeOfWord(selectionRange.startOffset, this._completionStopCharacters, this._element, "backward");
+ this._waitingForCompletions = true;
+ this._loadCompletions(this.proxyElement, wordPrefixRange, force || false, this._completionsReady.bind(this, selection, wordPrefixRange, !!reverse));
+ },
+
+ disableDefaultSuggestionForEmptyInput: function()
+ {
+ this._disableDefaultSuggestionForEmptyInput = true;
+ },
+
+ /**
+ * @param {!Selection} selection
+ * @param {!Range} textRange
+ */
+ _boxForAnchorAtStart: function(selection, textRange)
+ {
+ var rangeCopy = selection.getRangeAt(0).cloneRange();
+ var anchorElement = document.createElement("span");
+ anchorElement.textContent = "\u200B";
+ textRange.insertNode(anchorElement);
+ var box = anchorElement.boxInWindow(window);
+ anchorElement.remove();
+ selection.removeAllRanges();
+ selection.addRange(rangeCopy);
+ return box;
+ },
+
+ /**
+ * @param {!Array.<string>} completions
+ * @param {number} wordPrefixLength
+ */
+ _buildCommonPrefix: function(completions, wordPrefixLength)
+ {
+ var commonPrefix = completions[0];
+ for (var i = 0; i < completions.length; ++i) {
+ var completion = completions[i];
+ var lastIndex = Math.min(commonPrefix.length, completion.length);
+ for (var j = wordPrefixLength; j < lastIndex; ++j) {
+ if (commonPrefix[j] !== completion[j]) {
+ commonPrefix = commonPrefix.substr(0, j);
+ break;
+ }
+ }
+ }
+ return commonPrefix;
+ },
+
+ /**
+ * @param {!Selection} selection
+ * @param {!Range} originalWordPrefixRange
+ * @param {boolean} reverse
+ * @param {!Array.<string>} completions
+ * @param {number=} selectedIndex
+ */
+ _completionsReady: function(selection, originalWordPrefixRange, reverse, completions, selectedIndex)
+ {
+ if (!this._waitingForCompletions || !completions.length) {
+ this.hideSuggestBox();
+ return;
+ }
+ delete this._waitingForCompletions;
+
+ var selectionRange = selection.getRangeAt(0);
+
+ var fullWordRange = document.createRange();
+ fullWordRange.setStart(originalWordPrefixRange.startContainer, originalWordPrefixRange.startOffset);
+ fullWordRange.setEnd(selectionRange.endContainer, selectionRange.endOffset);
+
+ if (originalWordPrefixRange.toString() + selectionRange.toString() !== fullWordRange.toString())
+ return;
+
+ selectedIndex = (this._disableDefaultSuggestionForEmptyInput && !this.text) ? -1 : (selectedIndex || 0);
+
+ this._userEnteredRange = fullWordRange;
+ this._userEnteredText = fullWordRange.toString();
+
+ if (this._suggestBox)
+ this._suggestBox.updateSuggestions(this._boxForAnchorAtStart(selection, fullWordRange), completions, selectedIndex, !this.isCaretAtEndOfPrompt(), this._userEnteredText);
+
+ if (selectedIndex === -1)
+ return;
+
+ var wordPrefixLength = originalWordPrefixRange.toString().length;
+ this._commonPrefix = this._buildCommonPrefix(completions, wordPrefixLength);
+
+ if (this.isCaretAtEndOfPrompt()) {
+ this._userEnteredRange.deleteContents();
+ this._element.normalize();
+ var finalSelectionRange = document.createRange();
+ var completionText = completions[selectedIndex];
+ 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);
+ selection.removeAllRanges();
+ selection.addRange(finalSelectionRange);
+ this.dispatchEventToListeners(WebInspector.TextPrompt.Events.ItemApplied);
+ }
+ },
+
+ _completeCommonPrefix: function()
+ {
+ if (!this.autoCompleteElement || !this._commonPrefix || !this._userEnteredText || !this._commonPrefix.startsWith(this._userEnteredText))
+ return;
+
+ if (!this.isSuggestBoxVisible()) {
+ this.acceptAutoComplete();
+ return;
+ }
+
+ this.autoCompleteElement.textContent = this._commonPrefix.substring(this._userEnteredText.length);
+ this._acceptSuggestionInternal(true);
+ },
+
+ /**
+ * @param {string} completionText
+ * @param {boolean=} isIntermediateSuggestion
+ */
+ applySuggestion: function(completionText, isIntermediateSuggestion)
+ {
+ this._applySuggestion(completionText, isIntermediateSuggestion);
+ },
+
+ /**
+ * @param {string} completionText
+ * @param {boolean=} isIntermediateSuggestion
+ * @param {!Range=} originalPrefixRange
+ */
+ _applySuggestion: function(completionText, isIntermediateSuggestion, originalPrefixRange)
+ {
+ var wordPrefixLength;
+ if (originalPrefixRange)
+ wordPrefixLength = originalPrefixRange.toString().length;
+ else
+ wordPrefixLength = this._userEnteredText ? this._userEnteredText.length : 0;
+
+ this._userEnteredRange.deleteContents();
+ this._element.normalize();
+ var finalSelectionRange = document.createRange();
+ var completionTextNode = document.createTextNode(completionText);
+ this._userEnteredRange.insertNode(completionTextNode);
+ if (this.autoCompleteElement) {
+ this.autoCompleteElement.remove();
+ delete this.autoCompleteElement;
+ }
+
+ if (isIntermediateSuggestion)
+ finalSelectionRange.setStart(completionTextNode, wordPrefixLength);
+ else
+ finalSelectionRange.setStart(completionTextNode, completionText.length);
+
+ finalSelectionRange.setEnd(completionTextNode, completionText.length);
+
+ var selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(finalSelectionRange);
+ if (isIntermediateSuggestion)
+ this.dispatchEventToListeners(WebInspector.TextPrompt.Events.ItemApplied, { itemText: completionText });
+ },
+
+ /**
+ * @override
+ */
+ acceptSuggestion: function()
+ {
+ this._acceptSuggestionInternal();
+ },
+
+ /**
+ * @param {boolean=} prefixAccepted
+ * @return {boolean}
+ */
+ _acceptSuggestionInternal: function(prefixAccepted)
+ {
+ if (this._isAcceptingSuggestion)
+ return false;
+
+ 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);
+
+ if (!prefixAccepted) {
+ this.hideSuggestBox();
+ this.dispatchEventToListeners(WebInspector.TextPrompt.Events.ItemAccepted);
+ } else
+ this.autoCompleteSoon(true);
+
+ return true;
+ },
+
+ hideSuggestBox: function()
+ {
+ if (this.isSuggestBoxVisible())
+ this._suggestBox.hide();
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isSuggestBoxVisible: function()
+ {
+ return this._suggestBox && this._suggestBox.visible();
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isCaretInsidePrompt: function()
+ {
+ return this._element.isInsertionCaretInside();
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isCaretAtEndOfPrompt: function()
+ {
+ var selection = window.getSelection();
+ if (!selection.rangeCount || !selection.isCollapsed)
+ return false;
+
+ var selectionRange = selection.getRangeAt(0);
+ var node = selectionRange.startContainer;
+ if (!node.isSelfOrDescendant(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 && (!this.autoCompleteElement || !this.autoCompleteElement.isAncestor(node)))
+ return false;
+ foundNextText = true;
+ }
+
+ node = node.traverseNextNode(this._element);
+ }
+
+ return true;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isCaretOnFirstLine: function()
+ {
+ var selection = window.getSelection();
+ var focusNode = selection.focusNode;
+ if (!focusNode || focusNode.nodeType !== Node.TEXT_NODE || focusNode.parentNode !== this._element)
+ return true;
+
+ if (focusNode.textContent.substring(0, selection.focusOffset).indexOf("\n") !== -1)
+ return false;
+ focusNode = focusNode.previousSibling;
+
+ while (focusNode) {
+ if (focusNode.nodeType !== Node.TEXT_NODE)
+ return true;
+ if (focusNode.textContent.indexOf("\n") !== -1)
+ return false;
+ focusNode = focusNode.previousSibling;
+ }
+
+ return true;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isCaretOnLastLine: function()
+ {
+ var selection = window.getSelection();
+ var focusNode = selection.focusNode;
+ if (!focusNode || focusNode.nodeType !== Node.TEXT_NODE || focusNode.parentNode !== this._element)
+ return true;
+
+ if (focusNode.textContent.substring(selection.focusOffset).indexOf("\n") !== -1)
+ return false;
+ focusNode = focusNode.nextSibling;
+
+ while (focusNode) {
+ if (focusNode.nodeType !== Node.TEXT_NODE)
+ return true;
+ if (focusNode.textContent.indexOf("\n") !== -1)
+ return false;
+ focusNode = focusNode.nextSibling;
+ }
+
+ 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);
+ },
+
+ /**
+ * @param {!Event} event
+ * @return {boolean}
+ */
+ tabKeyPressed: function(event)
+ {
+ this._completeCommonPrefix();
+
+ // Consume the key.
+ return true;
+ },
+
+ __proto__: WebInspector.Object.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.TextPrompt}
+ * @param {function(!Element, !Range, boolean, function(!Array.<string>, number=))} completions
+ * @param {string=} stopCharacters
+ */
+WebInspector.TextPromptWithHistory = function(completions, stopCharacters)
+{
+ WebInspector.TextPrompt.call(this, completions, stopCharacters);
+
+ /**
+ * @type {!Array.<string>}
+ */
+ this._data = [];
+
+ /**
+ * 1-based entry in the history stack.
+ * @type {number}
+ */
+ this._historyOffset = 1;
+
+ /**
+ * Whether to coalesce duplicate items in the history, default is true.
+ * @type {boolean}
+ */
+ this._coalesceHistoryDupes = true;
+}
+
+WebInspector.TextPromptWithHistory.prototype = {
+ /**
+ * @return {!Array.<string>}
+ */
+ get historyData()
+ {
+ // FIXME: do we need to copy this?
+ return this._data;
+ },
+
+ /**
+ * @param {boolean} x
+ */
+ setCoalesceHistoryDupes: function(x)
+ {
+ this._coalesceHistoryDupes = x;
+ },
+
+ /**
+ * @param {!Array.<string>} data
+ */
+ setHistoryData: function(data)
+ {
+ this._data = [].concat(data);
+ this._historyOffset = 1;
+ },
+
+ /**
+ * Pushes a committed text into the history.
+ * @param {string} text
+ */
+ pushHistoryItem: function(text)
+ {
+ if (this._uncommittedIsTop) {
+ this._data.pop();
+ delete this._uncommittedIsTop;
+ }
+
+ this._historyOffset = 1;
+ if (this._coalesceHistoryDupes && text === this._currentHistoryItem())
+ return;
+ this._data.push(text);
+ },
+
+ /**
+ * Pushes the current (uncommitted) text into the history.
+ */
+ _pushCurrentText: function()
+ {
+ if (this._uncommittedIsTop)
+ this._data.pop(); // Throw away obsolete uncommitted text.
+ this._uncommittedIsTop = true;
+ this.clearAutoComplete(true);
+ this._data.push(this.text);
+ },
+
+ /**
+ * @return {string|undefined}
+ */
+ _previous: function()
+ {
+ if (this._historyOffset > this._data.length)
+ return undefined;
+ if (this._historyOffset === 1)
+ this._pushCurrentText();
+ ++this._historyOffset;
+ return this._currentHistoryItem();
+ },
+
+ /**
+ * @return {string|undefined}
+ */
+ _next: function()
+ {
+ if (this._historyOffset === 1)
+ return undefined;
+ --this._historyOffset;
+ return this._currentHistoryItem();
+ },
+
+ /**
+ * @return {string|undefined}
+ */
+ _currentHistoryItem: function()
+ {
+ return this._data[this._data.length - this._historyOffset];
+ },
+
+ /**
+ * @override
+ */
+ onKeyDown: function(event)
+ {
+ var newText;
+ var isPrevious;
+
+ switch (event.keyIdentifier) {
+ case "Up":
+ if (!this.isCaretOnFirstLine() || this.isSuggestBoxVisible())
+ break;
+ newText = this._previous();
+ isPrevious = true;
+ break;
+ case "Down":
+ if (!this.isCaretOnLastLine() || this.isSuggestBoxVisible())
+ break;
+ newText = this._next();
+ break;
+ case "U+0050": // Ctrl+P = Previous
+ if (WebInspector.isMac() && event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey) {
+ newText = this._previous();
+ isPrevious = true;
+ }
+ break;
+ case "U+004E": // Ctrl+N = Next
+ if (WebInspector.isMac() && event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey)
+ newText = this._next();
+ break;
+ }
+
+ if (newText !== undefined) {
+ event.consume(true);
+ this.text = newText;
+
+ if (isPrevious) {
+ var firstNewlineIndex = this.text.indexOf("\n");
+ if (firstNewlineIndex === -1)
+ this.moveCaretToEndOfPrompt();
+ else {
+ var selection = window.getSelection();
+ var selectionRange = document.createRange();
+
+ selectionRange.setStart(this._element.firstChild, firstNewlineIndex);
+ selectionRange.setEnd(this._element.firstChild, firstNewlineIndex);
+
+ selection.removeAllRanges();
+ selection.addRange(selectionRange);
+ }
+ }
+
+ return;
+ }
+
+ WebInspector.TextPrompt.prototype.onKeyDown.apply(this, arguments);
+ },
+
+ __proto__: WebInspector.TextPrompt.prototype
+}
+
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/TextUtils.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/TextUtils.js
new file mode 100644
index 00000000000..87273320313
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/TextUtils.js
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2013 Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.TextUtils = {
+ /**
+ * @param {string} char
+ * @return {boolean}
+ */
+ isStopChar: function(char)
+ {
+ return (char > " " && char < "0") ||
+ (char > "9" && char < "A") ||
+ (char > "Z" && char < "_") ||
+ (char > "_" && char < "a") ||
+ (char > "z" && char <= "~");
+ },
+
+ /**
+ * @param {string} char
+ * @return {boolean}
+ */
+ isWordChar: function(char)
+ {
+ return !WebInspector.TextUtils.isStopChar(char) && !WebInspector.TextUtils.isSpaceChar(char);
+ },
+
+ /**
+ * @param {string} char
+ * @return {boolean}
+ */
+ isSpaceChar: function(char)
+ {
+ return WebInspector.TextUtils._SpaceCharRegex.test(char);
+ },
+
+ /**
+ * @param {string} word
+ * @return {boolean}
+ */
+ isWord: function(word)
+ {
+ for (var i = 0; i < word.length; ++i) {
+ if (!WebInspector.TextUtils.isWordChar(word.charAt(i)))
+ return false;
+ }
+ return true;
+ },
+
+ /**
+ * @param {string} char
+ * @return {boolean}
+ */
+ isOpeningBraceChar: function(char)
+ {
+ return char === "(" || char === "{";
+ },
+
+ /**
+ * @param {string} char
+ * @return {boolean}
+ */
+ isClosingBraceChar: function(char)
+ {
+ return char === ")" || char === "}";
+ },
+
+ /**
+ * @param {string} char
+ * @return {boolean}
+ */
+ isBraceChar: function(char)
+ {
+ return WebInspector.TextUtils.isOpeningBraceChar(char) || WebInspector.TextUtils.isClosingBraceChar(char);
+ },
+
+ /**
+ * @param {string} text
+ * @param {function(string):boolean} isWordChar
+ * @return {!Array.<string>}
+ */
+ textToWords: function(text, isWordChar)
+ {
+ var words = [];
+ var startWord = -1;
+ for(var i = 0; i < text.length; ++i) {
+ if (!isWordChar(text.charAt(i))) {
+ if (startWord !== -1)
+ words.push(text.substring(startWord, i));
+ startWord = -1;
+ } else if (startWord === -1)
+ startWord = i;
+ }
+ if (startWord !== -1)
+ words.push(text.substring(startWord));
+ return words;
+ },
+
+ /**
+ * @param {string} source
+ * @param {number=} startIndex
+ * @param {number=} lastIndex
+ * @return {number}
+ */
+ findBalancedCurlyBrackets: function(source, startIndex, lastIndex) {
+ lastIndex = lastIndex || source.length;
+ startIndex = startIndex || 0;
+ var counter = 0;
+ var inString = false;
+
+ for (var index = startIndex; index < lastIndex; ++index) {
+ var character = source[index];
+ if (inString) {
+ if (character === "\\")
+ ++index;
+ else if (character === "\"")
+ inString = false;
+ } else {
+ if (character === "\"")
+ inString = true;
+ else if (character === "{")
+ ++counter;
+ else if (character === "}") {
+ if (--counter === 0)
+ return index + 1;
+ }
+ }
+ }
+ return -1;
+ }
+}
+
+WebInspector.TextUtils._SpaceCharRegex = /\s/;
+
+/**
+ * @enum {string}
+ */
+WebInspector.TextUtils.Indent = {
+ TwoSpaces: " ",
+ FourSpaces: " ",
+ EightSpaces: " ",
+ TabCharacter: "\t"
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js
new file mode 100644
index 00000000000..a2d7f8feadd
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js
@@ -0,0 +1,911 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com).
+ * Copyright (C) 2009 Joseph Pecoraro
+ *
+ * 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.
+ */
+
+/**
+ * @param {!Element} element
+ * @param {?function(!MouseEvent): boolean} elementDragStart
+ * @param {function(!MouseEvent)} elementDrag
+ * @param {?function(!MouseEvent)} elementDragEnd
+ * @param {string} cursor
+ * @param {?string=} hoverCursor
+ */
+WebInspector.installDragHandle = function(element, elementDragStart, elementDrag, elementDragEnd, cursor, hoverCursor)
+{
+ element.addEventListener("mousedown", WebInspector.elementDragStart.bind(WebInspector, elementDragStart, elementDrag, elementDragEnd, cursor), false);
+ if (hoverCursor !== null)
+ element.style.cursor = hoverCursor || cursor;
+}
+
+/**
+ * @param {?function(!MouseEvent):boolean} elementDragStart
+ * @param {function(!MouseEvent)} elementDrag
+ * @param {?function(!MouseEvent)} elementDragEnd
+ * @param {string} cursor
+ * @param {?Event} event
+ */
+WebInspector.elementDragStart = function(elementDragStart, elementDrag, elementDragEnd, cursor, event)
+{
+ // Only drag upon left button. Right will likely cause a context menu. So will ctrl-click on mac.
+ if (event.button || (WebInspector.isMac() && event.ctrlKey))
+ return;
+
+ if (WebInspector._elementDraggingEventListener)
+ return;
+
+ if (elementDragStart && !elementDragStart(/** @type {!MouseEvent} */ (event)))
+ return;
+
+ if (WebInspector._elementDraggingGlassPane) {
+ WebInspector._elementDraggingGlassPane.dispose();
+ delete WebInspector._elementDraggingGlassPane;
+ }
+
+ var targetDocument = event.target.ownerDocument;
+
+ WebInspector._elementDraggingEventListener = elementDrag;
+ WebInspector._elementEndDraggingEventListener = elementDragEnd;
+ WebInspector._mouseOutWhileDraggingTargetDocument = targetDocument;
+
+ targetDocument.addEventListener("mousemove", WebInspector._elementDragMove, true);
+ targetDocument.addEventListener("mouseup", WebInspector._elementDragEnd, true);
+ targetDocument.addEventListener("mouseout", WebInspector._mouseOutWhileDragging, true);
+
+ targetDocument.body.style.cursor = cursor;
+
+ event.preventDefault();
+}
+
+WebInspector._mouseOutWhileDragging = function()
+{
+ WebInspector._unregisterMouseOutWhileDragging();
+ WebInspector._elementDraggingGlassPane = new WebInspector.GlassPane();
+}
+
+WebInspector._unregisterMouseOutWhileDragging = function()
+{
+ if (!WebInspector._mouseOutWhileDraggingTargetDocument)
+ return;
+ WebInspector._mouseOutWhileDraggingTargetDocument.removeEventListener("mouseout", WebInspector._mouseOutWhileDragging, true);
+ delete WebInspector._mouseOutWhileDraggingTargetDocument;
+}
+
+/**
+ * @param {!Event} event
+ */
+WebInspector._elementDragMove = function(event)
+{
+ if (WebInspector._elementDraggingEventListener(/** @type {!MouseEvent} */ (event)))
+ WebInspector._cancelDragEvents(event);
+}
+
+/**
+ * @param {!Event} event
+ */
+WebInspector._cancelDragEvents = function(event)
+{
+ var targetDocument = event.target.ownerDocument;
+ targetDocument.removeEventListener("mousemove", WebInspector._elementDragMove, true);
+ targetDocument.removeEventListener("mouseup", WebInspector._elementDragEnd, true);
+ WebInspector._unregisterMouseOutWhileDragging();
+
+ targetDocument.body.style.removeProperty("cursor");
+
+ if (WebInspector._elementDraggingGlassPane)
+ WebInspector._elementDraggingGlassPane.dispose();
+
+ delete WebInspector._elementDraggingGlassPane;
+ delete WebInspector._elementDraggingEventListener;
+ delete WebInspector._elementEndDraggingEventListener;
+}
+
+/**
+ * @param {!Event} event
+ */
+WebInspector._elementDragEnd = function(event)
+{
+ var elementDragEnd = WebInspector._elementEndDraggingEventListener;
+
+ WebInspector._cancelDragEvents(/** @type {!MouseEvent} */ (event));
+
+ event.preventDefault();
+ if (elementDragEnd)
+ elementDragEnd(/** @type {!MouseEvent} */ (event));
+}
+
+/**
+ * @constructor
+ */
+WebInspector.GlassPane = function()
+{
+ this.element = document.createElement("div");
+ this.element.style.cssText = "position:absolute;top:0;bottom:0;left:0;right:0;background-color:transparent;z-index:1000;";
+ this.element.id = "glass-pane";
+ document.body.appendChild(this.element);
+ WebInspector._glassPane = this;
+}
+
+WebInspector.GlassPane.prototype = {
+ dispose: function()
+ {
+ delete WebInspector._glassPane;
+ if (WebInspector.GlassPane.DefaultFocusedViewStack.length)
+ WebInspector.GlassPane.DefaultFocusedViewStack[0].focus();
+ this.element.remove();
+ }
+}
+
+/**
+ * @type {!Array.<!WebInspector.View>}
+ */
+WebInspector.GlassPane.DefaultFocusedViewStack = [];
+
+/**
+ * @param {?Node=} node
+ * @return {boolean}
+ */
+WebInspector.isBeingEdited = function(node)
+{
+ if (!node || node.nodeType !== Node.ELEMENT_NODE)
+ return false;
+ var element = /** {!Element} */ (node);
+ if (element.classList.contains("text-prompt") || element.nodeName === "INPUT" || element.nodeName === "TEXTAREA")
+ return true;
+
+ if (!WebInspector.__editingCount)
+ return false;
+
+ while (element) {
+ if (element.__editing)
+ return true;
+ element = element.parentElement;
+ }
+ return false;
+}
+
+/**
+ * @param {!Element} element
+ * @param {boolean} value
+ * @return {boolean}
+ */
+WebInspector.markBeingEdited = function(element, value)
+{
+ if (value) {
+ if (element.__editing)
+ return false;
+ element.classList.add("being-edited");
+ element.__editing = true;
+ WebInspector.__editingCount = (WebInspector.__editingCount || 0) + 1;
+ } else {
+ if (!element.__editing)
+ return false;
+ element.classList.remove("being-edited");
+ delete element.__editing;
+ --WebInspector.__editingCount;
+ }
+ return true;
+}
+
+WebInspector.CSSNumberRegex = /^(-?(?:\d+(?:\.\d+)?|\.\d+))$/;
+
+WebInspector.StyleValueDelimiters = " \xA0\t\n\"':;,/()";
+
+
+/**
+ * @param {!Event} event
+ * @return {?string}
+ */
+WebInspector._valueModificationDirection = function(event)
+{
+ var direction = null;
+ if (event.type === "mousewheel") {
+ if (event.wheelDeltaY > 0)
+ direction = "Up";
+ else if (event.wheelDeltaY < 0)
+ direction = "Down";
+ } else {
+ if (event.keyIdentifier === "Up" || event.keyIdentifier === "PageUp")
+ direction = "Up";
+ else if (event.keyIdentifier === "Down" || event.keyIdentifier === "PageDown")
+ direction = "Down";
+ }
+ return direction;
+}
+
+/**
+ * @param {string} hexString
+ * @param {!Event} event
+ */
+WebInspector._modifiedHexValue = function(hexString, event)
+{
+ var direction = WebInspector._valueModificationDirection(event);
+ if (!direction)
+ return hexString;
+
+ var number = parseInt(hexString, 16);
+ if (isNaN(number) || !isFinite(number))
+ return hexString;
+
+ var maxValue = Math.pow(16, hexString.length) - 1;
+ var arrowKeyOrMouseWheelEvent = (event.keyIdentifier === "Up" || event.keyIdentifier === "Down" || event.type === "mousewheel");
+ var delta;
+
+ if (arrowKeyOrMouseWheelEvent)
+ delta = (direction === "Up") ? 1 : -1;
+ else
+ delta = (event.keyIdentifier === "PageUp") ? 16 : -16;
+
+ if (event.shiftKey)
+ delta *= 16;
+
+ var result = number + delta;
+ if (result < 0)
+ result = 0; // Color hex values are never negative, so clamp to 0.
+ else if (result > maxValue)
+ return hexString;
+
+ // Ensure the result length is the same as the original hex value.
+ var resultString = result.toString(16).toUpperCase();
+ for (var i = 0, lengthDelta = hexString.length - resultString.length; i < lengthDelta; ++i)
+ resultString = "0" + resultString;
+ return resultString;
+}
+
+/**
+ * @param {number} number
+ * @param {!Event} event
+ */
+WebInspector._modifiedFloatNumber = function(number, event)
+{
+ var direction = WebInspector._valueModificationDirection(event);
+ if (!direction)
+ return number;
+
+ var arrowKeyOrMouseWheelEvent = (event.keyIdentifier === "Up" || event.keyIdentifier === "Down" || event.type === "mousewheel");
+
+ // Jump by 10 when shift is down or jump by 0.1 when 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 && !arrowKeyOrMouseWheelEvent)
+ changeAmount = 100;
+ else if (event.shiftKey || !arrowKeyOrMouseWheelEvent)
+ changeAmount = 10;
+ else if (event.altKey)
+ changeAmount = 0.1;
+
+ if (direction === "Down")
+ 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.
+ var result = Number((number + changeAmount).toFixed(6));
+ if (!String(result).match(WebInspector.CSSNumberRegex))
+ return null;
+
+ return result;
+}
+
+/**
+ * @param {?Event} event
+ * @param {!Element} element
+ * @param {function(string,string)=} finishHandler
+ * @param {function(string)=} suggestionHandler
+ * @param {function(number):number=} customNumberHandler
+ * @return {boolean}
+ */
+WebInspector.handleElementValueModifications = function(event, element, finishHandler, suggestionHandler, customNumberHandler)
+{
+ var arrowKeyOrMouseWheelEvent = (event.keyIdentifier === "Up" || event.keyIdentifier === "Down" || event.type === "mousewheel");
+ var pageKeyPressed = (event.keyIdentifier === "PageUp" || event.keyIdentifier === "PageDown");
+ if (!arrowKeyOrMouseWheelEvent && !pageKeyPressed)
+ return false;
+
+ var selection = window.getSelection();
+ if (!selection.rangeCount)
+ return false;
+
+ var selectionRange = selection.getRangeAt(0);
+ if (!selectionRange.commonAncestorContainer.isSelfOrDescendant(element))
+ return false;
+
+ var originalValue = element.textContent;
+ var wordRange = selectionRange.startContainer.rangeOfWord(selectionRange.startOffset, WebInspector.StyleValueDelimiters, element);
+ var wordString = wordRange.toString();
+
+ if (suggestionHandler && suggestionHandler(wordString))
+ return false;
+
+ var replacementString;
+ var prefix, suffix, number;
+
+ var matches;
+ matches = /(.*#)([\da-fA-F]+)(.*)/.exec(wordString);
+ if (matches && matches.length) {
+ prefix = matches[1];
+ suffix = matches[3];
+ number = WebInspector._modifiedHexValue(matches[2], event);
+
+ if (customNumberHandler)
+ number = customNumberHandler(number);
+
+ replacementString = prefix + number + suffix;
+ } else {
+ matches = /(.*?)(-?(?:\d+(?:\.\d+)?|\.\d+))(.*)/.exec(wordString);
+ if (matches && matches.length) {
+ prefix = matches[1];
+ suffix = matches[3];
+ number = WebInspector._modifiedFloatNumber(parseFloat(matches[2]), event);
+
+ // Need to check for null explicitly.
+ if (number === null)
+ return false;
+
+ if (customNumberHandler)
+ number = customNumberHandler(number);
+
+ replacementString = prefix + number + suffix;
+ }
+ }
+
+ if (replacementString) {
+ 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.handled = true;
+ event.preventDefault();
+
+ if (finishHandler)
+ finishHandler(originalValue, replacementString);
+
+ return true;
+ }
+ return false;
+}
+
+/**
+ * @param {number} ms
+ * @param {number=} precision
+ * @return {string}
+ */
+Number.preciseMillisToString = function(ms, precision)
+{
+ precision = precision || 0;
+ var format = "%." + precision + "f\u2009ms";
+ return WebInspector.UIString(format, ms);
+}
+
+/**
+ * @param {number} ms
+ * @param {boolean=} higherResolution
+ * @return {string}
+ */
+Number.millisToString = function(ms, higherResolution)
+{
+ if (!isFinite(ms))
+ return "-";
+
+ if (ms === 0)
+ return "0";
+
+ if (higherResolution && ms < 1000)
+ return WebInspector.UIString("%.3f\u2009ms", ms);
+ else if (ms < 1000)
+ return WebInspector.UIString("%.0f\u2009ms", ms);
+
+ var seconds = ms / 1000;
+ if (seconds < 60)
+ return WebInspector.UIString("%.2f\u2009s", seconds);
+
+ var minutes = seconds / 60;
+ if (minutes < 60)
+ return WebInspector.UIString("%.1f\u2009min", minutes);
+
+ var hours = minutes / 60;
+ if (hours < 24)
+ return WebInspector.UIString("%.1f\u2009hrs", hours);
+
+ var days = hours / 24;
+ return WebInspector.UIString("%.1f\u2009days", days);
+}
+
+/**
+ * @param {number} seconds
+ * @param {boolean=} higherResolution
+ * @return {string}
+ */
+Number.secondsToString = function(seconds, higherResolution)
+{
+ if (!isFinite(seconds))
+ return "-";
+ return Number.millisToString(seconds * 1000, higherResolution);
+}
+
+/**
+ * @param {number} bytes
+ * @return {string}
+ */
+Number.bytesToString = function(bytes)
+{
+ if (bytes < 1024)
+ return WebInspector.UIString("%.0f\u2009B", bytes);
+
+ var kilobytes = bytes / 1024;
+ if (kilobytes < 100)
+ return WebInspector.UIString("%.1f\u2009KB", kilobytes);
+ if (kilobytes < 1024)
+ return WebInspector.UIString("%.0f\u2009KB", kilobytes);
+
+ var megabytes = kilobytes / 1024;
+ if (megabytes < 100)
+ return WebInspector.UIString("%.1f\u2009MB", megabytes);
+ else
+ return WebInspector.UIString("%.0f\u2009MB", megabytes);
+}
+
+/**
+ * @param {number} num
+ * @return {string}
+ */
+Number.withThousandsSeparator = function(num)
+{
+ var str = num + "";
+ var re = /(\d+)(\d{3})/;
+ while (str.match(re))
+ str = str.replace(re, "$1\u2009$2"); // \u2009 is a thin space.
+ return str;
+}
+
+/**
+ * @return {boolean}
+ */
+WebInspector.useLowerCaseMenuTitles = function()
+{
+ return WebInspector.platform() === "windows";
+}
+
+/**
+ * @param {string} format
+ * @param {?Array.<string>} substitutions
+ * @param {!Object.<string, function(string, ...):*>} formatters
+ * @param {string} initialValue
+ * @param {function(string, string): ?} append
+ * @return {!{formattedResult: string, unusedSubstitutions: ?Array.<string>}};
+ */
+WebInspector.formatLocalized = function(format, substitutions, formatters, initialValue, append)
+{
+ return String.format(WebInspector.UIString(format), substitutions, formatters, initialValue, append);
+}
+
+/**
+ * @return {string}
+ */
+WebInspector.openLinkExternallyLabel = function()
+{
+ return WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Open link in new tab" : "Open Link in New Tab");
+}
+
+/**
+ * @return {string}
+ */
+WebInspector.copyLinkAddressLabel = function()
+{
+ return WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy link address" : "Copy Link Address");
+}
+
+WebInspector.installPortStyles = function()
+{
+ var platform = WebInspector.platform();
+ document.body.classList.add("platform-" + platform);
+ var flavor = WebInspector.platformFlavor();
+ if (flavor)
+ document.body.classList.add("platform-" + flavor);
+ var port = WebInspector.port();
+ document.body.classList.add("port-" + port);
+}
+
+WebInspector._windowFocused = function(event)
+{
+ if (event.target.document.nodeType === Node.DOCUMENT_NODE)
+ document.body.classList.remove("inactive");
+}
+
+WebInspector._windowBlurred = function(event)
+{
+ if (event.target.document.nodeType === Node.DOCUMENT_NODE)
+ document.body.classList.add("inactive");
+}
+
+/**
+ * @return {!Element}
+ */
+WebInspector.previousFocusElement = function()
+{
+ return WebInspector._previousFocusElement;
+}
+
+/**
+ * @return {!Element}
+ */
+WebInspector.currentFocusElement = function()
+{
+ return WebInspector._currentFocusElement;
+}
+
+WebInspector._focusChanged = function(event)
+{
+ WebInspector.setCurrentFocusElement(event.target);
+}
+
+WebInspector._documentBlurred = function(event)
+{
+ // We want to know when currentFocusElement loses focus to nowhere.
+ // This is the case when event.relatedTarget is null (no element is being focused)
+ // and document.activeElement is reset to default (this is not a window blur).
+ if (!event.relatedTarget && document.activeElement === document.body)
+ WebInspector.setCurrentFocusElement(null);
+}
+
+WebInspector._textInputTypes = ["text", "search", "tel", "url", "email", "password"].keySet();
+WebInspector._isTextEditingElement = function(element)
+{
+ if (element instanceof HTMLInputElement)
+ return element.type in WebInspector._textInputTypes;
+
+ if (element instanceof HTMLTextAreaElement)
+ return true;
+
+ return false;
+}
+
+WebInspector.setCurrentFocusElement = function(x)
+{
+ if (WebInspector._glassPane && x && !WebInspector._glassPane.element.isAncestor(x))
+ return;
+ if (WebInspector._currentFocusElement !== x)
+ WebInspector._previousFocusElement = WebInspector._currentFocusElement;
+ WebInspector._currentFocusElement = x;
+
+ if (WebInspector._currentFocusElement) {
+ WebInspector._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.
+ // This is needed (at least) to remove caret from console when focus is moved to some element in the panel.
+ // The code below should not be applied to text fields and text areas, hence _isTextEditingElement check.
+ var selection = window.getSelection();
+ if (!WebInspector._isTextEditingElement(WebInspector._currentFocusElement) && selection.isCollapsed && !WebInspector._currentFocusElement.isInsertionCaretInside()) {
+ var selectionRange = WebInspector._currentFocusElement.ownerDocument.createRange();
+ selectionRange.setStart(WebInspector._currentFocusElement, 0);
+ selectionRange.setEnd(WebInspector._currentFocusElement, 0);
+
+ selection.removeAllRanges();
+ selection.addRange(selectionRange);
+ }
+ } else if (WebInspector._previousFocusElement)
+ WebInspector._previousFocusElement.blur();
+}
+
+WebInspector.restoreFocusFromElement = function(element)
+{
+ if (element && element.isSelfOrAncestor(WebInspector.currentFocusElement()))
+ WebInspector.setCurrentFocusElement(WebInspector.previousFocusElement());
+}
+
+WebInspector.setToolbarColors = function(backgroundColor, color)
+{
+ if (!WebInspector._themeStyleElement) {
+ WebInspector._themeStyleElement = document.createElement("style");
+ document.head.appendChild(WebInspector._themeStyleElement);
+ }
+ var parsedColor = WebInspector.Color.parse(color);
+ var shadowColor = parsedColor ? parsedColor.invert().setAlpha(0.33).toString(WebInspector.Color.Format.RGBA) : "white";
+ var prefix = WebInspector.isMac() ? "body:not(.undocked)" : "";
+ WebInspector._themeStyleElement.textContent =
+ String.sprintf(
+ "%s .toolbar-background {\
+ background-image: none !important;\
+ background-color: %s !important;\
+ color: %s !important;\
+ }", prefix, backgroundColor, color) +
+ String.sprintf(
+ "%s .toolbar-background button.status-bar-item .glyph, %s .toolbar-background button.status-bar-item .long-click-glyph {\
+ background-color: %s;\
+ }", prefix, prefix, color) +
+ String.sprintf(
+ "%s .toolbar-background button.status-bar-item .glyph.shadow, %s .toolbar-background button.status-bar-item .long-click-glyph.shadow {\
+ background-color: %s;\
+ }", prefix, prefix, shadowColor);
+}
+
+WebInspector.resetToolbarColors = function()
+{
+ if (WebInspector._themeStyleElement)
+ WebInspector._themeStyleElement.textContent = "";
+}
+
+/**
+ * @param {!Element} element
+ * @param {number} offset
+ * @param {number} length
+ * @param {!Array.<!Object>=} domChanges
+ * @return {?Element}
+ */
+WebInspector.highlightSearchResult = function(element, offset, length, domChanges)
+{
+ var result = WebInspector.highlightSearchResults(element, [new WebInspector.SourceRange(offset, length)], domChanges);
+ return result.length ? result[0] : null;
+}
+
+/**
+ * @param {!Element} element
+ * @param {!Array.<!WebInspector.SourceRange>} resultRanges
+ * @param {!Array.<!Object>=} changes
+ * @return {!Array.<!Element>}
+ */
+WebInspector.highlightSearchResults = function(element, resultRanges, changes)
+{
+ return WebInspector.highlightRangesWithStyleClass(element, resultRanges, "highlighted-search-result", changes);
+}
+
+/**
+ * @param {!Element} element
+ * @param {string} className
+ */
+WebInspector.runCSSAnimationOnce = function(element, className)
+{
+ function animationEndCallback()
+ {
+ element.classList.remove(className);
+ element.removeEventListener("animationend", animationEndCallback, false);
+ }
+
+ if (element.classList.contains(className))
+ element.classList.remove(className);
+
+ element.addEventListener("animationend", animationEndCallback, false);
+ element.classList.add(className);
+}
+
+/**
+ * @param {!Element} element
+ * @param {!Array.<!WebInspector.SourceRange>} resultRanges
+ * @param {string} styleClass
+ * @param {!Array.<!Object>=} changes
+ * @return {!Array.<!Element>}
+ */
+WebInspector.highlightRangesWithStyleClass = function(element, resultRanges, styleClass, changes)
+{
+ changes = changes || [];
+ var highlightNodes = [];
+ var lineText = element.textContent;
+ var ownerDocument = element.ownerDocument;
+ var textNodeSnapshot = ownerDocument.evaluate(".//text()", element, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+
+ var snapshotLength = textNodeSnapshot.snapshotLength;
+ if (snapshotLength === 0)
+ return highlightNodes;
+
+ var nodeRanges = [];
+ var rangeEndOffset = 0;
+ for (var i = 0; i < snapshotLength; ++i) {
+ var range = {};
+ range.offset = rangeEndOffset;
+ range.length = textNodeSnapshot.snapshotItem(i).textContent.length;
+ rangeEndOffset = range.offset + range.length;
+ nodeRanges.push(range);
+ }
+
+ var startIndex = 0;
+ for (var i = 0; i < resultRanges.length; ++i) {
+ var startOffset = resultRanges[i].offset;
+ var endOffset = startOffset + resultRanges[i].length;
+
+ while (startIndex < snapshotLength && nodeRanges[startIndex].offset + nodeRanges[startIndex].length <= startOffset)
+ startIndex++;
+ var endIndex = startIndex;
+ while (endIndex < snapshotLength && nodeRanges[endIndex].offset + nodeRanges[endIndex].length < endOffset)
+ endIndex++;
+ if (endIndex === snapshotLength)
+ break;
+
+ var highlightNode = ownerDocument.createElement("span");
+ highlightNode.className = styleClass;
+ highlightNode.textContent = lineText.substring(startOffset, endOffset);
+
+ var lastTextNode = textNodeSnapshot.snapshotItem(endIndex);
+ var lastText = lastTextNode.textContent;
+ lastTextNode.textContent = lastText.substring(endOffset - nodeRanges[endIndex].offset);
+ changes.push({ node: lastTextNode, type: "changed", oldText: lastText, newText: lastTextNode.textContent });
+
+ if (startIndex === endIndex) {
+ lastTextNode.parentElement.insertBefore(highlightNode, lastTextNode);
+ changes.push({ node: highlightNode, type: "added", nextSibling: lastTextNode, parent: lastTextNode.parentElement });
+ highlightNodes.push(highlightNode);
+
+ var prefixNode = ownerDocument.createTextNode(lastText.substring(0, startOffset - nodeRanges[startIndex].offset));
+ lastTextNode.parentElement.insertBefore(prefixNode, highlightNode);
+ changes.push({ node: prefixNode, type: "added", nextSibling: highlightNode, parent: lastTextNode.parentElement });
+ } else {
+ var firstTextNode = textNodeSnapshot.snapshotItem(startIndex);
+ var firstText = firstTextNode.textContent;
+ var anchorElement = firstTextNode.nextSibling;
+
+ firstTextNode.parentElement.insertBefore(highlightNode, anchorElement);
+ changes.push({ node: highlightNode, type: "added", nextSibling: anchorElement, parent: firstTextNode.parentElement });
+ highlightNodes.push(highlightNode);
+
+ firstTextNode.textContent = firstText.substring(0, startOffset - nodeRanges[startIndex].offset);
+ changes.push({ node: firstTextNode, type: "changed", oldText: firstText, newText: firstTextNode.textContent });
+
+ for (var j = startIndex + 1; j < endIndex; j++) {
+ var textNode = textNodeSnapshot.snapshotItem(j);
+ var text = textNode.textContent;
+ textNode.textContent = "";
+ changes.push({ node: textNode, type: "changed", oldText: text, newText: textNode.textContent });
+ }
+ }
+ startIndex = endIndex;
+ nodeRanges[startIndex].offset = endOffset;
+ nodeRanges[startIndex].length = lastTextNode.textContent.length;
+
+ }
+ return highlightNodes;
+}
+
+WebInspector.applyDomChanges = function(domChanges)
+{
+ for (var i = 0, size = domChanges.length; i < size; ++i) {
+ var entry = domChanges[i];
+ switch (entry.type) {
+ case "added":
+ entry.parent.insertBefore(entry.node, entry.nextSibling);
+ break;
+ case "changed":
+ entry.node.textContent = entry.newText;
+ break;
+ }
+ }
+}
+
+WebInspector.revertDomChanges = function(domChanges)
+{
+ for (var i = domChanges.length - 1; i >= 0; --i) {
+ var entry = domChanges[i];
+ switch (entry.type) {
+ case "added":
+ entry.node.remove();
+ break;
+ case "changed":
+ entry.node.textContent = entry.oldText;
+ break;
+ }
+ }
+}
+
+/**
+ * @constructor
+ * @param {boolean} autoInvoke
+ */
+WebInspector.InvokeOnceHandlers = function(autoInvoke)
+{
+ this._handlers = null;
+ this._autoInvoke = autoInvoke;
+}
+
+WebInspector.InvokeOnceHandlers.prototype = {
+ /**
+ * @param {!Object} object
+ * @param {function()} method
+ */
+ add: function(object, method)
+ {
+ if (!this._handlers) {
+ this._handlers = new Map();
+ if (this._autoInvoke)
+ this.scheduleInvoke();
+ }
+ var methods = this._handlers.get(object);
+ if (!methods) {
+ methods = new Set();
+ this._handlers.put(object, methods);
+ }
+ methods.add(method);
+ },
+
+ scheduleInvoke: function()
+ {
+ if (this._handlers)
+ requestAnimationFrame(this._invoke.bind(this));
+ },
+
+ _invoke: function()
+ {
+ var handlers = this._handlers;
+ this._handlers = null;
+ var keys = handlers.keys();
+ for (var i = 0; i < keys.length; ++i) {
+ var object = keys[i];
+ var methods = handlers.get(object).values();
+ for (var j = 0; j < methods.length; ++j)
+ methods[j].call(object);
+ }
+ }
+}
+
+WebInspector._coalescingLevel = 0;
+WebInspector._postUpdateHandlers = null;
+
+WebInspector.startBatchUpdate = function()
+{
+ if (!WebInspector._coalescingLevel++)
+ WebInspector._postUpdateHandlers = new WebInspector.InvokeOnceHandlers(false);
+}
+
+WebInspector.endBatchUpdate = function()
+{
+ if (--WebInspector._coalescingLevel)
+ return;
+ WebInspector._postUpdateHandlers.scheduleInvoke();
+ WebInspector._postUpdateHandlers = null;
+}
+
+/**
+ * @param {!Object} object
+ * @param {function()} method
+ */
+WebInspector.invokeOnceAfterBatchUpdate = function(object, method)
+{
+ if (!WebInspector._postUpdateHandlers)
+ WebInspector._postUpdateHandlers = new WebInspector.InvokeOnceHandlers(true);
+ WebInspector._postUpdateHandlers.add(object, method);
+}
+
+;(function() {
+
+function windowLoaded()
+{
+ window.addEventListener("focus", WebInspector._windowFocused, false);
+ window.addEventListener("blur", WebInspector._windowBlurred, false);
+ document.addEventListener("focus", WebInspector._focusChanged, true);
+ document.addEventListener("blur", WebInspector._documentBlurred, true);
+ window.removeEventListener("DOMContentLoaded", windowLoaded, false);
+}
+
+window.addEventListener("DOMContentLoaded", windowLoaded, false);
+
+})();
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/View.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/View.js
new file mode 100644
index 00000000000..3a841c088b5
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/View.js
@@ -0,0 +1,703 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2011 Google 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.View = function()
+{
+ this.element = document.createElement("div");
+ this.element.className = "view";
+ this.element.__view = this;
+ this._visible = true;
+ this._isRoot = false;
+ this._isShowing = false;
+ this._children = [];
+ this._hideOnDetach = false;
+ this._cssFiles = [];
+ this._notificationDepth = 0;
+}
+
+WebInspector.View._cssFileToVisibleViewCount = {};
+WebInspector.View._cssFileToStyleElement = {};
+WebInspector.View._cssUnloadTimeout = 2000;
+
+WebInspector.View._buildSourceURL = function(cssFile)
+{
+ return "\n/*# sourceURL=" + WebInspector.ParsedURL.completeURL(window.location.href, cssFile) + " */";
+}
+
+/**
+ * @param {string} cssFile
+ * @return {!Element}
+ */
+WebInspector.View.createStyleElement = function(cssFile)
+{
+ var styleElement = document.createElement("style");
+ styleElement.type = "text/css";
+ styleElement.textContent = loadResource(cssFile) + WebInspector.View._buildSourceURL(cssFile);
+ document.head.insertBefore(styleElement, document.head.firstChild);
+ return styleElement;
+}
+
+WebInspector.View.prototype = {
+ markAsRoot: function()
+ {
+ WebInspector.View._assert(!this.element.parentElement, "Attempt to mark as root attached node");
+ this._isRoot = true;
+ },
+
+ /**
+ * @return {?WebInspector.View}
+ */
+ parentView: function()
+ {
+ return this._parentView;
+ },
+
+ /**
+ * @return {!Array.<!WebInspector.View>}
+ */
+ children: function()
+ {
+ return this._children;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isShowing: function()
+ {
+ return this._isShowing;
+ },
+
+ setHideOnDetach: function()
+ {
+ this._hideOnDetach = true;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ _inNotification: function()
+ {
+ return !!this._notificationDepth || (this._parentView && this._parentView._inNotification());
+ },
+
+ _parentIsShowing: function()
+ {
+ if (this._isRoot)
+ return true;
+ return this._parentView && this._parentView.isShowing();
+ },
+
+ /**
+ * @param {function(this:WebInspector.View)} method
+ */
+ _callOnVisibleChildren: function(method)
+ {
+ var copy = this._children.slice();
+ for (var i = 0; i < copy.length; ++i) {
+ if (copy[i]._parentView === this && copy[i]._visible)
+ method.call(copy[i]);
+ }
+ },
+
+ _processWillShow: function()
+ {
+ this._loadCSSIfNeeded();
+ this._callOnVisibleChildren(this._processWillShow);
+ this._isShowing = true;
+ },
+
+ _processWasShown: function()
+ {
+ if (this._inNotification())
+ return;
+ this.restoreScrollPositions();
+ this._notify(this.wasShown);
+ this._callOnVisibleChildren(this._processWasShown);
+ },
+
+ _processWillHide: function()
+ {
+ if (this._inNotification())
+ return;
+ this.storeScrollPositions();
+
+ this._callOnVisibleChildren(this._processWillHide);
+ this._notify(this.willHide);
+ this._isShowing = false;
+ },
+
+ _processWasHidden: function()
+ {
+ this._disableCSSIfNeeded();
+ this._callOnVisibleChildren(this._processWasHidden);
+ },
+
+ _processOnResize: function()
+ {
+ if (this._inNotification())
+ return;
+ if (!this.isShowing())
+ return;
+ this._notify(this.onResize);
+ this._callOnVisibleChildren(this._processOnResize);
+ },
+
+ /**
+ * @param {function(this:WebInspector.View)} notification
+ */
+ _notify: function(notification)
+ {
+ ++this._notificationDepth;
+ try {
+ notification.call(this);
+ } finally {
+ --this._notificationDepth;
+ }
+ },
+
+ wasShown: function()
+ {
+ },
+
+ willHide: function()
+ {
+ },
+
+ onResize: function()
+ {
+ },
+
+ onLayout: function()
+ {
+ },
+
+ /**
+ * @param {?Element} parentElement
+ * @param {?Element=} insertBefore
+ */
+ show: function(parentElement, insertBefore)
+ {
+ WebInspector.View._assert(parentElement, "Attempt to attach view with no parent element");
+
+ // Update view hierarchy
+ if (this.element.parentElement !== parentElement) {
+ if (this.element.parentElement)
+ this.detach();
+
+ var currentParent = parentElement;
+ while (currentParent && !currentParent.__view)
+ currentParent = currentParent.parentElement;
+
+ if (currentParent) {
+ this._parentView = currentParent.__view;
+ this._parentView._children.push(this);
+ this._isRoot = false;
+ } else
+ WebInspector.View._assert(this._isRoot, "Attempt to attach view to orphan node");
+ } else if (this._visible) {
+ return;
+ }
+
+ this._visible = true;
+
+ if (this._parentIsShowing())
+ this._processWillShow();
+
+ this.element.classList.add("visible");
+
+ // Reparent
+ if (this.element.parentElement !== parentElement) {
+ WebInspector.View._incrementViewCounter(parentElement, this.element);
+ if (insertBefore)
+ WebInspector.View._originalInsertBefore.call(parentElement, this.element, insertBefore);
+ else
+ WebInspector.View._originalAppendChild.call(parentElement, this.element);
+ }
+
+ if (this._parentIsShowing())
+ this._processWasShown();
+
+ if (this._parentView && this._hasNonZeroConstraints())
+ this._parentView.invalidateConstraints();
+ else
+ this._processOnResize();
+ },
+
+ /**
+ * @param {boolean=} overrideHideOnDetach
+ */
+ detach: function(overrideHideOnDetach)
+ {
+ var parentElement = this.element.parentElement;
+ if (!parentElement)
+ return;
+
+ if (this._parentIsShowing())
+ this._processWillHide();
+
+ if (this._hideOnDetach && !overrideHideOnDetach) {
+ this.element.classList.remove("visible");
+ this._visible = false;
+ if (this._parentIsShowing())
+ this._processWasHidden();
+ if (this._parentView && this._hasNonZeroConstraints())
+ this._parentView.invalidateConstraints();
+ return;
+ }
+
+ // Force legal removal
+ WebInspector.View._decrementViewCounter(parentElement, this.element);
+ WebInspector.View._originalRemoveChild.call(parentElement, this.element);
+
+ this._visible = false;
+ if (this._parentIsShowing())
+ this._processWasHidden();
+
+ // Update view hierarchy
+ if (this._parentView) {
+ var childIndex = this._parentView._children.indexOf(this);
+ WebInspector.View._assert(childIndex >= 0, "Attempt to remove non-child view");
+ this._parentView._children.splice(childIndex, 1);
+ var parent = this._parentView;
+ this._parentView = null;
+ if (this._hasNonZeroConstraints())
+ parent.invalidateConstraints();
+ } else
+ WebInspector.View._assert(this._isRoot, "Removing non-root view from DOM");
+ },
+
+ detachChildViews: function()
+ {
+ var children = this._children.slice();
+ for (var i = 0; i < children.length; ++i)
+ children[i].detach();
+ },
+
+ /**
+ * @return {!Array.<!Element>}
+ */
+ elementsToRestoreScrollPositionsFor: function()
+ {
+ return [this.element];
+ },
+
+ storeScrollPositions: function()
+ {
+ var elements = this.elementsToRestoreScrollPositionsFor();
+ for (var i = 0; i < elements.length; ++i) {
+ var container = elements[i];
+ container._scrollTop = container.scrollTop;
+ container._scrollLeft = container.scrollLeft;
+ }
+ },
+
+ restoreScrollPositions: function()
+ {
+ var elements = this.elementsToRestoreScrollPositionsFor();
+ for (var i = 0; i < elements.length; ++i) {
+ var container = elements[i];
+ if (container._scrollTop)
+ container.scrollTop = container._scrollTop;
+ if (container._scrollLeft)
+ container.scrollLeft = container._scrollLeft;
+ }
+ },
+
+ doResize: function()
+ {
+ if (!this.isShowing())
+ return;
+ // No matter what notification we are in, dispatching onResize is not needed.
+ if (!this._inNotification())
+ this._callOnVisibleChildren(this._processOnResize);
+ },
+
+ doLayout: function()
+ {
+ if (!this.isShowing())
+ return;
+ this._notify(this.onLayout);
+ this.doResize();
+ },
+
+ registerRequiredCSS: function(cssFile)
+ {
+ this._cssFiles.push(cssFile);
+ },
+
+ _loadCSSIfNeeded: function()
+ {
+ for (var i = 0; i < this._cssFiles.length; ++i) {
+ var cssFile = this._cssFiles[i];
+
+ var viewsWithCSSFile = WebInspector.View._cssFileToVisibleViewCount[cssFile];
+ WebInspector.View._cssFileToVisibleViewCount[cssFile] = (viewsWithCSSFile || 0) + 1;
+ if (!viewsWithCSSFile)
+ this._doLoadCSS(cssFile);
+ }
+ },
+
+ _doLoadCSS: function(cssFile)
+ {
+ var styleElement = WebInspector.View._cssFileToStyleElement[cssFile];
+ if (styleElement) {
+ styleElement.disabled = false;
+ return;
+ }
+ styleElement = WebInspector.View.createStyleElement(cssFile);
+ WebInspector.View._cssFileToStyleElement[cssFile] = styleElement;
+ },
+
+ _disableCSSIfNeeded: function()
+ {
+ var scheduleUnload = !!WebInspector.View._cssUnloadTimer;
+
+ for (var i = 0; i < this._cssFiles.length; ++i) {
+ var cssFile = this._cssFiles[i];
+
+ if (!--WebInspector.View._cssFileToVisibleViewCount[cssFile])
+ scheduleUnload = true;
+ }
+
+ function doUnloadCSS()
+ {
+ delete WebInspector.View._cssUnloadTimer;
+
+ for (cssFile in WebInspector.View._cssFileToVisibleViewCount) {
+ if (WebInspector.View._cssFileToVisibleViewCount.hasOwnProperty(cssFile) && !WebInspector.View._cssFileToVisibleViewCount[cssFile])
+ WebInspector.View._cssFileToStyleElement[cssFile].disabled = true;
+ }
+ }
+
+ if (scheduleUnload && !WebInspector.View._cssUnloadTimer)
+ WebInspector.View._cssUnloadTimer = setTimeout(doUnloadCSS, WebInspector.View._cssUnloadTimeout);
+ },
+
+ printViewHierarchy: function()
+ {
+ var lines = [];
+ this._collectViewHierarchy("", lines);
+ console.log(lines.join("\n"));
+ },
+
+ _collectViewHierarchy: function(prefix, lines)
+ {
+ lines.push(prefix + "[" + this.element.className + "]" + (this._children.length ? " {" : ""));
+
+ for (var i = 0; i < this._children.length; ++i)
+ this._children[i]._collectViewHierarchy(prefix + " ", lines);
+
+ if (this._children.length)
+ lines.push(prefix + "}");
+ },
+
+ /**
+ * @return {!Element}
+ */
+ defaultFocusedElement: function()
+ {
+ return this._defaultFocusedElement || this.element;
+ },
+
+ /**
+ * @param {!Element} element
+ */
+ setDefaultFocusedElement: function(element)
+ {
+ this._defaultFocusedElement = element;
+ },
+
+ focus: function()
+ {
+ var element = this.defaultFocusedElement();
+ if (!element || element.isAncestor(document.activeElement))
+ return;
+
+ WebInspector.setCurrentFocusElement(element);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ hasFocus: function()
+ {
+ var activeElement = document.activeElement;
+ return activeElement && activeElement.isSelfOrDescendant(this.element);
+ },
+
+ /**
+ * @return {!Size}
+ */
+ measurePreferredSize: function()
+ {
+ this._loadCSSIfNeeded();
+ WebInspector.View._originalAppendChild.call(document.body, this.element);
+ this.element.positionAt(0, 0);
+ var result = new Size(this.element.offsetWidth, this.element.offsetHeight);
+ this.element.positionAt(undefined, undefined);
+ WebInspector.View._originalRemoveChild.call(document.body, this.element);
+ this._disableCSSIfNeeded();
+ return result;
+ },
+
+ /**
+ * @return {!Constraints}
+ */
+ calculateConstraints: function()
+ {
+ return new Constraints(new Size(0, 0));
+ },
+
+ /**
+ * @return {!Constraints}
+ */
+ constraints: function()
+ {
+ if (typeof this._constraints !== "undefined")
+ return this._constraints;
+ if (typeof this._cachedConstraints === "undefined")
+ this._cachedConstraints = this.calculateConstraints();
+ return this._cachedConstraints;
+ },
+
+ /**
+ * @param {number} width
+ * @param {number} height
+ * @param {number} preferredWidth
+ * @param {number} preferredHeight
+ */
+ setMinimumAndPreferredSizes: function(width, height, preferredWidth, preferredHeight)
+ {
+ this._constraints = new Constraints(new Size(width, height), new Size(preferredWidth, preferredHeight));
+ this.invalidateConstraints();
+ },
+
+ /**
+ * @param {number} width
+ * @param {number} height
+ */
+ setMinimumSize: function(width, height)
+ {
+ this._constraints = new Constraints(new Size(width, height));
+ this.invalidateConstraints();
+ },
+
+ /**
+ * @return {boolean}
+ */
+ _hasNonZeroConstraints: function()
+ {
+ var constraints = this.constraints();
+ return !!(constraints.minimum.width || constraints.minimum.height || constraints.preferred.width || constraints.preferred.height);
+ },
+
+ invalidateConstraints: function()
+ {
+ var cached = this._cachedConstraints;
+ delete this._cachedConstraints;
+ var actual = this.constraints();
+ if (!actual.isEqual(cached) && this._parentView)
+ this._parentView.invalidateConstraints();
+ else
+ this.doLayout();
+ },
+
+ __proto__: WebInspector.Object.prototype
+}
+
+WebInspector.View._originalAppendChild = Element.prototype.appendChild;
+WebInspector.View._originalInsertBefore = Element.prototype.insertBefore;
+WebInspector.View._originalRemoveChild = Element.prototype.removeChild;
+WebInspector.View._originalRemoveChildren = Element.prototype.removeChildren;
+
+WebInspector.View._incrementViewCounter = function(parentElement, childElement)
+{
+ var count = (childElement.__viewCounter || 0) + (childElement.__view ? 1 : 0);
+ if (!count)
+ return;
+
+ while (parentElement) {
+ parentElement.__viewCounter = (parentElement.__viewCounter || 0) + count;
+ parentElement = parentElement.parentElement;
+ }
+}
+
+WebInspector.View._decrementViewCounter = function(parentElement, childElement)
+{
+ var count = (childElement.__viewCounter || 0) + (childElement.__view ? 1 : 0);
+ if (!count)
+ return;
+
+ while (parentElement) {
+ parentElement.__viewCounter -= count;
+ parentElement = parentElement.parentElement;
+ }
+}
+
+WebInspector.View._assert = function(condition, message)
+{
+ if (!condition) {
+ console.trace();
+ throw new Error(message);
+ }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.VBox = function()
+{
+ WebInspector.View.call(this);
+ this.element.classList.add("vbox");
+};
+
+WebInspector.VBox.prototype = {
+ /**
+ * @return {!Constraints}
+ */
+ calculateConstraints: function()
+ {
+ var constraints = new Constraints(new Size(0, 0));
+
+ /**
+ * @this {!WebInspector.View}
+ * @suppressReceiverCheck
+ */
+ function updateForChild()
+ {
+ var child = this.constraints();
+ constraints = constraints.widthToMax(child);
+ constraints = constraints.addHeight(child);
+ }
+
+ this._callOnVisibleChildren(updateForChild);
+ return constraints;
+ },
+
+ __proto__: WebInspector.View.prototype
+};
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.HBox = function()
+{
+ WebInspector.View.call(this);
+ this.element.classList.add("hbox");
+};
+
+WebInspector.HBox.prototype = {
+ /**
+ * @return {!Constraints}
+ */
+ calculateConstraints: function()
+ {
+ var constraints = new Constraints(new Size(0, 0));
+
+ /**
+ * @this {!WebInspector.View}
+ * @suppressReceiverCheck
+ */
+ function updateForChild()
+ {
+ var child = this.constraints();
+ constraints = constraints.addWidth(child);
+ constraints = constraints.heightToMax(child);
+ }
+
+ this._callOnVisibleChildren(updateForChild);
+ return constraints;
+ },
+
+ __proto__: WebInspector.View.prototype
+};
+
+/**
+ * @constructor
+ * @extends {WebInspector.VBox}
+ * @param {function()} resizeCallback
+ */
+WebInspector.VBoxWithResizeCallback = function(resizeCallback)
+{
+ WebInspector.VBox.call(this);
+ this._resizeCallback = resizeCallback;
+}
+
+WebInspector.VBoxWithResizeCallback.prototype = {
+ onResize: function()
+ {
+ this._resizeCallback();
+ },
+
+ __proto__: WebInspector.VBox.prototype
+}
+
+/**
+ * @param {?Node} child
+ * @return {?Node}
+ * @suppress {duplicate}
+ */
+Element.prototype.appendChild = function(child)
+{
+ WebInspector.View._assert(!child.__view || child.parentElement === this, "Attempt to add view via regular DOM operation.");
+ return WebInspector.View._originalAppendChild.call(this, child);
+}
+
+/**
+ * @param {?Node} child
+ * @param {?Node} anchor
+ * @return {?Node}
+ * @suppress {duplicate}
+ */
+Element.prototype.insertBefore = function(child, anchor)
+{
+ WebInspector.View._assert(!child.__view || child.parentElement === this, "Attempt to add view via regular DOM operation.");
+ return WebInspector.View._originalInsertBefore.call(this, child, anchor);
+}
+
+/**
+ * @param {?Node} child
+ * @return {?Node}
+ * @suppress {duplicate}
+ */
+Element.prototype.removeChild = function(child)
+{
+ WebInspector.View._assert(!child.__viewCounter && !child.__view, "Attempt to remove element containing view via regular DOM operation");
+ return WebInspector.View._originalRemoveChild.call(this, child);
+}
+
+Element.prototype.removeChildren = function()
+{
+ WebInspector.View._assert(!this.__viewCounter, "Attempt to remove element containing view via regular DOM operation");
+ WebInspector.View._originalRemoveChildren.call(this);
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/ViewportControl.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/ViewportControl.js
new file mode 100644
index 00000000000..87d91652ad3
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/ViewportControl.js
@@ -0,0 +1,529 @@
+/*
+ * Copyright (C) 2013 Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+/**
+ * @constructor
+ * @param {!WebInspector.ViewportControl.Provider} provider
+ */
+WebInspector.ViewportControl = function(provider)
+{
+ this.element = document.createElement("div");
+ this.element.style.overflow = "auto";
+ this._topGapElement = this.element.createChild("div", "viewport-control-gap-element");
+ this._topGapElement.textContent = ".";
+ this._topGapElement.style.height = "0px";
+ this._contentElement = this.element.createChild("div");
+ this._bottomGapElement = this.element.createChild("div", "viewport-control-gap-element");
+ this._bottomGapElement.textContent = ".";
+ this._bottomGapElement.style.height = "0px";
+
+ this._provider = provider;
+ this.element.addEventListener("scroll", this._onScroll.bind(this), false);
+ this.element.addEventListener("copy", this._onCopy.bind(this), false);
+ this.element.addEventListener("dragstart", this._onDragStart.bind(this), false);
+
+ this._firstVisibleIndex = 0;
+ this._lastVisibleIndex = -1;
+ this._renderedItems = [];
+ this._anchorSelection = null;
+ this._headSelection = null;
+ this._stickToBottom = false;
+}
+
+/**
+ * @interface
+ */
+WebInspector.ViewportControl.Provider = function()
+{
+}
+
+WebInspector.ViewportControl.Provider.prototype = {
+ /**
+ * @param {number} index
+ * @return {number}
+ */
+ fastHeight: function(index) { return 0; },
+
+ /**
+ * @return {number}
+ */
+ itemCount: function() { return 0; },
+
+ /**
+ * @return {number}
+ */
+ minimumRowHeight: function() { return 0; },
+
+ /**
+ * @param {number} index
+ * @return {?WebInspector.ViewportElement}
+ */
+ itemElement: function(index) { return null; }
+}
+
+/**
+ * @interface
+ */
+WebInspector.ViewportElement = function() { }
+WebInspector.ViewportElement.prototype = {
+ cacheFastHeight: function() { },
+
+ willHide: function() { },
+
+ wasShown: function() { },
+
+ /**
+ * @return {!Element}
+ */
+ element: function() { },
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.ViewportElement}
+ * @param {!Element} element
+ */
+WebInspector.StaticViewportElement = function(element)
+{
+ this._element = element;
+}
+
+WebInspector.StaticViewportElement.prototype = {
+ cacheFastHeight: function() { },
+
+ willHide: function() { },
+
+ wasShown: function() { },
+
+ /**
+ * @return {!Element}
+ */
+ element: function()
+ {
+ return this._element;
+ },
+}
+
+WebInspector.ViewportControl.prototype = {
+ /**
+ * @param {boolean} value
+ */
+ setStickToBottom: function(value)
+ {
+ this._stickToBottom = value;
+ },
+
+ /**
+ * @param {?Event} event
+ */
+ _onCopy: function(event)
+ {
+ var text = this._selectedText();
+ if (!text)
+ return;
+ event.preventDefault();
+ event.clipboardData.setData("text/plain", text);
+ },
+
+ /**
+ * @param {?Event} event
+ */
+ _onDragStart: function(event)
+ {
+ var text = this._selectedText();
+ if (!text)
+ return false;
+ event.dataTransfer.clearData();
+ event.dataTransfer.setData("text/plain", text);
+ event.dataTransfer.effectAllowed = "copy";
+ return true;
+ },
+
+ /**
+ * @return {!Element}
+ */
+ contentElement: function()
+ {
+ return this._contentElement;
+ },
+
+ invalidate: function()
+ {
+ delete this._cumulativeHeights;
+ this.refresh();
+ },
+
+ _rebuildCumulativeHeightsIfNeeded: function()
+ {
+ if (this._cumulativeHeights)
+ return;
+ var itemCount = this._provider.itemCount();
+ if (!itemCount)
+ return;
+ this._cumulativeHeights = new Int32Array(itemCount);
+ this._cumulativeHeights[0] = this._provider.fastHeight(0);
+ for (var i = 1; i < itemCount; ++i)
+ this._cumulativeHeights[i] = this._cumulativeHeights[i - 1] + this._provider.fastHeight(i);
+ },
+
+ /**
+ * @param {number} index
+ * @return {number}
+ */
+ _cachedItemHeight: function(index)
+ {
+ return index === 0 ? this._cumulativeHeights[0] : this._cumulativeHeights[index] - this._cumulativeHeights[index - 1];
+ },
+
+ /**
+ * @param {?Selection} selection
+ */
+ _isSelectionBackwards: function(selection)
+ {
+ if (!selection || !selection.rangeCount)
+ return false;
+ var range = document.createRange();
+ range.setStart(selection.anchorNode, selection.anchorOffset);
+ range.setEnd(selection.focusNode, selection.focusOffset);
+ return range.collapsed;
+ },
+
+ /**
+ * @param {number} itemIndex
+ * @param {!Node} node
+ * @param {number} offset
+ * @return {!{item: number, node: !Node, offset: number}}
+ */
+ _createSelectionModel: function(itemIndex, node, offset)
+ {
+ return {
+ item: itemIndex,
+ node: node,
+ offset: offset
+ };
+ },
+
+ /**
+ * @param {?Selection} selection
+ */
+ _updateSelectionModel: function(selection)
+ {
+ if (!selection || !selection.rangeCount) {
+ this._headSelection = null;
+ this._anchorSelection = null;
+ return false;
+ }
+
+ var firstSelected = Number.MAX_VALUE;
+ var lastSelected = -1;
+
+ var range = selection.getRangeAt(0);
+ var hasVisibleSelection = false;
+ for (var i = 0; i < this._renderedItems.length; ++i) {
+ if (range.intersectsNode(this._renderedItems[i].element())) {
+ var index = i + this._firstVisibleIndex;
+ firstSelected = Math.min(firstSelected, index);
+ lastSelected = Math.max(lastSelected, index);
+ hasVisibleSelection = true;
+ }
+ }
+ if (hasVisibleSelection) {
+ firstSelected = this._createSelectionModel(firstSelected, /** @type {!Node} */(range.startContainer), range.startOffset);
+ lastSelected = this._createSelectionModel(lastSelected, /** @type {!Node} */(range.endContainer), range.endOffset);
+ }
+ var topOverlap = range.intersectsNode(this._topGapElement) && this._topGapElement._active;
+ var bottomOverlap = range.intersectsNode(this._bottomGapElement) && this._bottomGapElement._active;
+ if (!topOverlap && !bottomOverlap && !hasVisibleSelection) {
+ this._headSelection = null;
+ this._anchorSelection = null;
+ return false;
+ }
+
+ if (!this._anchorSelection || !this._headSelection) {
+ this._anchorSelection = this._createSelectionModel(0, this.element, 0);
+ this._headSelection = this._createSelectionModel(this._provider.itemCount() - 1, this.element, this.element.children.length);
+ this._selectionIsBackward = false;
+ }
+
+ var isBackward = this._isSelectionBackwards(selection);
+ var startSelection = this._selectionIsBackward ? this._headSelection : this._anchorSelection;
+ var endSelection = this._selectionIsBackward ? this._anchorSelection : this._headSelection;
+ if (topOverlap && bottomOverlap && hasVisibleSelection) {
+ firstSelected = firstSelected.item < startSelection.item ? firstSelected : startSelection;
+ lastSelected = lastSelected.item > endSelection.item ? lastSelected : endSelection;
+ } else if (!hasVisibleSelection) {
+ firstSelected = startSelection;
+ lastSelected = endSelection;
+ } else if (topOverlap)
+ firstSelected = isBackward ? this._headSelection : this._anchorSelection;
+ else if (bottomOverlap)
+ lastSelected = isBackward ? this._anchorSelection : this._headSelection;
+
+ if (isBackward) {
+ this._anchorSelection = lastSelected;
+ this._headSelection = firstSelected;
+ } else {
+ this._anchorSelection = firstSelected;
+ this._headSelection = lastSelected;
+ }
+ this._selectionIsBackward = isBackward;
+ return true;
+ },
+
+ /**
+ * @param {?Selection} selection
+ */
+ _restoreSelection: function(selection)
+ {
+ var anchorElement = null;
+ var anchorOffset;
+ if (this._firstVisibleIndex <= this._anchorSelection.item && this._anchorSelection.item <= this._lastVisibleIndex) {
+ anchorElement = this._anchorSelection.node;
+ anchorOffset = this._anchorSelection.offset;
+ } else {
+ if (this._anchorSelection.item < this._firstVisibleIndex)
+ anchorElement = this._topGapElement;
+ else if (this._anchorSelection.item > this._lastVisibleIndex)
+ anchorElement = this._bottomGapElement;
+ anchorOffset = this._selectionIsBackward ? 1 : 0;
+ }
+
+ var headElement = null;
+ var headOffset;
+ if (this._firstVisibleIndex <= this._headSelection.item && this._headSelection.item <= this._lastVisibleIndex) {
+ headElement = this._headSelection.node;
+ headOffset = this._headSelection.offset;
+ } else {
+ if (this._headSelection.item < this._firstVisibleIndex)
+ headElement = this._topGapElement;
+ else if (this._headSelection.item > this._lastVisibleIndex)
+ headElement = this._bottomGapElement;
+ headOffset = this._selectionIsBackward ? 0 : 1;
+ }
+
+ selection.setBaseAndExtent(anchorElement, anchorOffset, headElement, headOffset);
+ },
+
+ refresh: function()
+ {
+ if (!this.element.clientHeight)
+ return; // Do nothing for invisible controls.
+
+ var itemCount = this._provider.itemCount();
+ if (!itemCount) {
+ for (var i = 0; i < this._renderedItems.length; ++i)
+ this._renderedItems[i].cacheFastHeight();
+ for (var i = 0; i < this._renderedItems.length; ++i)
+ this._renderedItems[i].willHide();
+ this._renderedItems = [];
+ this._contentElement.removeChildren();
+ this._topGapElement.style.height = "0px";
+ this._bottomGapElement.style.height = "0px";
+ this._firstVisibleIndex = -1;
+ this._lastVisibleIndex = -1;
+ return;
+ }
+
+ var selection = window.getSelection();
+ var shouldRestoreSelection = this._updateSelectionModel(selection);
+
+ var visibleFrom = this.element.scrollTop;
+ var clientHeight = this.element.clientHeight;
+ var shouldStickToBottom = this._stickToBottom && this.element.isScrolledToBottom();
+
+ if (this._cumulativeHeights && itemCount !== this._cumulativeHeights.length)
+ delete this._cumulativeHeights;
+ for (var i = 0; i < this._renderedItems.length; ++i) {
+ this._renderedItems[i].cacheFastHeight();
+ // Tolerate 1-pixel error due to double-to-integer rounding errors.
+ if (this._cumulativeHeights && Math.abs(this._cachedItemHeight(this._firstVisibleIndex + i) - this._provider.fastHeight(i + this._firstVisibleIndex)) > 1)
+ delete this._cumulativeHeights;
+ }
+ this._rebuildCumulativeHeightsIfNeeded();
+ if (shouldStickToBottom) {
+ this._lastVisibleIndex = itemCount - 1;
+ this._firstVisibleIndex = Math.max(itemCount - Math.ceil(clientHeight / this._provider.minimumRowHeight()), 0);
+ } else {
+ this._firstVisibleIndex = Math.max(Array.prototype.lowerBound.call(this._cumulativeHeights, visibleFrom + 1), 0);
+ // Proactively render more rows in case some of them will be collapsed without triggering refresh. @see crbug.com/390169
+ this._lastVisibleIndex = this._firstVisibleIndex + Math.ceil(clientHeight / this._provider.minimumRowHeight()) - 1;
+ this._lastVisibleIndex = Math.min(this._lastVisibleIndex, itemCount - 1);
+ }
+ var topGapHeight = this._cumulativeHeights[this._firstVisibleIndex - 1] || 0;
+ var bottomGapHeight = this._cumulativeHeights[this._cumulativeHeights.length - 1] - this._cumulativeHeights[this._lastVisibleIndex];
+
+ this._topGapElement.style.height = topGapHeight + "px";
+ this._bottomGapElement.style.height = bottomGapHeight + "px";
+ this._topGapElement._active = !!topGapHeight;
+ this._bottomGapElement._active = !!bottomGapHeight;
+
+ this._contentElement.style.setProperty("height", "10000000px");
+ for (var i = 0; i < this._renderedItems.length; ++i)
+ this._renderedItems[i].willHide();
+ this._renderedItems = [];
+ this._contentElement.removeChildren();
+ for (var i = this._firstVisibleIndex; i <= this._lastVisibleIndex; ++i) {
+ var viewportElement = this._provider.itemElement(i);
+ this._contentElement.appendChild(viewportElement.element());
+ this._renderedItems.push(viewportElement);
+ viewportElement.wasShown();
+ }
+
+ this._contentElement.style.removeProperty("height");
+ // Should be the last call in the method as it might force layout.
+ if (shouldRestoreSelection)
+ this._restoreSelection(selection);
+ if (shouldStickToBottom)
+ this.element.scrollTop = this.element.scrollHeight;
+ },
+
+ /**
+ * @return {?string}
+ */
+ _selectedText: function()
+ {
+ this._updateSelectionModel(window.getSelection());
+ if (!this._headSelection || !this._anchorSelection)
+ return null;
+
+ var startSelection = null;
+ var endSelection = null;
+ if (this._selectionIsBackward) {
+ startSelection = this._headSelection;
+ endSelection = this._anchorSelection;
+ } else {
+ startSelection = this._anchorSelection;
+ endSelection = this._headSelection;
+ }
+
+ var textLines = [];
+ for (var i = startSelection.item; i <= endSelection.item; ++i)
+ textLines.push(this._provider.itemElement(i).element().textContent);
+
+ var endSelectionElement = this._provider.itemElement(endSelection.item).element();
+ if (endSelection.node && endSelection.node.isSelfOrDescendant(endSelectionElement)) {
+ var itemTextOffset = this._textOffsetInNode(endSelectionElement, endSelection.node, endSelection.offset);
+ textLines[textLines.length - 1] = textLines.peekLast().substring(0, itemTextOffset);
+ }
+
+ var startSelectionElement = this._provider.itemElement(startSelection.item).element();
+ if (startSelection.node && startSelection.node.isSelfOrDescendant(startSelectionElement)) {
+ var itemTextOffset = this._textOffsetInNode(startSelectionElement, startSelection.node, startSelection.offset);
+ textLines[0] = textLines[0].substring(itemTextOffset);
+ }
+
+ return textLines.join("\n");
+ },
+
+ /**
+ * @param {!Element} itemElement
+ * @param {!Node} container
+ * @param {number} offset
+ * @return {number}
+ */
+ _textOffsetInNode: function(itemElement, container, offset)
+ {
+ var chars = 0;
+ var node = itemElement;
+ while ((node = node.traverseNextTextNode()) && node !== container)
+ chars += node.textContent.length;
+ return chars + offset;
+ },
+
+ /**
+ * @param {?Event} event
+ */
+ _onScroll: function(event)
+ {
+ this.refresh();
+ },
+
+ /**
+ * @return {number}
+ */
+ firstVisibleIndex: function()
+ {
+ return this._firstVisibleIndex;
+ },
+
+ /**
+ * @return {number}
+ */
+ lastVisibleIndex: function()
+ {
+ return this._lastVisibleIndex;
+ },
+
+ /**
+ * @return {?Element}
+ */
+ renderedElementAt: function(index)
+ {
+ if (index < this._firstVisibleIndex)
+ return null;
+ if (index > this._lastVisibleIndex)
+ return null;
+ return this._renderedItems[index - this._firstVisibleIndex].element();
+ },
+
+ /**
+ * @param {number} index
+ * @param {boolean=} makeLast
+ */
+ scrollItemIntoView: function(index, makeLast)
+ {
+ if (index > this._firstVisibleIndex && index < this._lastVisibleIndex)
+ return;
+ if (makeLast)
+ this.forceScrollItemToBeLast(index);
+ else if (index <= this._firstVisibleIndex)
+ this.forceScrollItemToBeFirst(index);
+ else if (index >= this._lastVisibleIndex)
+ this.forceScrollItemToBeLast(index);
+ },
+
+ /**
+ * @param {number} index
+ */
+ forceScrollItemToBeFirst: function(index)
+ {
+ this._rebuildCumulativeHeightsIfNeeded();
+ this.element.scrollTop = index > 0 ? this._cumulativeHeights[index - 1] : 0;
+ },
+
+ /**
+ * @param {number} index
+ */
+ forceScrollItemToBeLast: function(index)
+ {
+ this._rebuildCumulativeHeightsIfNeeded();
+ this.element.scrollTop = this._cumulativeHeights[index] - this.element.clientHeight;
+ }
+}
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/ZoomManager.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/ZoomManager.js
new file mode 100644
index 00000000000..83aa496aca5
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/ZoomManager.js
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.ZoomManager = function()
+{
+ this._zoomFactor = InspectorFrontendHost.zoomFactor();
+ window.addEventListener("resize", this._onWindowResize.bind(this), true);
+};
+
+WebInspector.ZoomManager.Events = {
+ ZoomChanged: "ZoomChanged"
+};
+
+WebInspector.ZoomManager.prototype = {
+ /**
+ * @return {number}
+ */
+ zoomFactor: function()
+ {
+ return this._zoomFactor;
+ },
+
+ _onWindowResize: function()
+ {
+ var oldZoomFactor = this._zoomFactor;
+ this._zoomFactor = InspectorFrontendHost.zoomFactor();
+ if (oldZoomFactor !== this._zoomFactor)
+ this.dispatchEventToListeners(WebInspector.ZoomManager.Events.ZoomChanged, {from: oldZoomFactor, to: this._zoomFactor});
+ },
+
+ __proto__: WebInspector.Object.prototype
+};
+
+/**
+ * @type {!WebInspector.ZoomManager}
+ */
+WebInspector.zoomManager;
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/treeoutline.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/treeoutline.js
new file mode 100644
index 00000000000..437b58e190d
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/treeoutline.js
@@ -0,0 +1,990 @@
+/*
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @param {!Element} listNode
+ * @param {boolean=} nonFocusable
+ */
+function TreeOutline(listNode, nonFocusable)
+{
+ /** @type {!Array.<!TreeElement>} */
+ this.children = [];
+ this.selectedTreeElement = null;
+ this._childrenListNode = listNode;
+ this.childrenListElement = this._childrenListNode;
+ this._childrenListNode.removeChildren();
+ this.expandTreeElementsWhenArrowing = false;
+ this.root = true;
+ this.hasChildren = false;
+ this.expanded = true;
+ this.selected = false;
+ this.treeOutline = this;
+ /** @type {?function(!TreeElement, !TreeElement):number} */
+ this.comparator = null;
+
+ this.setFocusable(!nonFocusable);
+ this._childrenListNode.addEventListener("keydown", this._treeKeyDown.bind(this), true);
+
+ /** @type {!Map.<!Object, !Array.<!TreeElement>>} */
+ this._treeElementsMap = new Map();
+ /** @type {!Map.<!Object, boolean>} */
+ this._expandedStateMap = new Map();
+ this.element = listNode;
+}
+
+TreeOutline.prototype.setFocusable = function(focusable)
+{
+ if (focusable)
+ this._childrenListNode.setAttribute("tabIndex", 0);
+ else
+ this._childrenListNode.removeAttribute("tabIndex");
+}
+
+/**
+ * @param {!TreeElement} child
+ */
+TreeOutline.prototype.appendChild = function(child)
+{
+ var insertionIndex;
+ if (this.treeOutline.comparator)
+ insertionIndex = insertionIndexForObjectInListSortedByFunction(child, this.children, this.treeOutline.comparator);
+ else
+ insertionIndex = this.children.length;
+ this.insertChild(child, insertionIndex);
+}
+
+/**
+ * @param {!TreeElement} child
+ * @param {!TreeElement} beforeChild
+ */
+TreeOutline.prototype.insertBeforeChild = function(child, beforeChild)
+{
+ if (!child)
+ throw("child can't be undefined or null");
+
+ if (!beforeChild)
+ throw("beforeChild can't be undefined or null");
+
+ var childIndex = this.children.indexOf(beforeChild);
+ if (childIndex === -1)
+ throw("beforeChild not found in this node's children");
+
+ this.insertChild(child, childIndex);
+}
+
+/**
+ * @param {!TreeElement} child
+ * @param {number} index
+ */
+TreeOutline.prototype.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 && typeof(child.treeOutline._expandedStateMap.get(child.representedObject)) !== "undefined")
+ child.expanded = child.treeOutline._expandedStateMap.get(child.representedObject);
+
+ if (!this._childrenListNode) {
+ this._childrenListNode = this.treeOutline._childrenListNode.ownerDocument.createElement("ol");
+ this._childrenListNode.parentTreeElement = this;
+ this._childrenListNode.classList.add("children");
+ if (this.hidden)
+ this._childrenListNode.classList.add("hidden");
+ }
+
+ child._attach();
+}
+
+/**
+ * @param {number} childIndex
+ */
+TreeOutline.prototype.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);
+
+ var parent = child.parent;
+ if (child.deselect()) {
+ if (child.previousSibling)
+ child.previousSibling.select();
+ else if (child.nextSibling)
+ child.nextSibling.select();
+ else
+ parent.select();
+ }
+
+ 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;
+}
+
+/**
+ * @param {!TreeElement} child
+ */
+TreeOutline.prototype.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");
+
+ this.removeChildAtIndex.call(this, childIndex);
+}
+
+TreeOutline.prototype.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 = [];
+}
+
+/**
+ * @param {!TreeElement} element
+ */
+TreeOutline.prototype._rememberTreeElement = function(element)
+{
+ if (!this._treeElementsMap.get(element.representedObject))
+ this._treeElementsMap.put(element.representedObject, []);
+
+ // check if the element is already known
+ var elements = this._treeElementsMap.get(element.representedObject);
+ if (elements.indexOf(element) !== -1)
+ return;
+
+ // add the element
+ elements.push(element);
+}
+
+/**
+ * @param {!TreeElement} element
+ */
+TreeOutline.prototype._forgetTreeElement = function(element)
+{
+ if (this._treeElementsMap.get(element.representedObject)) {
+ var elements = this._treeElementsMap.get(element.representedObject);
+ elements.remove(element, true);
+ if (!elements.length)
+ this._treeElementsMap.remove(element.representedObject);
+ }
+}
+
+/**
+ * @param {!TreeElement} parentElement
+ */
+TreeOutline.prototype._forgetChildrenRecursive = function(parentElement)
+{
+ var child = parentElement.children[0];
+ while (child) {
+ this._forgetTreeElement(child);
+ child = child.traverseNextTreeElement(false, parentElement, true);
+ }
+}
+
+/**
+ * @param {?Object} representedObject
+ * @return {?TreeElement}
+ */
+TreeOutline.prototype.getCachedTreeElement = function(representedObject)
+{
+ if (!representedObject)
+ return null;
+
+ var elements = this._treeElementsMap.get(representedObject);
+ if (elements && elements.length)
+ return elements[0];
+ return null;
+}
+
+/**
+ * @param {?Object} representedObject
+ * @param {function(!Object):?Object} getParent
+ * @return {?TreeElement}
+ */
+TreeOutline.prototype.findTreeElement = function(representedObject, getParent)
+{
+ if (!representedObject)
+ return null;
+
+ var cachedElement = this.getCachedTreeElement(representedObject);
+ if (cachedElement)
+ return cachedElement;
+
+ // Walk up the parent pointers from the desired representedObject
+ var ancestors = [];
+ for (var currentObject = getParent(representedObject); currentObject; currentObject = getParent(currentObject)) {
+ ancestors.push(currentObject);
+ if (this.getCachedTreeElement(currentObject)) // stop climbing as soon as we hit
+ break;
+ }
+
+ if (!currentObject)
+ return null;
+
+ // Walk down to populate each ancestor's children, to fill in the tree and the cache.
+ for (var i = ancestors.length - 1; i >= 0; --i) {
+ var treeElement = this.getCachedTreeElement(ancestors[i]);
+ if (treeElement)
+ treeElement.onpopulate(); // fill the cache with the children of treeElement
+ }
+
+ return this.getCachedTreeElement(representedObject);
+}
+
+/**
+ * @param {number} x
+ * @param {number} y
+ * @return {?TreeElement}
+ */
+TreeOutline.prototype.treeElementFromPoint = function(x, y)
+{
+ var node = this._childrenListNode.ownerDocument.elementFromPoint(x, y);
+ if (!node)
+ return null;
+
+ var listNode = node.enclosingNodeOrSelfWithNodeNameInArray(["ol", "li"]);
+ if (listNode)
+ return listNode.parentTreeElement || listNode.treeElement;
+ return null;
+}
+
+TreeOutline.prototype._treeKeyDown = function(event)
+{
+ if (event.target !== this._childrenListNode)
+ return;
+
+ if (!this.selectedTreeElement || event.shiftKey || event.metaKey || event.ctrlKey)
+ return;
+
+ 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;
+ while (nextSelectedElement && !nextSelectedElement.selectable)
+ nextSelectedElement = nextSelectedElement.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];
+ while (nextSelectedElement && !nextSelectedElement.selectable)
+ nextSelectedElement = nextSelectedElement.nextSibling;
+ handled = nextSelectedElement ? true : false;
+ } else {
+ if (event.altKey)
+ this.selectedTreeElement.expandRecursively();
+ else
+ this.selectedTreeElement.expand();
+ }
+ }
+ } else if (event.keyCode === 8 /* Backspace */ || event.keyCode === 46 /* Delete */)
+ handled = this.selectedTreeElement.ondelete();
+ else if (isEnterKey(event))
+ handled = this.selectedTreeElement.onenter();
+ else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Space.code)
+ handled = this.selectedTreeElement.onspace();
+
+ if (nextSelectedElement) {
+ nextSelectedElement.reveal();
+ nextSelectedElement.select(false, true);
+ }
+
+ if (handled)
+ event.consume(true);
+}
+
+TreeOutline.prototype.expand = function()
+{
+ // this is the root, do nothing
+}
+
+TreeOutline.prototype.collapse = function()
+{
+ // this is the root, do nothing
+}
+
+/**
+ * @return {boolean}
+ */
+TreeOutline.prototype.revealed = function()
+{
+ return true;
+}
+
+TreeOutline.prototype.reveal = function()
+{
+ // this is the root, do nothing
+}
+
+TreeOutline.prototype.select = function()
+{
+ // this is the root, do nothing
+}
+
+/**
+ * @param {boolean=} omitFocus
+ */
+TreeOutline.prototype.revealAndSelect = function(omitFocus)
+{
+ // this is the root, do nothing
+}
+
+/**
+ * @constructor
+ * @param {string|!Node} title
+ * @param {?Object=} representedObject
+ * @param {boolean=} hasChildren
+ */
+function TreeElement(title, representedObject, hasChildren)
+{
+ this._title = title;
+ this.representedObject = (representedObject || {});
+
+ this.root = false;
+ this._hidden = false;
+ this._selectable = true;
+ 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 = {
+ arrowToggleWidth: 10,
+
+ get selectable() {
+ if (this._hidden)
+ return false;
+ return this._selectable;
+ },
+
+ set selectable(x) {
+ this._selectable = x;
+ },
+
+ get listItemElement() {
+ return this._listItemNode;
+ },
+
+ get childrenListElement() {
+ return this._childrenListNode;
+ },
+
+ get title() {
+ return this._title;
+ },
+
+ set title(x) {
+ this._title = x;
+ this._setListItemNodeContent();
+ },
+
+ 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.classList.add("parent");
+ else {
+ this._listItemNode.classList.remove("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.classList.add("hidden");
+ if (this._childrenListNode)
+ this._childrenListNode.classList.add("hidden");
+ } else {
+ if (this._listItemNode)
+ this._listItemNode.classList.remove("hidden");
+ if (this._childrenListNode)
+ this._childrenListNode.classList.remove("hidden");
+ }
+ },
+
+ get shouldRefreshChildren() {
+ return this._shouldRefreshChildren;
+ },
+
+ set shouldRefreshChildren(x) {
+ this._shouldRefreshChildren = x;
+ if (x && this.expanded)
+ this.expand();
+ },
+
+ _setListItemNodeContent: function()
+ {
+ if (!this._listItemNode)
+ return;
+
+ if (typeof this._title === "string")
+ this._listItemNode.textContent = this._title;
+ else {
+ this._listItemNode.removeChildren();
+ if (this._title)
+ this._listItemNode.appendChild(this._title);
+ }
+ }
+}
+
+TreeElement.prototype.appendChild = TreeOutline.prototype.appendChild;
+TreeElement.prototype.insertChild = TreeOutline.prototype.insertChild;
+TreeElement.prototype.insertBeforeChild = TreeOutline.prototype.insertBeforeChild;
+TreeElement.prototype.removeChild = TreeOutline.prototype.removeChild;
+TreeElement.prototype.removeChildAtIndex = TreeOutline.prototype.removeChildAtIndex;
+TreeElement.prototype.removeChildren = TreeOutline.prototype.removeChildren;
+
+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._setListItemNodeContent();
+ this._listItemNode.title = this._tooltip ? this._tooltip : "";
+
+ if (this.hidden)
+ this._listItemNode.classList.add("hidden");
+ if (this.hasChildren)
+ this._listItemNode.classList.add("parent");
+ if (this.expanded)
+ this._listItemNode.classList.add("expanded");
+ if (this.selected)
+ this._listItemNode.classList.add("selected");
+
+ this._listItemNode.addEventListener("mousedown", TreeElement.treeElementMouseDown, false);
+ this._listItemNode.addEventListener("click", TreeElement.treeElementToggled, false);
+ this._listItemNode.addEventListener("dblclick", TreeElement.treeElementDoubleClicked, false);
+
+ this.onattach();
+ }
+
+ 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.treeElementMouseDown = function(event)
+{
+ var element = event.currentTarget;
+ if (!element || !element.treeElement || !element.treeElement.selectable)
+ return;
+
+ if (element.treeElement.isEventWithinDisclosureTriangle(event))
+ return;
+
+ element.treeElement.selectOnMouseDown(event);
+}
+
+TreeElement.treeElementToggled = function(event)
+{
+ var element = event.currentTarget;
+ if (!element || !element.treeElement)
+ return;
+
+ var toggleOnClick = element.treeElement.toggleOnClick && !element.treeElement.selectable;
+ var isInTriangle = element.treeElement.isEventWithinDisclosureTriangle(event);
+ if (!toggleOnClick && !isInTriangle)
+ 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();
+ }
+ event.consume();
+}
+
+TreeElement.treeElementDoubleClicked = function(event)
+{
+ var element = event.currentTarget;
+ if (!element || !element.treeElement)
+ return;
+
+ var handled = element.treeElement.ondblclick.call(element.treeElement, event);
+ if (handled)
+ return;
+ if (element.treeElement.hasChildren && !element.treeElement.expanded)
+ element.treeElement.expand();
+}
+
+TreeElement.prototype.collapse = function()
+{
+ if (this._listItemNode)
+ this._listItemNode.classList.remove("expanded");
+ if (this._childrenListNode)
+ this._childrenListNode.classList.remove("expanded");
+
+ this.expanded = false;
+
+ if (this.treeOutline)
+ this.treeOutline._expandedStateMap.put(this.representedObject, false);
+
+ this.oncollapse();
+}
+
+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;
+
+ // Set this before onpopulate. Since onpopulate can add elements, this makes
+ // sure the expanded flag is true before calling those functions. This prevents the possibility
+ // of an infinite loop if onpopulate were to call expand.
+
+ this.expanded = true;
+ if (this.treeOutline)
+ this.treeOutline._expandedStateMap.put(this.representedObject, true);
+
+ 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.classList.add("children");
+
+ if (this.hidden)
+ this._childrenListNode.classList.add("hidden");
+
+ this.onpopulate();
+
+ for (var i = 0; i < this.children.length; ++i)
+ this.children[i]._attach();
+
+ delete this._shouldRefreshChildren;
+ }
+
+ if (this._listItemNode) {
+ this._listItemNode.classList.add("expanded");
+ if (this._childrenListNode && this._childrenListNode.parentNode != this._listItemNode.parentNode)
+ this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling);
+ }
+
+ if (this._childrenListNode)
+ this._childrenListNode.classList.add("expanded");
+
+ this.onexpand();
+}
+
+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 (isNaN(maxDepth))
+ maxDepth = 3;
+
+ while (item) {
+ if (depth < maxDepth)
+ item.expand();
+ item = item.traverseNextTreeElement(false, this, (depth >= maxDepth), info);
+ depth += info.depthChange;
+ }
+}
+
+/**
+ * @param {?TreeElement} ancestor
+ * @return {boolean}
+ */
+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;
+ }
+
+ this.onreveal();
+}
+
+/**
+ * @return {boolean}
+ */
+TreeElement.prototype.revealed = function()
+{
+ var currentAncestor = this.parent;
+ while (currentAncestor && !currentAncestor.root) {
+ if (!currentAncestor.expanded)
+ return false;
+ currentAncestor = currentAncestor.parent;
+ }
+
+ return true;
+}
+
+TreeElement.prototype.selectOnMouseDown = function(event)
+{
+ if (this.select(false, true))
+ event.consume(true);
+}
+
+/**
+ * @param {boolean=} omitFocus
+ * @param {boolean=} selectedByUser
+ * @return {boolean}
+ */
+TreeElement.prototype.select = function(omitFocus, selectedByUser)
+{
+ if (!this.treeOutline || !this.selectable || this.selected)
+ return false;
+
+ if (this.treeOutline.selectedTreeElement)
+ this.treeOutline.selectedTreeElement.deselect();
+
+ this.selected = true;
+
+ if (!omitFocus)
+ this.treeOutline._childrenListNode.focus();
+
+ // Focusing on another node may detach "this" from tree.
+ if (!this.treeOutline)
+ return false;
+ this.treeOutline.selectedTreeElement = this;
+ if (this._listItemNode)
+ this._listItemNode.classList.add("selected");
+
+ return this.onselect(selectedByUser);
+}
+
+/**
+ * @param {boolean=} omitFocus
+ */
+TreeElement.prototype.revealAndSelect = function(omitFocus)
+{
+ this.reveal();
+ this.select(omitFocus);
+}
+
+/**
+ * @param {boolean=} supressOnDeselect
+ * @return {boolean}
+ */
+TreeElement.prototype.deselect = function(supressOnDeselect)
+{
+ if (!this.treeOutline || this.treeOutline.selectedTreeElement !== this || !this.selected)
+ return false;
+
+ this.selected = false;
+ this.treeOutline.selectedTreeElement = null;
+ if (this._listItemNode)
+ this._listItemNode.classList.remove("selected");
+ return true;
+}
+
+// Overridden by subclasses.
+TreeElement.prototype.onpopulate = function() { }
+
+/**
+ * @return {boolean}
+ */
+TreeElement.prototype.onenter = function() { return false; }
+
+/**
+ * @return {boolean}
+ */
+TreeElement.prototype.ondelete = function() { return false; }
+
+/**
+ * @return {boolean}
+ */
+TreeElement.prototype.onspace = function() { return false; }
+
+TreeElement.prototype.onattach = function() { }
+
+TreeElement.prototype.onexpand = function() { }
+
+TreeElement.prototype.oncollapse = function() { }
+
+/**
+ * @param {!MouseEvent} e
+ * @return {boolean}
+ */
+TreeElement.prototype.ondblclick = function(e) { return false; }
+
+TreeElement.prototype.onreveal = function() { }
+
+/**
+ * @param {boolean=} selectedByUser
+ * @return {boolean}
+ */
+TreeElement.prototype.onselect = function(selectedByUser) { return false; }
+
+/**
+ * @param {boolean} skipUnrevealed
+ * @param {(!TreeOutline|!TreeElement|null)=} stayWithin
+ * @param {boolean=} dontPopulate
+ * @param {!Object=} info
+ * @return {?TreeElement}
+ */
+TreeElement.prototype.traverseNextTreeElement = function(skipUnrevealed, stayWithin, dontPopulate, info)
+{
+ if (!dontPopulate && this.hasChildren)
+ this.onpopulate();
+
+ if (info)
+ info.depthChange = 0;
+
+ var element = skipUnrevealed ? (this.revealed() ? this.children[0] : null) : this.children[0];
+ if (element && (!skipUnrevealed || (skipUnrevealed && this.expanded))) {
+ if (info)
+ info.depthChange = 1;
+ return element;
+ }
+
+ if (this === stayWithin)
+ return null;
+
+ element = skipUnrevealed ? (this.revealed() ? this.nextSibling : null) : this.nextSibling;
+ if (element)
+ return element;
+
+ element = this;
+ while (element && !element.root && !(skipUnrevealed ? (element.revealed() ? element.nextSibling : null) : element.nextSibling) && element.parent !== stayWithin) {
+ if (info)
+ info.depthChange -= 1;
+ element = element.parent;
+ }
+
+ if (!element)
+ return null;
+
+ return (skipUnrevealed ? (element.revealed() ? element.nextSibling : null) : element.nextSibling);
+}
+
+/**
+ * @param {boolean} skipUnrevealed
+ * @param {boolean=} dontPopulate
+ * @return {?TreeElement}
+ */
+TreeElement.prototype.traversePreviousTreeElement = function(skipUnrevealed, dontPopulate)
+{
+ var element = skipUnrevealed ? (this.revealed() ? this.previousSibling : null) : this.previousSibling;
+ if (!dontPopulate && element && element.hasChildren)
+ element.onpopulate();
+
+ while (element && (skipUnrevealed ? (element.revealed() && element.expanded ? element.children[element.children.length - 1] : null) : element.children[element.children.length - 1])) {
+ if (!dontPopulate && element.hasChildren)
+ element.onpopulate();
+ element = (skipUnrevealed ? (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;
+}
+
+/**
+ * @return {boolean}
+ */
+TreeElement.prototype.isEventWithinDisclosureTriangle = function(event)
+{
+ // FIXME: We should not use getComputedStyle(). For that we need to get rid of using ::before for disclosure triangle. (http://webk.it/74446)
+ var paddingLeftValue = window.getComputedStyle(this._listItemNode).getPropertyCSSValue("padding-left");
+ var computedLeftPadding = paddingLeftValue ? paddingLeftValue.getFloatValue(CSSPrimitiveValue.CSS_PX) : 0;
+ var left = this._listItemNode.totalOffsetLeft() + computedLeftPadding;
+ return event.pageX >= left && event.pageX <= left + this.arrowToggleWidth && this.hasChildren;
+}