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 29WebInspector.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 56WebInspector.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 // FIXME: (Bug 19439) We should specially format SQL NULL here 189 // (which is represented by JavaScript null here, and turned 190 // into the string "null" by the String() function). 191 var text = String(row[columnIdentifier]); 192 data[columnIdentifier] = text; 193 if (text.length > columns[columnIdentifier].width) 194 columns[columnIdentifier].width = text.length; 195 } 196 197 var node = new WebInspector.DataGridNode(data, false); 198 node.selectable = false; 199 nodes.push(node); 200 } 201 202 var totalColumnWidths = 0; 203 for (var columnIdentifier in columns) 204 totalColumnWidths += columns[columnIdentifier].width; 205 206 // Calculate the percentage width for the columns. 207 const minimumPrecent = 5; 208 var recoupPercent = 0; 209 for (var columnIdentifier in columns) { 210 var width = columns[columnIdentifier].width; 211 width = Math.round((width / totalColumnWidths) * 100); 212 if (width < minimumPrecent) { 213 recoupPercent += (minimumPrecent - width); 214 width = minimumPrecent; 215 } 216 217 columns[columnIdentifier].width = width; 218 } 219 220 // Enforce the minimum percentage width. 221 while (recoupPercent > 0) { 222 for (var columnIdentifier in columns) { 223 if (columns[columnIdentifier].width > minimumPrecent) { 224 --columns[columnIdentifier].width; 225 --recoupPercent; 226 if (!recoupPercent) 227 break; 228 } 229 } 230 } 231 232 // Change the width property to a string suitable for a style width. 233 for (var columnIdentifier in columns) 234 columns[columnIdentifier].width += "%"; 235 236 var dataGrid = new WebInspector.DataGrid(columns); 237 var length = nodes.length; 238 for (var i = 0; i < length; ++i) 239 dataGrid.appendChild(nodes[i]); 240 241 return dataGrid; 242 }, 243 244 _startSidebarDragging: function(event) 245 { 246 WebInspector.elementDragStart(this.sidebarResizeElement, this._sidebarDragging.bind(this), this._endSidebarDragging.bind(this), event, "col-resize"); 247 }, 248 249 _sidebarDragging: function(event) 250 { 251 this._updateSidebarWidth(event.pageX); 252 253 event.preventDefault(); 254 }, 255 256 _endSidebarDragging: function(event) 257 { 258 WebInspector.elementDragEnd(event); 259 }, 260 261 _updateSidebarWidth: function(width) 262 { 263 if (this.sidebarElement.offsetWidth <= 0) { 264 // The stylesheet hasn't loaded yet or the window is closed, 265 // so we can't calculate what is need. Return early. 266 return; 267 } 268 269 if (!("_currentSidebarWidth" in this)) 270 this._currentSidebarWidth = this.sidebarElement.offsetWidth; 271 272 if (typeof width === "undefined") 273 width = this._currentSidebarWidth; 274 275 width = Number.constrain(width, Preferences.minSidebarWidth, window.innerWidth / 2); 276 277 this._currentSidebarWidth = width; 278 279 this.sidebarElement.style.width = width + "px"; 280 this.databaseViews.style.left = width + "px"; 281 this.sidebarResizeElement.style.left = (width - 3) + "px"; 282 } 283} 284 285WebInspector.DatabasesPanel.prototype.__proto__ = WebInspector.Panel.prototype; 286 287WebInspector.DatabaseSidebarTreeElement = function(database) 288{ 289 this.database = database; 290 291 WebInspector.SidebarTreeElement.call(this, "database-sidebar-tree-item", "", "", database, true); 292 293 this.refreshTitles(); 294} 295 296WebInspector.DatabaseSidebarTreeElement.prototype = { 297 onselect: function() 298 { 299 WebInspector.panels.databases.showDatabase(this.database); 300 }, 301 302 oncollapse: function() 303 { 304 // Request a refresh after every collapse so the next 305 // expand will have an updated table list. 306 this.shouldRefreshChildren = true; 307 }, 308 309 onpopulate: function() 310 { 311 this.removeChildren(); 312 313 var tableNames = this.database.tableNames; 314 var tableNamesLength = tableNames.length; 315 for (var i = 0; i < tableNamesLength; ++i) 316 this.appendChild(new WebInspector.SidebarDatabaseTableTreeElement(this.database, tableNames[i])); 317 }, 318 319 get mainTitle() 320 { 321 return this.database.name; 322 }, 323 324 set mainTitle(x) 325 { 326 // Do nothing. 327 }, 328 329 get subtitle() 330 { 331 return this.database.displayDomain; 332 }, 333 334 set subtitle(x) 335 { 336 // Do nothing. 337 } 338} 339 340WebInspector.DatabaseSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype; 341 342WebInspector.SidebarDatabaseTableTreeElement = function(database, tableName) 343{ 344 this.database = database; 345 this.tableName = tableName; 346 347 WebInspector.SidebarTreeElement.call(this, "database-table-sidebar-tree-item small", tableName, "", null, false); 348} 349 350WebInspector.SidebarDatabaseTableTreeElement.prototype = { 351 onselect: function() 352 { 353 WebInspector.panels.databases.showDatabase(this.database, this.tableName); 354 } 355} 356 357WebInspector.SidebarDatabaseTableTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype; 358