1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/**
6 * @constructor
7 * @extends {WebInspector.ViewportDataGrid}
8 * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>} columnsArray
9 * @param {function(!WebInspector.DataGridNode, string, string, string)=} editCallback
10 * @param {function(!WebInspector.DataGridNode)=} deleteCallback
11 * @param {function()=} refreshCallback
12 * @param {function(!WebInspector.ContextMenu, !WebInspector.DataGridNode)=} contextMenuCallback
13 */
14WebInspector.SortableDataGrid = function(columnsArray, editCallback, deleteCallback, refreshCallback, contextMenuCallback)
15{
16    WebInspector.ViewportDataGrid.call(this, columnsArray, editCallback, deleteCallback, refreshCallback, contextMenuCallback);
17    /** @type {!WebInspector.SortableDataGrid.NodeComparator} */
18    this._sortingFunction = WebInspector.SortableDataGrid.TrivialComparator;
19    this.setRootNode(new WebInspector.SortableDataGridNode());
20}
21
22/** @typedef {function(!WebInspector.DataGridNode, !WebInspector.DataGridNode):number} */
23WebInspector.SortableDataGrid.NodeComparator;
24
25/**
26 * @param {!WebInspector.DataGridNode} a
27 * @param {!WebInspector.DataGridNode} b
28 * @return {number}
29 */
30WebInspector.SortableDataGrid.TrivialComparator = function(a, b)
31{
32    return 0;
33}
34
35/**
36 * @param {string} columnIdentifier
37 * @param {!WebInspector.DataGridNode} a
38 * @param {!WebInspector.DataGridNode} b
39 * @return {number}
40 */
41WebInspector.SortableDataGrid.NumericComparator = function(columnIdentifier, a, b)
42{
43    var aValue = a.data[columnIdentifier];
44    var bValue = b.data[columnIdentifier];
45    var aNumber = Number(aValue instanceof Node ? aValue.textContent : aValue);
46    var bNumber = Number(bValue instanceof Node ? bValue.textContent : bValue);
47    return aNumber < bNumber ? -1 : (aNumber > bNumber ? 1 : 0);
48}
49
50/**
51 * @param {string} columnIdentifier
52 * @param {!WebInspector.DataGridNode} a
53 * @param {!WebInspector.DataGridNode} b
54 * @return {number}
55 */
56WebInspector.SortableDataGrid.StringComparator = function(columnIdentifier, a, b)
57{
58    var aValue = a.data[columnIdentifier];
59    var bValue = b.data[columnIdentifier];
60    var aString = aValue instanceof Node ? aValue.textContent : String(aValue);
61    var bString = bValue instanceof Node ? bValue.textContent : String(bValue);
62    return aString < bString ? -1 : (aString > bString ? 1 : 0);
63}
64
65/**
66 * @param {!WebInspector.SortableDataGrid.NodeComparator} comparator
67 * @param {boolean} reverseMode
68 * @param {!WebInspector.DataGridNode} a
69 * @param {!WebInspector.DataGridNode} b
70 * @return {number}
71 */
72WebInspector.SortableDataGrid.Comparator = function(comparator, reverseMode, a, b)
73{
74    return reverseMode ? comparator(b, a) : comparator(a, b);
75}
76
77/**
78 * @param {!Array.<string>} columnNames
79 * @param {!Array.<string>} values
80 * @return {?WebInspector.SortableDataGrid}
81 */
82WebInspector.SortableDataGrid.create = function(columnNames, values)
83{
84    var numColumns = columnNames.length;
85    if (!numColumns)
86        return null;
87
88    var columns = [];
89    for (var i = 0; i < columnNames.length; ++i)
90        columns.push({ title: columnNames[i], width: columnNames[i].length, sortable: true });
91
92    var nodes = [];
93    for (var i = 0; i < values.length / numColumns; ++i) {
94        var data = {};
95        for (var j = 0; j < columnNames.length; ++j)
96            data[j] = values[numColumns * i + j];
97
98        var node = new WebInspector.SortableDataGridNode(data);
99        node.selectable = false;
100        nodes.push(node);
101    }
102
103    var dataGrid = new WebInspector.SortableDataGrid(columns);
104    var length = nodes.length;
105    var rootNode = dataGrid.rootNode();
106    for (var i = 0; i < length; ++i)
107        rootNode.appendChild(nodes[i]);
108
109    dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, sortDataGrid);
110
111    function sortDataGrid()
112    {
113        var nodes = dataGrid.rootNode().children;
114        var sortColumnIdentifier = dataGrid.sortColumnIdentifier();
115        if (!sortColumnIdentifier)
116            return;
117
118        var columnIsNumeric = true;
119        for (var i = 0; i < nodes.length; i++) {
120            var value = nodes[i].data[sortColumnIdentifier];
121            if (isNaN(value instanceof Node ? value.textContent : value)) {
122                columnIsNumeric = false;
123                break;
124            }
125        }
126
127        var comparator = columnIsNumeric ? WebInspector.SortableDataGrid.NumericComparator : WebInspector.SortableDataGrid.StringComparator;
128        dataGrid.sortNodes(comparator.bind(null, sortColumnIdentifier), !dataGrid.isSortOrderAscending());
129    }
130    return dataGrid;
131}
132
133WebInspector.SortableDataGrid.prototype = {
134    /**
135     * @param {!WebInspector.DataGridNode} node
136     */
137    insertChild: function(node)
138    {
139        var parentNode = this.rootNode();
140        parentNode.insertChild(node, parentNode.children.upperBound(node, this._sortingFunction));
141    },
142
143    /**
144     * @param {!WebInspector.SortableDataGrid.NodeComparator} comparator
145     * @param {boolean} reverseMode
146     */
147    sortNodes: function(comparator, reverseMode)
148    {
149        this._sortingFunction = WebInspector.SortableDataGrid.Comparator.bind(null, comparator, reverseMode);
150        var children = this._rootNode.children;
151        children.sort(this._sortingFunction);
152        for (var i = 0; i < children.length; ++i)
153            children[i].recalculateSiblings(i);
154        this.scheduleUpdate();
155    },
156
157    __proto__: WebInspector.ViewportDataGrid.prototype
158}
159
160/**
161 * @constructor
162 * @extends {WebInspector.ViewportDataGridNode}
163 * @param {?Object.<string, *>=} data
164 */
165WebInspector.SortableDataGridNode = function(data)
166{
167    WebInspector.ViewportDataGridNode.call(this, data);
168}
169
170WebInspector.SortableDataGridNode.prototype = {
171    __proto__: WebInspector.ViewportDataGridNode.prototype
172}
173