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.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