74c9e800e5e21d4138bd1bbd6fa5c93d4ee194fc
[WebKit-https.git] / WebCore / page / inspector / DatabasesPanel.js
1 /*
2  * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 WebInspector.DatabasesPanel = function(database)
30 {
31     WebInspector.Panel.call(this);
32
33     this.sidebarElement = document.createElement("div");
34     this.sidebarElement.id = "databases-sidebar";
35     this.sidebarElement.className = "sidebar";
36     this.element.appendChild(this.sidebarElement);
37
38     this.sidebarResizeElement = document.createElement("div");
39     this.sidebarResizeElement.className = "sidebar-resizer-vertical";
40     this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarDragging.bind(this), false);
41     this.element.appendChild(this.sidebarResizeElement);
42
43     this.sidebarTreeElement = document.createElement("ol");
44     this.sidebarTreeElement.className = "sidebar-tree";
45     this.sidebarElement.appendChild(this.sidebarTreeElement);
46
47     this.sidebarTree = new TreeOutline(this.sidebarTreeElement);
48
49     this.databaseViews = document.createElement("div");
50     this.databaseViews.id = "database-views";
51     this.element.appendChild(this.databaseViews);
52
53     this.reset();
54 }
55
56 WebInspector.DatabasesPanel.prototype = {
57     toolbarItemClass: "databases",
58
59     get toolbarItemLabel()
60     {
61         return WebInspector.UIString("Databases");
62     },
63
64     show: function()
65     {
66         WebInspector.Panel.prototype.show.call(this);
67         this._updateSidebarWidth();
68     },
69
70     reset: function()
71     {
72         if (this._databases) {
73             var databasesLength = this._databases.length;
74             for (var i = 0; i < databasesLength; ++i) {
75                 var database = this._databases[i];
76
77                 delete database._tableViews;
78                 delete database._queryView;
79             }
80         }
81
82         this._databases = [];
83
84         this.sidebarTree.removeChildren();
85         this.databaseViews.removeChildren();
86     },
87
88     handleKeyEvent: function(event)
89     {
90         this.sidebarTree.handleKeyEvent(event);
91     },
92
93     addDatabase: function(database)
94     {
95         this._databases.push(database);
96
97         var databaseTreeElement = new WebInspector.DatabaseSidebarTreeElement(database);
98         database._databasesTreeElement = databaseTreeElement;
99
100         this.sidebarTree.appendChild(databaseTreeElement);
101     },
102
103     showDatabase: function(database, tableName)
104     {
105         if (!database)
106             return;
107
108         if (this.visibleDatabaseView)
109             this.visibleDatabaseView.hide();
110
111         var view;
112         if (tableName) {
113             if (!("_tableViews" in database))
114                 database._tableViews = {};
115             view = database._tableViews[tableName];
116             if (!view) {
117                 view = new WebInspector.DatabaseTableView(database, tableName);
118                 database._tableViews[tableName] = view;
119             }
120         } else {
121             view = database._queryView;
122             if (!view) {
123                 view = new WebInspector.DatabaseQueryView(database);
124                 database._queryView = view;
125             }
126         }
127
128         view.show(this.databaseViews);
129
130         this.visibleDatabaseView = view;
131     },
132
133     closeVisibleView: function()
134     {
135         if (this.visibleDatabaseView)
136             this.visibleDatabaseView.hide();
137         delete this.visibleDatabaseView;
138     },
139
140     updateDatabaseTables: function(database)
141     {
142         if (!database || !database._databasesTreeElement)
143             return;
144
145         database._databasesTreeElement.shouldRefreshChildren = true;
146
147         if (!("_tableViews" in database))
148             return;
149
150         var tableNamesHash = {};
151         var tableNames = database.tableNames;
152         var tableNamesLength = tableNames.length;
153         for (var i = 0; i < tableNamesLength; ++i)
154             tableNamesHash[tableNames[i]] = true;
155
156         for (var tableName in database._tableViews) {
157             if (!(tableName in tableNamesHash)) {
158                 if (this.visibleDatabaseView === database._tableViews[tableName])
159                     this.closeVisibleView();
160                 delete database._tableViews[tableName];
161             }
162         }
163     },
164
165     dataGridForResult: function(result)
166     {
167         if (!result.rows.length)
168             return null;
169
170         var columns = {};
171
172         var rows = result.rows;
173         for (var columnIdentifier in rows.item(0)) {
174             var column = {};
175             column.width = columnIdentifier.length;
176             column.title = columnIdentifier;
177
178             columns[columnIdentifier] = column;
179         }
180
181         var nodes = [];
182         var length = rows.length;
183         for (var i = 0; i < length; ++i) {
184             var data = {};
185
186             var row = rows.item(i);
187             for (var columnIdentifier in row) {
188                 var text = row[columnIdentifier];
189                 data[columnIdentifier] = text;
190                 if (text.length > columns[columnIdentifier].width)
191                     columns[columnIdentifier].width = text.length;
192             }
193
194             var node = new WebInspector.DataGridNode(data, false);
195             node.selectable = false;
196             nodes.push(node);
197         }
198
199         var totalColumnWidths = 0;
200         for (var columnIdentifier in columns)
201             totalColumnWidths += columns[columnIdentifier].width;
202
203         // Calculate the percentage width for the columns.
204         const minimumPrecent = 5;
205         var recoupPercent = 0;
206         for (var columnIdentifier in columns) {
207             var width = columns[columnIdentifier].width;
208             width = Math.round((width / totalColumnWidths) * 100);
209             if (width < minimumPrecent) {
210                 recoupPercent += (minimumPrecent - width);
211                 width = minimumPrecent;
212             }
213
214             columns[columnIdentifier].width = width;
215         }
216
217         // Enforce the minimum percentage width.
218         while (recoupPercent > 0) {
219             for (var columnIdentifier in columns) {
220                 if (columns[columnIdentifier].width > minimumPrecent) {
221                     --columns[columnIdentifier].width;
222                     --recoupPercent;
223                     if (!recoupPercent)
224                         break;
225                 }
226             }
227         }
228
229         // Change the width property to a string suitable for a style width.
230         for (var columnIdentifier in columns)
231             columns[columnIdentifier].width += "%";
232
233         var dataGrid = new WebInspector.DataGrid(columns);
234         var length = nodes.length;
235         for (var i = 0; i < length; ++i)
236             dataGrid.appendChild(nodes[i]);
237
238         return dataGrid;
239     },
240
241     _startSidebarDragging: function(event)
242     {
243         WebInspector.elementDragStart(this.sidebarResizeElement, this._sidebarDragging.bind(this), this._endSidebarDragging.bind(this), event, "col-resize");
244     },
245
246     _sidebarDragging: function(event)
247     {
248         this._updateSidebarWidth(event.pageX);
249
250         event.preventDefault();
251     },
252
253     _endSidebarDragging: function(event)
254     {
255         WebInspector.elementDragEnd(event);
256     },
257
258     _updateSidebarWidth: function(width)
259     {
260         if (this.sidebarElement.offsetWidth <= 0) {
261             // The stylesheet hasn't loaded yet, so we need to update later.
262             setTimeout(this._updateSidebarWidth.bind(this), 0, width);
263             return;
264         }
265
266         if (!("_currentSidebarWidth" in this))
267             this._currentSidebarWidth = this.sidebarElement.offsetWidth;
268
269         if (typeof width === "undefined")
270             width = this._currentSidebarWidth;
271
272         width = Number.constrain(width, Preferences.minSidebarWidth, window.innerWidth / 2);
273
274         this._currentSidebarWidth = width;
275
276         this.sidebarElement.style.width = width + "px";
277         this.databaseViews.style.left = width + "px";
278         this.sidebarResizeElement.style.left = (width - 3) + "px";
279     }
280 }
281
282 WebInspector.DatabasesPanel.prototype.__proto__ = WebInspector.Panel.prototype;
283
284 WebInspector.DatabaseSidebarTreeElement = function(database)
285 {
286     this.database = database;
287
288     WebInspector.SidebarTreeElement.call(this, "database-sidebar-tree-item", "", "", database, true);
289
290     this.refreshTitles();
291 }
292
293 WebInspector.DatabaseSidebarTreeElement.prototype = {
294     onselect: function()
295     {
296         WebInspector.panels.databases.showDatabase(this.database);
297     },
298
299     oncollapse: function()
300     {
301         // Request a refresh after every collapse so the next
302         // expand will have an updated table list.
303         this.shouldRefreshChildren = true;
304     },
305
306     onpopulate: function()
307     {
308         this.removeChildren();
309
310         var tableNames = this.database.tableNames;
311         var tableNamesLength = tableNames.length;
312         for (var i = 0; i < tableNamesLength; ++i)
313             this.appendChild(new WebInspector.SidebarDatabaseTableTreeElement(this.database, tableNames[i]));
314     },
315
316     get mainTitle()
317     {
318         return this.database.name;
319     },
320
321     set mainTitle(x)
322     {
323         // Do nothing.
324     },
325
326     get subtitle()
327     {
328         return this.database.displayDomain;
329     },
330
331     set subtitle(x)
332     {
333         // Do nothing.
334     }
335 }
336
337 WebInspector.DatabaseSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype;
338
339 WebInspector.SidebarDatabaseTableTreeElement = function(database, tableName)
340 {
341     this.database = database;
342     this.tableName = tableName;
343
344     WebInspector.SidebarTreeElement.call(this, "database-table-sidebar-tree-item small", tableName, "", null, false);
345 }
346
347 WebInspector.SidebarDatabaseTableTreeElement.prototype = {
348     onselect: function()
349     {
350         WebInspector.panels.databases.showDatabase(this.database, this.tableName);
351     }
352 }
353
354 WebInspector.SidebarDatabaseTableTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype;