Home | History | Annotate | Download | only in front_end
      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