summaryrefslogtreecommitdiffstats
path: root/WebCore/page/inspector/DatabasePanel.js
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/page/inspector/DatabasePanel.js')
-rw-r--r--WebCore/page/inspector/DatabasePanel.js462
1 files changed, 462 insertions, 0 deletions
diff --git a/WebCore/page/inspector/DatabasePanel.js b/WebCore/page/inspector/DatabasePanel.js
new file mode 100644
index 0000000..9101b9c
--- /dev/null
+++ b/WebCore/page/inspector/DatabasePanel.js
@@ -0,0 +1,462 @@
+/*
+ * 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.
+ */
+
+WebInspector.DatabasePanel = function(database, views)
+{
+ var allViews = [{ title: WebInspector.UIString("Query"), name: "query" }, { title: WebInspector.UIString("Browse"), name: "browse" }];
+ if (views)
+ allViews = allViews.concat(views);
+
+ WebInspector.ResourcePanel.call(this, database, allViews);
+
+ this.currentView = this.views.browse;
+
+ this.queryPromptElement = document.createElement("textarea");
+ this.queryPromptElement.className = "database-prompt";
+ this.element.appendChild(this.queryPromptElement);
+
+ this.queryPromptElement.addEventListener("keydown", this.queryInputKeyDown.bind(this), false);
+
+ this.queryPromptHistory = [];
+ this.queryPromptHistoryOffset = 0;
+
+ var queryView = this.views.query;
+
+ queryView.commandListElement = document.createElement("ol");
+ queryView.commandListElement.className = "database-command-list";
+ queryView.contentElement.appendChild(queryView.commandListElement);
+
+ var panel = this;
+ queryView.show = function()
+ {
+ panel.queryPromptElement.focus();
+ this.commandListElement.scrollTop = this.previousScrollTop;
+ };
+
+ queryView.hide = function()
+ {
+ this.previousScrollTop = this.commandListElement.scrollTop;
+ };
+
+ var browseView = this.views.browse;
+
+ browseView.reloadTableElement = document.createElement("button");
+ browseView.reloadTableElement.appendChild(document.createElement("img"));
+ browseView.reloadTableElement.className = "database-table-reload";
+ browseView.reloadTableElement.title = WebInspector.UIString("Reload");
+ browseView.reloadTableElement.addEventListener("click", this.reloadClicked.bind(this), false);
+
+ browseView.show = function()
+ {
+ panel.updateTableList();
+ panel.queryPromptElement.focus();
+
+ this.tableSelectElement.removeStyleClass("hidden");
+ if (!this.tableSelectElement.parentNode)
+ document.getElementById("toolbarButtons").appendChild(this.tableSelectElement);
+
+ this.reloadTableElement.removeStyleClass("hidden");
+ if (!this.reloadTableElement.parentNode)
+ document.getElementById("toolbarButtons").appendChild(this.reloadTableElement);
+
+ this.contentElement.scrollTop = this.previousScrollTop;
+ };
+
+ browseView.hide = function()
+ {
+ this.tableSelectElement.addStyleClass("hidden");
+ this.reloadTableElement.addStyleClass("hidden");
+ this.previousScrollTop = this.contentElement.scrollTop;
+ };
+}
+
+// FIXME: The function and local variables are a workaround for http://bugs.webkit.org/show_bug.cgi?id=15574.
+WebInspector.DatabasePanel.prototype = (function() {
+var document = window.document;
+var Math = window.Math;
+return {
+ show: function()
+ {
+ WebInspector.ResourcePanel.prototype.show.call(this);
+ this.queryPromptElement.focus();
+ },
+
+ get currentTable()
+ {
+ return this._currentTable;
+ },
+
+ set currentTable(x)
+ {
+ if (this._currentTable === x)
+ return;
+
+ this._currentTable = x;
+
+ if (x) {
+ var browseView = this.views.browse;
+ if (browseView.tableSelectElement) {
+ var length = browseView.tableSelectElement.options.length;
+ for (var i = 0; i < length; ++i) {
+ var option = browseView.tableSelectElement.options[i];
+ if (option.value === x) {
+ browseView.tableSelectElement.selectedIndex = i;
+ break;
+ }
+ }
+ }
+
+ this.updateTableBrowser();
+ }
+ },
+
+ reloadClicked: function()
+ {
+ this.updateTableList();
+ this.updateTableBrowser();
+ },
+
+ updateTableList: function()
+ {
+ var browseView = this.views.browse;
+ if (!browseView.tableSelectElement) {
+ browseView.tableSelectElement = document.createElement("select");
+ browseView.tableSelectElement.className = "database-table-select hidden";
+
+ var panel = this;
+ var changeTableFunction = function()
+ {
+ var index = browseView.tableSelectElement.selectedIndex;
+ if (index != -1)
+ panel.currentTable = browseView.tableSelectElement.options[index].value;
+ else
+ panel.currentTable = null;
+ };
+
+ browseView.tableSelectElement.addEventListener("change", changeTableFunction, false);
+ }
+
+ browseView.tableSelectElement.removeChildren();
+
+ var selectedTableName = this.currentTable;
+ var tableNames = InspectorController.databaseTableNames(this.resource.database).sort();
+
+ var length = tableNames.length;
+ for (var i = 0; i < length; ++i) {
+ var option = document.createElement("option");
+ option.value = tableNames[i];
+ option.text = tableNames[i];
+ browseView.tableSelectElement.appendChild(option);
+
+ if (tableNames[i] === selectedTableName)
+ browseView.tableSelectElement.selectedIndex = i;
+ }
+
+ if (!selectedTableName && length)
+ this.currentTable = tableNames[0];
+ },
+
+ updateTableBrowser: function()
+ {
+ if (!this.currentTable) {
+ this.views.browse.contentElement.removeChildren();
+ return;
+ }
+
+ var panel = this;
+ var query = "SELECT * FROM " + this.currentTable;
+ this.resource.database.transaction(function(tx)
+ {
+ tx.executeSql(query, [], function(tx, result) { panel.browseQueryFinished(result) }, function(tx, error) { panel.browseQueryError(error) });
+ }, function(tx, error) { panel.browseQueryError(error) });
+ },
+
+ browseQueryFinished: function(result)
+ {
+ this.views.browse.contentElement.removeChildren();
+
+ var table = this._tableForResult(result);
+ if (!table) {
+ var emptyMsgElement = document.createElement("div");
+ emptyMsgElement.className = "database-table-empty";
+ emptyMsgElement.textContent = WebInspector.UIString("The ā€œ%sā€\ntable is empty.", this.currentTable);
+ this.views.browse.contentElement.appendChild(emptyMsgElement);
+ return;
+ }
+
+ var rowCount = table.getElementsByTagName("tr").length;
+ var columnCount = table.getElementsByTagName("tr").item(0).getElementsByTagName("th").length;
+
+ var tr = document.createElement("tr");
+ tr.className = "database-result-filler-row";
+ table.appendChild(tr);
+
+ if (!(rowCount % 2))
+ tr.addStyleClass("alternate");
+
+ for (var i = 0; i < columnCount; ++i) {
+ var td = document.createElement("td");
+ tr.appendChild(td);
+ }
+
+ table.addStyleClass("database-browse-table");
+ this.views.browse.contentElement.appendChild(table);
+ },
+
+ browseQueryError: function(error)
+ {
+ this.views.browse.contentElement.removeChildren();
+
+ var errorMsgElement = document.createElement("div");
+ errorMsgElement.className = "database-table-error";
+ errorMsgElement.textContent = WebInspector.UIString("An error occurred trying to\nread the ā€œ%sā€ table.", this.currentTable);
+ this.views.browse.contentElement.appendChild(errorMsgElement);
+ },
+
+ queryInputKeyDown: function(event)
+ {
+ switch (event.keyIdentifier) {
+ case "Enter":
+ this._onQueryInputEnterPressed(event);
+ break;
+ case "Up":
+ this._onQueryInputUpPressed(event);
+ break;
+ case "Down":
+ this._onQueryInputDownPressed(event);
+ break;
+ }
+ },
+
+ appendQueryResult: function(query, result, resultClassName)
+ {
+ var commandItem = document.createElement("li");
+ commandItem.className = "database-command";
+
+ var queryDiv = document.createElement("div");
+ queryDiv.className = "database-command-query";
+ queryDiv.textContent = query;
+ commandItem.appendChild(queryDiv);
+
+ var resultDiv = document.createElement("div");
+ resultDiv.className = "database-command-result";
+ commandItem.appendChild(resultDiv);
+
+ if (resultClassName)
+ resultDiv.addStyleClass(resultClassName);
+
+ if (typeof result === "string" || result instanceof String)
+ resultDiv.textContent = result;
+ else if (result && result.nodeName)
+ resultDiv.appendChild(result);
+
+ this.views.query.commandListElement.appendChild(commandItem);
+ commandItem.scrollIntoView(false);
+ },
+
+ queryFinished: function(query, result)
+ {
+ this.appendQueryResult(query, this._tableForResult(result));
+ },
+
+ queryError: function(query, error)
+ {
+ if (this.currentView !== this.views.query)
+ this.currentView = this.views.query;
+
+ if (error.code == 1)
+ var message = error.message;
+ else if (error.code == 2)
+ var message = WebInspector.UIString("Database no longer has expected version.");
+ else
+ var message = WebInspector.UIString("An unexpected error %s occured.", error.code);
+
+ this.appendQueryResult(query, message, "error");
+ },
+
+ _onQueryInputEnterPressed: function(event)
+ {
+ event.preventDefault();
+ event.stopPropagation();
+
+ var query = this.queryPromptElement.value;
+ if (!query.length)
+ return;
+
+ var panel = this;
+ this.resource.database.transaction(function(tx)
+ {
+ tx.executeSql(query, [], function(tx, result) { panel.queryFinished(query, result) }, function(tx, error) { panel.queryError(query, error) });
+ }, function(tx, error) { panel.queryError(query, error) });
+
+ this.queryPromptHistory.push(query);
+ this.queryPromptHistoryOffset = 0;
+
+ this.queryPromptElement.value = "";
+
+ if (query.match(/^select /i)) {
+ if (this.currentView !== this.views.query)
+ this.currentView = this.views.query;
+ } else {
+ if (query.match(/^create /i) || query.match(/^drop table /i))
+ this.updateTableList();
+
+ // FIXME: we should only call updateTableBrowser() is we know the current table was modified
+ this.updateTableBrowser();
+ }
+ },
+
+ _onQueryInputUpPressed: function(event)
+ {
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (this.queryPromptHistoryOffset == this.queryPromptHistory.length)
+ return;
+
+ if (this.queryPromptHistoryOffset == 0)
+ this.tempSavedQuery = this.queryPromptElement.value;
+
+ ++this.queryPromptHistoryOffset;
+ this.queryPromptElement.value = this.queryPromptHistory[this.queryPromptHistory.length - this.queryPromptHistoryOffset];
+ this.queryPromptElement.moveCursorToEnd();
+ },
+
+ _onQueryInputDownPressed: function(event)
+ {
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (this.queryPromptHistoryOffset == 0)
+ return;
+
+ --this.queryPromptHistoryOffset;
+
+ if (this.queryPromptHistoryOffset == 0) {
+ this.queryPromptElement.value = this.tempSavedQuery;
+ this.queryPromptElement.moveCursorToEnd();
+ delete this.tempSavedQuery;
+ return;
+ }
+
+ this.queryPromptElement.value = this.queryPromptHistory[this.queryPromptHistory.length - this.queryPromptHistoryOffset];
+ this.queryPromptElement.moveCursorToEnd();
+ },
+
+ _tableForResult: function(result)
+ {
+ if (!result.rows.length)
+ return null;
+
+ var rows = result.rows;
+ var length = rows.length;
+ var columnWidths = [];
+
+ var table = document.createElement("table");
+ table.className = "database-result-table";
+
+ var headerRow = document.createElement("tr");
+ table.appendChild(headerRow);
+
+ var j = 0;
+ for (var column in rows.item(0)) {
+ var th = document.createElement("th");
+ headerRow.appendChild(th);
+
+ var div = document.createElement("div");
+ div.textContent = column;
+ div.title = column;
+ th.appendChild(div);
+
+ columnWidths[j++] = column.length;
+ }
+
+ for (var i = 0; i < length; ++i) {
+ var row = rows.item(i);
+ var tr = document.createElement("tr");
+ if (i % 2)
+ tr.className = "alternate";
+ table.appendChild(tr);
+
+ var j = 0;
+ for (var column in row) {
+ var td = document.createElement("td");
+ tr.appendChild(td);
+
+ var text = row[column];
+ var div = document.createElement("div");
+ div.textContent = text;
+ div.title = text;
+ td.appendChild(div);
+
+ if (text.length > columnWidths[j])
+ columnWidths[j] = text.length;
+ ++j;
+ }
+ }
+
+ var totalColumnWidths = 0;
+ length = columnWidths.length;
+ for (var i = 0; i < length; ++i)
+ totalColumnWidths += columnWidths[i];
+
+ // Calculate the percentage width for the columns.
+ var minimumPrecent = 5;
+ var recoupPercent = 0;
+ for (var i = 0; i < length; ++i) {
+ columnWidths[i] = Math.round((columnWidths[i] / totalColumnWidths) * 100);
+ if (columnWidths[i] < minimumPrecent) {
+ recoupPercent += (minimumPrecent - columnWidths[i]);
+ columnWidths[i] = minimumPrecent;
+ }
+ }
+
+ // Enforce the minimum percentage width.
+ while (recoupPercent > 0) {
+ for (var i = 0; i < length; ++i) {
+ if (columnWidths[i] > minimumPrecent) {
+ --columnWidths[i];
+ --recoupPercent;
+ if (!recoupPercent)
+ break;
+ }
+ }
+ }
+
+ length = headerRow.childNodes.length;
+ for (var i = 0; i < length; ++i) {
+ var th = headerRow.childNodes[i];
+ th.style.width = columnWidths[i] + "%";
+ }
+
+ return table;
+ }
+}
+})();
+
+WebInspector.DatabasePanel.prototype.__proto__ = WebInspector.ResourcePanel.prototype;