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 26 WebInspector.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.addStyleClass("monospace"); 35 this.element.tabIndex = 0; 36 37 this.element.addEventListener("selectstart", this._selectStart.bind(this), false); 38 39 this.promptElement = document.createElement("div"); 40 this.promptElement.className = "database-query-prompt"; 41 this.promptElement.appendChild(document.createElement("br")); 42 this.promptElement.addEventListener("keydown", this._promptKeyDown.bind(this), true); 43 this.element.appendChild(this.promptElement); 44 45 this.prompt = new WebInspector.TextPrompt(this.promptElement, this.completions.bind(this), " "); 46 } 47 48 WebInspector.DatabaseQueryView.prototype = { 49 show: function(parentElement) 50 { 51 WebInspector.View.prototype.show.call(this, parentElement); 52 53 function moveBackIfOutside() 54 { 55 if (!this.prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed) 56 this.prompt.moveCaretToEndOfPrompt(); 57 } 58 59 setTimeout(moveBackIfOutside.bind(this), 0); 60 }, 61 62 completions: function(wordRange, bestMatchOnly, completionsReadyCallback) 63 { 64 var prefix = wordRange.toString().toLowerCase(); 65 if (!prefix.length) 66 return; 67 68 var results = []; 69 70 function accumulateMatches(textArray) 71 { 72 if (bestMatchOnly && results.length) 73 return; 74 for (var i = 0; i < textArray.length; ++i) { 75 var text = textArray[i].toLowerCase(); 76 if (text.length < prefix.length) 77 continue; 78 if (text.indexOf(prefix) !== 0) 79 continue; 80 results.push(textArray[i]); 81 if (bestMatchOnly) 82 return; 83 } 84 } 85 86 function tableNamesCallback(tableNames) 87 { 88 accumulateMatches(tableNames.map(function(name) { return name + " " })); 89 accumulateMatches(["SELECT ", "FROM ", "WHERE ", "LIMIT ", "DELETE FROM ", "CREATE ", "DROP ", "TABLE ", "INDEX ", "UPDATE ", "INSERT INTO ", "VALUES ("]); 90 91 completionsReadyCallback(results); 92 } 93 this.database.getTableNames(tableNamesCallback); 94 }, 95 96 _promptKeyDown: function(event) 97 { 98 if (isEnterKey(event)) { 99 this._enterKeyPressed(event); 100 return; 101 } 102 }, 103 104 _selectStart: function(event) 105 { 106 if (this._selectionTimeout) 107 clearTimeout(this._selectionTimeout); 108 109 this.prompt.clearAutoComplete(); 110 111 function moveBackIfOutside() 112 { 113 delete this._selectionTimeout; 114 if (!this.prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed) 115 this.prompt.moveCaretToEndOfPrompt(); 116 this.prompt.autoCompleteSoon(); 117 } 118 119 this._selectionTimeout = setTimeout(moveBackIfOutside.bind(this), 100); 120 }, 121 122 _enterKeyPressed: function(event) 123 { 124 event.preventDefault(); 125 event.stopPropagation(); 126 127 this.prompt.clearAutoComplete(true); 128 129 var query = this.prompt.text; 130 if (!query.length) 131 return; 132 133 this.prompt.history.push(query); 134 this.prompt.historyOffset = 0; 135 this.prompt.text = ""; 136 137 this.database.executeSql(query, this._queryFinished.bind(this, query), this._queryError.bind(this, query)); 138 }, 139 140 _queryFinished: function(query, result) 141 { 142 var dataGrid = WebInspector.panels.storage.dataGridForResult(result); 143 if (!dataGrid) 144 return; 145 dataGrid.element.addStyleClass("inline"); 146 this._appendQueryResult(query, dataGrid.element); 147 dataGrid.autoSizeColumns(5); 148 149 if (query.match(/^create /i) || query.match(/^drop table /i)) 150 WebInspector.panels.storage.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 _appendQueryResult: function(query, result, resultClassName) 166 { 167 var element = document.createElement("div"); 168 element.className = "database-user-query"; 169 170 var commandTextElement = document.createElement("span"); 171 commandTextElement.className = "database-query-text"; 172 commandTextElement.textContent = query; 173 element.appendChild(commandTextElement); 174 175 var resultElement = document.createElement("div"); 176 resultElement.className = "database-query-result"; 177 178 if (resultClassName) 179 resultElement.addStyleClass(resultClassName); 180 181 if (typeof result === "string" || result instanceof String) 182 resultElement.textContent = result; 183 else if (result && result.nodeName) 184 resultElement.appendChild(result); 185 186 if (resultElement.childNodes.length) 187 element.appendChild(resultElement); 188 189 this.element.insertBefore(element, this.promptElement); 190 this.promptElement.scrollIntoView(false); 191 } 192 } 193 194 WebInspector.DatabaseQueryView.prototype.__proto__ = WebInspector.View.prototype; 195