Factor out the Console text completion and command history into
authortimothy@apple.com <timothy@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Apr 2008 20:50:58 +0000 (20:50 +0000)
committertimothy@apple.com <timothy@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Apr 2008 20:50:58 +0000 (20:50 +0000)
a new TextPrompt object. This TextPrompt object will be used later
other parts of the Inspector that need text completion and history.
Not functionality changed, just moved code around.

Reviewed by Adam Roben.

* WebCore.vcproj/WebCore.vcproj: Add TextPrompt.js.
* page/inspector/Console.js: Call the new prompt object and
  delete all the code that moved.
* page/inspector/TextPrompt.js: Added. Moved code from Console.js.
* page/inspector/WebKit.qrc: Add TextPrompt.js.
* page/inspector/inspector.html: Add TextPrompt.js.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@31645 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/WebCore.vcproj/WebCore.vcproj
WebCore/page/inspector/Console.js
WebCore/page/inspector/TextPrompt.js [new file with mode: 0644]
WebCore/page/inspector/WebKit.qrc
WebCore/page/inspector/inspector.html

index 9a2d457..ea649ae 100644 (file)
@@ -1,5 +1,21 @@
 2008-04-04  Timothy Hatcher  <timothy@apple.com>
 
+        Factor out the Console text completion and command history into
+        a new TextPrompt object. This TextPrompt object will be used later
+        other parts of the Inspector that need text completion and history.
+        Not functionality changed, just moved code around.
+
+        Reviewed by Adam Roben.
+
+        * WebCore.vcproj/WebCore.vcproj: Add TextPrompt.js.
+        * page/inspector/Console.js: Call the new prompt object and
+          delete all the code that moved.
+        * page/inspector/TextPrompt.js: Added. Moved code from Console.js.
+        * page/inspector/WebKit.qrc: Add TextPrompt.js.
+        * page/inspector/inspector.html: Add TextPrompt.js.
+
+2008-04-04  Timothy Hatcher  <timothy@apple.com>
+
         Makes setting shouldRefreshChildren on a TreeElement perform the refresh
         immediately if the element is already expanded.
 
index 804f719..330112b 100644 (file)
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath="..\page\inspector\TextPrompt.js"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath="..\page\inspector\Database.js"\r
                                        >\r
                                </File>\r
index f1efbeb..59fa74b 100644 (file)
@@ -32,9 +32,6 @@ WebInspector.ConsolePanel = function()
 
     this.messages = [];
 
-    this.commandHistory = [];
-    this.commandOffset = 0;
-
     this.messagesElement = document.createElement("div");
     this.messagesElement.id = "console-messages";
     this.messagesElement.addEventListener("selectstart", this.messagesSelectStart.bind(this), true);
@@ -47,6 +44,8 @@ WebInspector.ConsolePanel = function()
     this.promptElement.appendChild(document.createElement("br"));
     this.messagesElement.appendChild(this.promptElement);
 
+    this.prompt = new WebInspector.TextPrompt(this.promptElement, this.completions.bind(this), " .=:[({;");
+
     this.clearButton = document.createElement("button");
     this.clearButton.title = WebInspector.UIString("Clear");
     this.clearButton.textContent = WebInspector.UIString("Clear");
@@ -54,23 +53,6 @@ WebInspector.ConsolePanel = function()
 }
 
 WebInspector.ConsolePanel.prototype = {
-    get promptText()
-    {
-        return this.promptElement.textContent;
-    },
-
-    set promptText(x)
-    {
-        if (!x) {
-            // Append a break element instead of setting textContent to make sure the selection is inside the prompt.
-            this.promptElement.removeChildren();
-            this.promptElement.appendChild(document.createElement("br"));
-        } else
-            this.promptElement.textContent = x;
-
-        this._moveCaretToEndOfPrompt();
-    },
-
     show: function()
     {
         WebInspector.Panel.prototype.show.call(this);
@@ -84,8 +66,8 @@ WebInspector.ConsolePanel.prototype = {
 
         function focusPrompt()
         {
-            if (!this._caretInsidePrompt())
-                this._moveCaretToEndOfPrompt();
+            if (!this.prompt.isCaretInsidePrompt())
+                this.prompt.moveCaretToEndOfPrompt();
         }
 
         setTimeout(focusPrompt.bind(this), 0);
@@ -139,132 +121,10 @@ WebInspector.ConsolePanel.prototype = {
             this.messagesElement.removeChild(this.messagesElement.firstChild);
     },
 
-    acceptAutoComplete: function()
-    {
-        if (!this.autoCompleteElement || !this.autoCompleteElement.parentNode)
-            return false;
-
-        var text = this.autoCompleteElement.textContent;
-        var textNode = document.createTextNode(text);
-        this.autoCompleteElement.parentNode.replaceChild(textNode, this.autoCompleteElement);
-        delete this.autoCompleteElement;
-
-        var finalSelectionRange = document.createRange();
-        finalSelectionRange.setStart(textNode, text.length);
-        finalSelectionRange.setEnd(textNode, text.length);
-
-        var selection = window.getSelection();
-        selection.removeAllRanges();
-        selection.addRange(finalSelectionRange);
-
-        return true;
-    },
-
-    clearAutoComplete: function(includeTimeout)
-    {
-        if (includeTimeout && "completeTimeout" in this) {
-            clearTimeout(this.completeTimeout);
-            delete this.completeTimeout;
-        }
-
-        if (!this.autoCompleteElement)
-            return;
-
-        if (this.autoCompleteElement.parentNode)
-            this.autoCompleteElement.parentNode.removeChild(this.autoCompleteElement);
-        delete this.autoCompleteElement;
-    },
-
-    autoCompleteSoon: function()
-    {
-        if (!("completeTimeout" in this))
-            this.completeTimeout = setTimeout(this.complete.bind(this, true), 250);
-    },
-
-    complete: function(auto)
-    {
-        this.clearAutoComplete(true);
-
-        var selection = window.getSelection();
-        if (!selection.rangeCount)
-            return;
-
-        var selectionRange = selection.getRangeAt(0);
-        if (!selectionRange.commonAncestorContainer.isDescendant(this.promptElement))
-            return;
-        if (auto && !this._caretAtEndOfPrompt())
-            return;
-
-        // Pass more characters to _backwardsRange so the range will be as short as possible.
-        var wordPrefixRange = this._backwardsRange(" .=:[({;", selectionRange.startContainer, selectionRange.startOffset, this.promptElement);
-
-        var completions = this.completions(wordPrefixRange, auto);
-
-        if (!completions || !completions.length)
-            return;
-
-        var fullWordRange = document.createRange();
-        fullWordRange.setStart(wordPrefixRange.startContainer, wordPrefixRange.startOffset);
-        fullWordRange.setEnd(selectionRange.endContainer, selectionRange.endOffset);
-
-        if (completions.length === 1 || selection.isCollapsed || auto) {
-            var completionText = completions[0];
-        } else {
-            var currentText = fullWordRange.toString().trimTrailingWhitespace();
-
-            var foundIndex = null;
-            for (var i = 0; i < completions.length; ++i) {
-                if (completions[i] === currentText)
-                    foundIndex = i;
-            }
-
-            if (foundIndex === null || (foundIndex + 1) >= completions.length)
-                var completionText = completions[0];
-            else
-                var completionText = completions[foundIndex + 1];
-        }
-
-        var wordPrefixLength = wordPrefixRange.toString().length;
-
-        fullWordRange.deleteContents();
-
-        var finalSelectionRange = document.createRange();
-
-        if (auto) {
-            var prefixText = completionText.substring(0, wordPrefixLength);
-            var suffixText = completionText.substring(wordPrefixLength);
-
-            var prefixTextNode = document.createTextNode(prefixText);
-            fullWordRange.insertNode(prefixTextNode);           
-
-            this.autoCompleteElement = document.createElement("span");
-            this.autoCompleteElement.className = "auto-complete-text";
-            this.autoCompleteElement.textContent = suffixText;
-
-            prefixTextNode.parentNode.insertBefore(this.autoCompleteElement, prefixTextNode.nextSibling);
-
-            finalSelectionRange.setStart(prefixTextNode, wordPrefixLength);
-            finalSelectionRange.setEnd(prefixTextNode, wordPrefixLength);
-        } else {
-            var completionTextNode = document.createTextNode(completionText);
-            fullWordRange.insertNode(completionTextNode);           
-
-            if (completions.length > 1)
-                finalSelectionRange.setStart(completionTextNode, wordPrefixLength);
-            else
-                finalSelectionRange.setStart(completionTextNode, completionText.length);
-
-            finalSelectionRange.setEnd(completionTextNode, completionText.length);
-        }
-
-        selection.removeAllRanges();
-        selection.addRange(finalSelectionRange);
-    },
-
     completions: function(wordRange, bestMatchOnly)
     {
-        // Pass less characters to _backwardsRange so the range will be a more complete expression.
-        var expression = this._backwardsRange(" =:{;", wordRange.startContainer, wordRange.startOffset, this.promptElement);
+        // Pass less characters to scanBackwards so the range will be a more complete expression.
+        var expression = this.prompt.scanBackwards(" =:{;", wordRange.startContainer, wordRange.startOffset);
         var expressionString = expression.toString();
         var lastIndex = expressionString.length - 1;
 
@@ -322,14 +182,14 @@ WebInspector.ConsolePanel.prototype = {
         if (this._selectionTimeout)
             clearTimeout(this._selectionTimeout);
 
-        this.clearAutoComplete();
+        this.prompt.clearAutoComplete();
 
         function moveBackIfOutside()
         {
             delete this._selectionTimeout;
-            if (!this._caretInsidePrompt() && window.getSelection().isCollapsed)
-                this._moveCaretToEndOfPrompt();
-            this.autoCompleteSoon();
+            if (!this.prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed)
+                this.prompt.moveCaretToEndOfPrompt();
+            this.prompt.autoCompleteSoon();
         }
 
         this._selectionTimeout = setTimeout(moveBackIfOutside.bind(this), 100);
@@ -367,64 +227,11 @@ WebInspector.ConsolePanel.prototype = {
     {
         switch (event.keyIdentifier) {
             case "Enter":
-                this._onEnterPressed(event);
-                break;
-            case "Up":
-                this._onUpPressed(event);
-                break;
-            case "Down":
-                this._onDownPressed(event);
-                break;
-            case "U+0009": // Tab
-                this._onTabPressed(event);
-                break;
-            case "Right":
-                if (!this.acceptAutoComplete())
-                    this.autoCompleteSoon();
-                break;
-            default:
-                this.clearAutoComplete();
-                this.autoCompleteSoon();
-                break;
-        }
-    },
-
-    _backwardsRange: function(stopCharacters, endNode, endOffset, stayWithinElement)
-    {
-        var startNode;
-        var startOffset = 0;
-        var node = endNode;
-
-        while (node) {
-            if (node === stayWithinElement) {
-                if (!startNode)
-                    startNode = stayWithinElement;
-                break;
-            }
-
-            if (node.nodeType === Node.TEXT_NODE) {
-                var start = (node === endNode ? endOffset : node.nodeValue.length);
-                for (var i = (start - 1); i >= 0; --i) {
-                    var character = node.nodeValue[i];
-                    if (stopCharacters.indexOf(character) !== -1) {
-                        startNode = node;
-                        startOffset = i + 1;
-                        break;
-                    }
-                }
-            }
-
-            if (startNode)
-                break;
-
-            node = node.traversePreviousNode();
+                this._enterKeyPressed(event);
+                return;
         }
 
-        var result = document.createRange();
-        result.setStart(startNode, startOffset);
-        result.setEnd(endNode, endOffset);
-
-        return result;
+        this.prompt.handleKeyEvent(event);
     },
 
     _evalInInspectedWindow: function(expression)
@@ -435,71 +242,14 @@ WebInspector.ConsolePanel.prototype = {
         }
     },
 
-    _caretInsidePrompt: function()
-    {
-        var selection = window.getSelection();
-        if (!selection.rangeCount || !selection.isCollapsed)
-            return false;
-        var selectionRange = selection.getRangeAt(0);
-        return selectionRange.startContainer === this.promptElement || selectionRange.startContainer.isDescendant(this.promptElement);
-    },
-
-    _caretAtEndOfPrompt: function()
-    {
-        var selection = window.getSelection();
-        if (!selection.rangeCount || !selection.isCollapsed)
-            return false;
-
-        var selectionRange = selection.getRangeAt(0);
-        var node = selectionRange.startContainer;
-        if (node !== this.promptElement && !node.isDescendant(this.promptElement))
-            return false;
-
-        if (node.nodeType === Node.TEXT_NODE && selectionRange.startOffset < node.nodeValue.length)
-            return false;
-
-        var foundNextText = false;
-        while (node) {
-            if (node.nodeType === Node.TEXT_NODE && node.nodeValue.length) {
-                if (foundNextText)
-                    return false;
-                foundNextText = true;
-            }
-
-            node = node.traverseNextNode(false, this.promptElement);
-        }
-
-        return true;
-    },
-
-    _moveCaretToEndOfPrompt: function()
-    {
-        var selection = window.getSelection();
-        var selectionRange = document.createRange();
-
-        var offset = this.promptElement.childNodes.length;
-        selectionRange.setStart(this.promptElement, offset);
-        selectionRange.setEnd(this.promptElement, offset);
-
-        selection.removeAllRanges();
-        selection.addRange(selectionRange);
-    },
-
-    _onTabPressed: function(event)
-    {
-        event.preventDefault();
-        event.stopPropagation();
-        this.complete();
-    },
-
-    _onEnterPressed: function(event)
+    _enterKeyPressed: function(event)
     {
         event.preventDefault();
         event.stopPropagation();
 
-        this.clearAutoComplete(true);
+        this.prompt.clearAutoComplete(true);
 
-        var str = this.promptText;
+        var str = this.prompt.text;
         if (!str.length)
             return;
 
@@ -517,44 +267,14 @@ WebInspector.ConsolePanel.prototype = {
             exception = true;
         }
 
+        this.prompt.history.push(str);
+        this.prompt.historyOffset = 0;
+        this.prompt.text = "";
+
         var level = exception ? WebInspector.ConsoleMessage.MessageLevel.Error : WebInspector.ConsoleMessage.MessageLevel.Log;
         this.addMessage(new WebInspector.ConsoleCommand(str, result, this._format(result), level));
     },
 
-    _onUpPressed: function(event)
-    {
-        event.preventDefault();
-        event.stopPropagation();
-
-        if (this.commandOffset == this.commandHistory.length)
-            return;
-
-        if (this.commandOffset == 0)
-            this.tempSavedCommand = this.promptText;
-
-        ++this.commandOffset;
-        this.promptText = this.commandHistory[this.commandHistory.length - this.commandOffset];
-    },
-
-    _onDownPressed: function(event)
-    {
-        event.preventDefault();
-        event.stopPropagation();
-
-        if (this.commandOffset == 0)
-            return;
-
-        --this.commandOffset;
-
-        if (this.commandOffset == 0) {
-            this.promptText = this.tempSavedCommand;
-            delete this.tempSavedCommand;
-            return;
-        }
-
-        this.promptText = this.commandHistory[this.commandHistory.length - this.commandOffset];
-    },
-
     _format: function(output)
     {
         var type = Object.type(output);
diff --git a/WebCore/page/inspector/TextPrompt.js b/WebCore/page/inspector/TextPrompt.js
new file mode 100644 (file)
index 0000000..a132dd4
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2008 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.TextPrompt = function(element, completions, stopCharacters)
+{
+    this.element = element;
+    this.completions = completions;
+    this.completionStopCharacters = stopCharacters;
+    this.history = [];
+    this.historyOffset = 0;
+}
+
+WebInspector.TextPrompt.prototype = {
+    get text()
+    {
+        return this.element.textContent;
+    },
+
+    set text(x)
+    {
+        if (!x) {
+            // Append a break element instead of setting textContent to make sure the selection is inside the prompt.
+            this.element.removeChildren();
+            this.element.appendChild(document.createElement("br"));
+        } else
+            this.element.textContent = x;
+
+        this.moveCaretToEndOfPrompt();
+    },
+
+    handleKeyEvent: function(event)
+    {
+        switch (event.keyIdentifier) {
+            case "Up":
+                this._upKeyPressed(event);
+                break;
+            case "Down":
+                this._downKeyPressed(event);
+                break;
+            case "U+0009": // Tab
+                this._tabKeyPressed(event);
+                break;
+            case "Right":
+                if (!this.acceptAutoComplete())
+                    this.autoCompleteSoon();
+                break;
+            default:
+                this.clearAutoComplete();
+                this.autoCompleteSoon();
+                break;
+        }
+    },
+
+    acceptAutoComplete: function()
+    {
+        if (!this.autoCompleteElement || !this.autoCompleteElement.parentNode)
+            return false;
+
+        var text = this.autoCompleteElement.textContent;
+        var textNode = document.createTextNode(text);
+        this.autoCompleteElement.parentNode.replaceChild(textNode, this.autoCompleteElement);
+        delete this.autoCompleteElement;
+
+        var finalSelectionRange = document.createRange();
+        finalSelectionRange.setStart(textNode, text.length);
+        finalSelectionRange.setEnd(textNode, text.length);
+
+        var selection = window.getSelection();
+        selection.removeAllRanges();
+        selection.addRange(finalSelectionRange);
+
+        return true;
+    },
+
+    clearAutoComplete: function(includeTimeout)
+    {
+        if (includeTimeout && "_completeTimeout" in this) {
+            clearTimeout(this._completeTimeout);
+            delete this._completeTimeout;
+        }
+
+        if (!this.autoCompleteElement)
+            return;
+
+        if (this.autoCompleteElement.parentNode)
+            this.autoCompleteElement.parentNode.removeChild(this.autoCompleteElement);
+        delete this.autoCompleteElement;
+    },
+
+    autoCompleteSoon: function()
+    {
+        if (!("_completeTimeout" in this))
+            this._completeTimeout = setTimeout(this.complete.bind(this, true), 250);
+    },
+
+    complete: function(auto)
+    {
+        this.clearAutoComplete(true);
+
+        var selection = window.getSelection();
+        if (!selection.rangeCount)
+            return;
+
+        var selectionRange = selection.getRangeAt(0);
+        if (!selectionRange.commonAncestorContainer.isDescendant(this.element))
+            return;
+        if (auto && !this.isCaretAtEndOfPrompt())
+            return;
+
+        var wordPrefixRange = this.scanBackwards(this.completionStopCharacters, selectionRange.startContainer, selectionRange.startOffset, this.element);
+
+        var completions = this.completions(wordPrefixRange, auto);
+
+        if (!completions || !completions.length)
+            return;
+
+        var fullWordRange = document.createRange();
+        fullWordRange.setStart(wordPrefixRange.startContainer, wordPrefixRange.startOffset);
+        fullWordRange.setEnd(selectionRange.endContainer, selectionRange.endOffset);
+
+        if (completions.length === 1 || selection.isCollapsed || auto) {
+            var completionText = completions[0];
+        } else {
+            var currentText = fullWordRange.toString().trimTrailingWhitespace();
+
+            var foundIndex = null;
+            for (var i = 0; i < completions.length; ++i) {
+                if (completions[i] === currentText)
+                    foundIndex = i;
+            }
+
+            if (foundIndex === null || (foundIndex + 1) >= completions.length)
+                var completionText = completions[0];
+            else
+                var completionText = completions[foundIndex + 1];
+        }
+
+        var wordPrefixLength = wordPrefixRange.toString().length;
+
+        fullWordRange.deleteContents();
+
+        var finalSelectionRange = document.createRange();
+
+        if (auto) {
+            var prefixText = completionText.substring(0, wordPrefixLength);
+            var suffixText = completionText.substring(wordPrefixLength);
+
+            var prefixTextNode = document.createTextNode(prefixText);
+            fullWordRange.insertNode(prefixTextNode);           
+
+            this.autoCompleteElement = document.createElement("span");
+            this.autoCompleteElement.className = "auto-complete-text";
+            this.autoCompleteElement.textContent = suffixText;
+
+            prefixTextNode.parentNode.insertBefore(this.autoCompleteElement, prefixTextNode.nextSibling);
+
+            finalSelectionRange.setStart(prefixTextNode, wordPrefixLength);
+            finalSelectionRange.setEnd(prefixTextNode, wordPrefixLength);
+        } else {
+            var completionTextNode = document.createTextNode(completionText);
+            fullWordRange.insertNode(completionTextNode);           
+
+            if (completions.length > 1)
+                finalSelectionRange.setStart(completionTextNode, wordPrefixLength);
+            else
+                finalSelectionRange.setStart(completionTextNode, completionText.length);
+
+            finalSelectionRange.setEnd(completionTextNode, completionText.length);
+        }
+
+        selection.removeAllRanges();
+        selection.addRange(finalSelectionRange);
+    },
+
+    scanBackwards: function(stopCharacters, endNode, endOffset, stayWithinElement)
+    {
+        var startNode;
+        var startOffset = 0;
+        var node = endNode;
+
+        if (!stayWithinElement)
+            stayWithinElement = this.element;
+
+        while (node) {
+            if (node === stayWithinElement) {
+                if (!startNode)
+                    startNode = stayWithinElement;
+                break;
+            }
+
+            if (node.nodeType === Node.TEXT_NODE) {
+                var start = (node === endNode ? endOffset : node.nodeValue.length);
+                for (var i = (start - 1); i >= 0; --i) {
+                    var character = node.nodeValue[i];
+                    if (stopCharacters.indexOf(character) !== -1) {
+                        startNode = node;
+                        startOffset = i + 1;
+                        break;
+                    }
+                }
+            }
+
+            if (startNode)
+                break;
+
+            node = node.traversePreviousNode();
+        }
+
+        var result = document.createRange();
+        result.setStart(startNode, startOffset);
+        result.setEnd(endNode, endOffset);
+
+        return result;
+    },
+
+    isCaretInsidePrompt: function()
+    {
+        var selection = window.getSelection();
+        if (!selection.rangeCount || !selection.isCollapsed)
+            return false;
+        var selectionRange = selection.getRangeAt(0);
+        return selectionRange.startContainer === this.element || selectionRange.startContainer.isDescendant(this.element);
+    },
+
+    isCaretAtEndOfPrompt: function()
+    {
+        var selection = window.getSelection();
+        if (!selection.rangeCount || !selection.isCollapsed)
+            return false;
+
+        var selectionRange = selection.getRangeAt(0);
+        var node = selectionRange.startContainer;
+        if (node !== this.element && !node.isDescendant(this.element))
+            return false;
+
+        if (node.nodeType === Node.TEXT_NODE && selectionRange.startOffset < node.nodeValue.length)
+            return false;
+
+        var foundNextText = false;
+        while (node) {
+            if (node.nodeType === Node.TEXT_NODE && node.nodeValue.length) {
+                if (foundNextText)
+                    return false;
+                foundNextText = true;
+            }
+
+            node = node.traverseNextNode(false, this.element);
+        }
+
+        return true;
+    },
+
+    moveCaretToEndOfPrompt: function()
+    {
+        var selection = window.getSelection();
+        var selectionRange = document.createRange();
+
+        var offset = this.element.childNodes.length;
+        selectionRange.setStart(this.element, offset);
+        selectionRange.setEnd(this.element, offset);
+
+        selection.removeAllRanges();
+        selection.addRange(selectionRange);
+    },
+
+    _tabKeyPressed: function(event)
+    {
+        event.preventDefault();
+        event.stopPropagation();
+
+        this.complete();
+    },
+
+    _upKeyPressed: function(event)
+    {
+        event.preventDefault();
+        event.stopPropagation();
+
+        if (this.historyOffset == this.history.length)
+            return;
+
+        this.clearAutoComplete(true);
+
+        if (this.historyOffset == 0)
+            this.tempSavedCommand = this.text;
+
+        ++this.historyOffset;
+        this.text = this.history[this.history.length - this.historyOffset];
+    },
+
+    _downKeyPressed: function(event)
+    {
+        event.preventDefault();
+        event.stopPropagation();
+
+        if (this.historyOffset == 0)
+            return;
+
+        this.clearAutoComplete(true);
+
+        --this.historyOffset;
+
+        if (this.historyOffset == 0) {
+            this.text = this.tempSavedCommand;
+            delete this.tempSavedCommand;
+            return;
+        }
+
+        this.text = this.history[this.history.length - this.historyOffset];
+    }
+}
index 3fe4abd..e275156 100644 (file)
@@ -5,6 +5,7 @@
     <file>DatabasesPanel.js</file>
     <file>ElementsPanel.js</file>
     <file>FontView.js</file>
+    <file>TextPrompt.js</file>
     <file>ImageView.js</file>
     <file>MetricsSidebarPane.js</file>
     <file>Panel.js</file>
index 0bc0bc1..ade2768 100644 (file)
@@ -33,6 +33,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     <script type="text/javascript" src="utilities.js"></script>
     <script type="text/javascript" src="treeoutline.js"></script>
     <script type="text/javascript" src="inspector.js"></script>
+    <script type="text/javascript" src="TextPrompt.js"></script>
     <script type="text/javascript" src="Resource.js"></script>
     <script type="text/javascript" src="ResourceCategory.js"></script>
     <script type="text/javascript" src="Database.js"></script>