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.addStyleClass("storage-view"); 37 this.element.addStyleClass("query"); 38 this.element.addStyleClass("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 function moveBackIfOutside() 107 { 108 delete this._selectionTimeout; 109 if (!this.prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed) 110 this.prompt.moveCaretToEndOfPrompt(); 111 this.prompt.autoCompleteSoon(); 112 } 113 114 this._selectionTimeout = setTimeout(moveBackIfOutside.bind(this), 100); 115 }, 116 117 _promptKeyDown: function(event) 118 { 119 if (isEnterKey(event)) { 120 this._enterKeyPressed(event); 121 return; 122 } 123 }, 124 125 _enterKeyPressed: function(event) 126 { 127 event.consume(true); 128 129 this.prompt.clearAutoComplete(true); 130 131 var query = this.prompt.text; 132 if (!query.length) 133 return; 134 135 this.prompt.pushHistoryItem(query); 136 this.prompt.text = ""; 137 138 this.database.executeSql(query, this._queryFinished.bind(this, query), this._queryError.bind(this, query)); 139 }, 140 141 _queryFinished: function(query, columnNames, values) 142 { 143 var dataGrid = WebInspector.DataGrid.createSortableDataGrid(columnNames, values); 144 var trimmedQuery = query.trim(); 145 146 if (dataGrid) { 147 dataGrid.renderInline(); 148 this._appendViewQueryResult(trimmedQuery, dataGrid); 149 dataGrid.autoSizeColumns(5); 150 } 151 152 if (trimmedQuery.match(/^create /i) || trimmedQuery.match(/^drop table /i)) 153 this.dispatchEventToListeners(WebInspector.DatabaseQueryView.Events.SchemaUpdated, this.database); 154 }, 155 156 _queryError: function(query, errorMessage) 157 { 158 this._appendErrorQueryResult(query, errorMessage); 159 }, 160 161 /** 162 * @param {string} query 163 * @param {WebInspector.View} view 164 */ 165 _appendViewQueryResult: function(query, view) 166 { 167 var resultElement = this._appendQueryResult(query); 168 view.show(resultElement); 169 170 this._promptElement.scrollIntoView(false); 171 }, 172 173 /** 174 * @param {string} query 175 * @param {string} errorText 176 */ 177 _appendErrorQueryResult: function(query, errorText) 178 { 179 var resultElement = this._appendQueryResult(query); 180 resultElement.addStyleClass("error") 181 resultElement.textContent = errorText; 182 183 this._promptElement.scrollIntoView(false); 184 }, 185 186 _appendQueryResult: function(query) 187 { 188 var element = document.createElement("div"); 189 element.className = "database-user-query"; 190 this.element.insertBefore(element, this.prompt.proxyElement); 191 192 var commandTextElement = document.createElement("span"); 193 commandTextElement.className = "database-query-text"; 194 commandTextElement.textContent = query; 195 element.appendChild(commandTextElement); 196 197 var resultElement = document.createElement("div"); 198 resultElement.className = "database-query-result"; 199 element.appendChild(resultElement); 200 return resultElement; 201 }, 202 203 __proto__: WebInspector.View.prototype 204 } 205