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 /** 27 * @constructor 28 * @extends {WebInspector.View} 29 */ 30 WebInspector.DatabaseQueryView = function(database) 31 { 32 WebInspector.View.call(this); 33 34 this.database = database; 35 36 this.element.classList.add("storage-view"); 37 this.element.classList.add("query"); 38 this.element.classList.add("monospace"); 39 this.element.addEventListener("selectstart", this._selectStart.bind(this), false); 40 41 this._promptElement = document.createElement("div"); 42 this._promptElement.className = "database-query-prompt"; 43 this._promptElement.appendChild(document.createElement("br")); 44 this._promptElement.addEventListener("keydown", this._promptKeyDown.bind(this), true); 45 this.element.appendChild(this._promptElement); 46 47 this.prompt = new WebInspector.TextPromptWithHistory(this.completions.bind(this), " "); 48 this.prompt.attach(this._promptElement); 49 50 this.element.addEventListener("click", this._messagesClicked.bind(this), true); 51 } 52 53 WebInspector.DatabaseQueryView.Events = { 54 SchemaUpdated: "SchemaUpdated" 55 } 56 57 WebInspector.DatabaseQueryView.prototype = { 58 _messagesClicked: function() 59 { 60 if (!this.prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed) 61 this.prompt.moveCaretToEndOfPrompt(); 62 }, 63 64 /** 65 * @param {!Element} proxyElement 66 * @param {!Range} wordRange 67 * @param {boolean} force 68 * @param {function(!Array.<string>, number=)} completionsReadyCallback 69 */ 70 completions: function(proxyElement, wordRange, force, completionsReadyCallback) 71 { 72 var prefix = wordRange.toString().toLowerCase(); 73 if (!prefix) 74 return; 75 var results = []; 76 77 function accumulateMatches(textArray) 78 { 79 for (var i = 0; i < textArray.length; ++i) { 80 var text = textArray[i].toLowerCase(); 81 if (text.length < prefix.length) 82 continue; 83 if (!text.startsWith(prefix)) 84 continue; 85 results.push(textArray[i]); 86 } 87 } 88 89 function tableNamesCallback(tableNames) 90 { 91 accumulateMatches(tableNames.map(function(name) { return name + " " })); 92 accumulateMatches(["SELECT ", "FROM ", "WHERE ", "LIMIT ", "DELETE FROM ", "CREATE ", "DROP ", "TABLE ", "INDEX ", "UPDATE ", "INSERT INTO ", "VALUES ("]); 93 94 completionsReadyCallback(results); 95 } 96 this.database.getTableNames(tableNamesCallback); 97 }, 98 99 _selectStart: function(event) 100 { 101 if (this._selectionTimeout) 102 clearTimeout(this._selectionTimeout); 103 104 this.prompt.clearAutoComplete(); 105 106 /** 107 * @this {WebInspector.DatabaseQueryView} 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 _promptKeyDown: function(event) 121 { 122 if (isEnterKey(event)) { 123 this._enterKeyPressed(event); 124 return; 125 } 126 }, 127 128 _enterKeyPressed: function(event) 129 { 130 event.consume(true); 131 132 this.prompt.clearAutoComplete(true); 133 134 var query = this.prompt.text; 135 if (!query.length) 136 return; 137 138 this.prompt.pushHistoryItem(query); 139 this.prompt.text = ""; 140 141 this.database.executeSql(query, this._queryFinished.bind(this, query), this._queryError.bind(this, query)); 142 }, 143 144 _queryFinished: function(query, columnNames, values) 145 { 146 var dataGrid = WebInspector.DataGrid.createSortableDataGrid(columnNames, values); 147 var trimmedQuery = query.trim(); 148 149 if (dataGrid) { 150 dataGrid.renderInline(); 151 this._appendViewQueryResult(trimmedQuery, dataGrid); 152 dataGrid.autoSizeColumns(5); 153 } 154 155 if (trimmedQuery.match(/^create /i) || trimmedQuery.match(/^drop table /i)) 156 this.dispatchEventToListeners(WebInspector.DatabaseQueryView.Events.SchemaUpdated, this.database); 157 }, 158 159 _queryError: function(query, errorMessage) 160 { 161 this._appendErrorQueryResult(query, errorMessage); 162 }, 163 164 /** 165 * @param {string} query 166 * @param {!WebInspector.View} view 167 */ 168 _appendViewQueryResult: function(query, view) 169 { 170 var resultElement = this._appendQueryResult(query); 171 view.show(resultElement); 172 173 this._promptElement.scrollIntoView(false); 174 }, 175 176 /** 177 * @param {string} query 178 * @param {string} errorText 179 */ 180 _appendErrorQueryResult: function(query, errorText) 181 { 182 var resultElement = this._appendQueryResult(query); 183 resultElement.classList.add("error") 184 resultElement.textContent = errorText; 185 186 this._promptElement.scrollIntoView(false); 187 }, 188 189 _appendQueryResult: function(query) 190 { 191 var element = document.createElement("div"); 192 element.className = "database-user-query"; 193 this.element.insertBefore(element, this.prompt.proxyElement); 194 195 var commandTextElement = document.createElement("span"); 196 commandTextElement.className = "database-query-text"; 197 commandTextElement.textContent = query; 198 element.appendChild(commandTextElement); 199 200 var resultElement = document.createElement("div"); 201 resultElement.className = "database-query-result"; 202 element.appendChild(resultElement); 203 return resultElement; 204 }, 205 206 __proto__: WebInspector.View.prototype 207 } 208