1/* 2 * Copyright (C) 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 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26WebInspector.DatabaseQueryView = function(database) 27{ 28 WebInspector.View.call(this); 29 30 this.database = database; 31 32 this.element.addStyleClass("storage-view"); 33 this.element.addStyleClass("query"); 34 this.element.tabIndex = 0; 35 36 this.element.addEventListener("selectstart", this._selectStart.bind(this), false); 37 38 this.promptElement = document.createElement("div"); 39 this.promptElement.className = "database-query-prompt"; 40 this.promptElement.appendChild(document.createElement("br")); 41 this.promptElement.handleKeyEvent = this._promptKeyDown.bind(this); 42 this.element.appendChild(this.promptElement); 43 44 this.prompt = new WebInspector.TextPrompt(this.promptElement, this.completions.bind(this), " "); 45} 46 47WebInspector.DatabaseQueryView.prototype = { 48 show: function(parentElement) 49 { 50 WebInspector.View.prototype.show.call(this, parentElement); 51 52 function moveBackIfOutside() 53 { 54 if (!this.prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed) 55 this.prompt.moveCaretToEndOfPrompt(); 56 } 57 58 setTimeout(moveBackIfOutside.bind(this), 0); 59 }, 60 61 completions: function(wordRange, bestMatchOnly, completionsReadyCallback) 62 { 63 var prefix = wordRange.toString().toLowerCase(); 64 if (!prefix.length) 65 return; 66 67 var results = []; 68 69 function accumulateMatches(textArray) 70 { 71 if (bestMatchOnly && results.length) 72 return; 73 for (var i = 0; i < textArray.length; ++i) { 74 var text = textArray[i].toLowerCase(); 75 if (text.length < prefix.length) 76 continue; 77 if (text.indexOf(prefix) !== 0) 78 continue; 79 results.push(textArray[i]); 80 if (bestMatchOnly) 81 return; 82 } 83 } 84 85 accumulateMatches(this.database.tableNames.map(function(name) { return name + " " })); 86 accumulateMatches(["SELECT ", "FROM ", "WHERE ", "LIMIT ", "DELETE FROM ", "CREATE ", "DROP ", "TABLE ", "INDEX ", "UPDATE ", "INSERT INTO ", "VALUES ("]); 87 88 completionsReadyCallback(results); 89 }, 90 91 _promptKeyDown: function(event) 92 { 93 switch (event.keyIdentifier) { 94 case "Enter": 95 this._enterKeyPressed(event); 96 return; 97 } 98 99 this.prompt.handleKeyEvent(event); 100 }, 101 102 _selectStart: function(event) 103 { 104 if (this._selectionTimeout) 105 clearTimeout(this._selectionTimeout); 106 107 this.prompt.clearAutoComplete(); 108 109 function moveBackIfOutside() 110 { 111 delete this._selectionTimeout; 112 if (!this.prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed) 113 this.prompt.moveCaretToEndOfPrompt(); 114 this.prompt.autoCompleteSoon(); 115 } 116 117 this._selectionTimeout = setTimeout(moveBackIfOutside.bind(this), 100); 118 }, 119 120 _enterKeyPressed: function(event) 121 { 122 event.preventDefault(); 123 event.stopPropagation(); 124 125 this.prompt.clearAutoComplete(true); 126 127 var query = this.prompt.text; 128 if (!query.length) 129 return; 130 131 this.prompt.history.push(query); 132 this.prompt.historyOffset = 0; 133 this.prompt.text = ""; 134 135 function queryTransaction(tx) 136 { 137 tx.executeSql(query, null, InspectorController.wrapCallback(this._queryFinished.bind(this, query)), InspectorController.wrapCallback(this._executeSqlError.bind(this, query))); 138 } 139 140 this.database.database.transaction(InspectorController.wrapCallback(queryTransaction.bind(this)), InspectorController.wrapCallback(this._queryError.bind(this, query))); 141 }, 142 143 _queryFinished: function(query, tx, result) 144 { 145 var dataGrid = WebInspector.panels.databases.dataGridForResult(result); 146 dataGrid.element.addStyleClass("inline"); 147 this._appendQueryResult(query, dataGrid.element); 148 149 if (query.match(/^create /i) || query.match(/^drop table /i)) 150 WebInspector.panels.databases.updateDatabaseTables(this.database); 151 }, 152 153 _queryError: function(query, error) 154 { 155 if (error.code == 1) 156 var message = error.message; 157 else if (error.code == 2) 158 var message = WebInspector.UIString("Database no longer has expected version."); 159 else 160 var message = WebInspector.UIString("An unexpected error %s occurred.", error.code); 161 162 this._appendQueryResult(query, message, "error"); 163 }, 164 165 _executeSqlError: function(query, tx, error) 166 { 167 this._queryError(query, error); 168 }, 169 170 _appendQueryResult: function(query, result, resultClassName) 171 { 172 var element = document.createElement("div"); 173 element.className = "database-user-query"; 174 175 var commandTextElement = document.createElement("span"); 176 commandTextElement.className = "database-query-text"; 177 commandTextElement.textContent = query; 178 element.appendChild(commandTextElement); 179 180 var resultElement = document.createElement("div"); 181 resultElement.className = "database-query-result"; 182 183 if (resultClassName) 184 resultElement.addStyleClass(resultClassName); 185 186 if (typeof result === "string" || result instanceof String) 187 resultElement.textContent = result; 188 else if (result && result.nodeName) 189 resultElement.appendChild(result); 190 191 if (resultElement.childNodes.length) 192 element.appendChild(resultElement); 193 194 this.element.insertBefore(element, this.promptElement); 195 this.promptElement.scrollIntoView(false); 196 } 197} 198 199WebInspector.DatabaseQueryView.prototype.__proto__ = WebInspector.View.prototype; 200