summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/WebKit/Source/devtools/front_end/ui/DataGrid.js
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/WebKit/Source/devtools/front_end/ui/DataGrid.js')
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/ui/DataGrid.js1837
1 files changed, 1837 insertions, 0 deletions
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
+}