Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / ui / TextPrompt.js
blob0734a53893b67189014a7d98df5db2599609428d
1 /*
2 * Copyright (C) 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2011 Google Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 /**
31 * @constructor
32 * @extends {WebInspector.Object}
33 * @implements {WebInspector.SuggestBoxDelegate}
34 * @param {function(!Element, !Range, boolean, function(!Array.<string>, number=))} completions
35 * @param {string=} stopCharacters
37 WebInspector.TextPrompt = function(completions, stopCharacters)
39 /**
40 * @type {!Element|undefined}
42 this._proxyElement;
43 this._proxyElementDisplay = "inline-block";
44 this._loadCompletions = completions;
45 this._completionStopCharacters = stopCharacters || " =:[({;,!+-*/&|^<>.";
46 this._autocompletionTimeout = WebInspector.TextPrompt.DefaultAutocompletionTimeout;
49 WebInspector.TextPrompt.DefaultAutocompletionTimeout = 250;
51 WebInspector.TextPrompt.Events = {
52 ItemApplied: "text-prompt-item-applied",
53 ItemAccepted: "text-prompt-item-accepted"
56 WebInspector.TextPrompt.prototype = {
57 /**
58 * @param {number} timeout
60 setAutocompletionTimeout: function(timeout)
62 this._autocompletionTimeout = timeout;
65 /**
66 * @param {boolean} suggestBoxEnabled
68 setSuggestBoxEnabled: function(suggestBoxEnabled)
70 this._suggestBoxEnabled = suggestBoxEnabled;
73 renderAsBlock: function()
75 this._proxyElementDisplay = "block";
78 /**
79 * Clients should never attach any event listeners to the |element|. Instead,
80 * they should use the result of this method to attach listeners for bubbling events.
82 * @param {!Element} element
83 * @return {!Element}
85 attach: function(element)
87 return this._attachInternal(element);
90 /**
91 * Clients should never attach any event listeners to the |element|. Instead,
92 * they should use the result of this method to attach listeners for bubbling events
93 * or the |blurListener| parameter to register a "blur" event listener on the |element|
94 * (since the "blur" event does not bubble.)
96 * @param {!Element} element
97 * @param {function(!Event)} blurListener
98 * @return {!Element}
100 attachAndStartEditing: function(element, blurListener)
102 var proxyElement = this._attachInternal(element);
103 this._startEditing(blurListener);
104 return proxyElement;
108 * @param {!Element} element
109 * @return {!Element}
111 _attachInternal: function(element)
113 if (this._proxyElement)
114 throw "Cannot attach an attached TextPrompt";
115 this._element = element;
117 this._boundOnKeyDown = this.onKeyDown.bind(this);
118 this._boundOnInput = this.onInput.bind(this);
119 this._boundOnMouseWheel = this.onMouseWheel.bind(this);
120 this._boundSelectStart = this._selectStart.bind(this);
121 this._boundRemoveSuggestionAids = this._removeSuggestionAids.bind(this);
122 this._proxyElement = element.ownerDocument.createElement("span");
123 var shadowRoot = WebInspector.createShadowRootWithCoreStyles(this._proxyElement);
124 shadowRoot.appendChild(WebInspector.Widget.createStyleElement("ui/textPrompt.css"));
125 this._contentElement = shadowRoot.createChild("div");
126 this._contentElement.createChild("content");
127 this._proxyElement.style.display = this._proxyElementDisplay;
128 element.parentElement.insertBefore(this._proxyElement, element);
129 this._proxyElement.appendChild(element);
130 this._element.classList.add("text-prompt");
131 this._element.addEventListener("keydown", this._boundOnKeyDown, false);
132 this._element.addEventListener("input", this._boundOnInput, false);
133 this._element.addEventListener("mousewheel", this._boundOnMouseWheel, false);
134 this._element.addEventListener("selectstart", this._boundSelectStart, false);
135 this._element.addEventListener("blur", this._boundRemoveSuggestionAids, false);
136 this._element.ownerDocument.defaultView.addEventListener("resize", this._boundRemoveSuggestionAids, false);
138 if (this._suggestBoxEnabled)
139 this._suggestBox = new WebInspector.SuggestBox(this);
141 return this._proxyElement;
144 detach: function()
146 this._removeFromElement();
147 this._proxyElement.parentElement.insertBefore(this._element, this._proxyElement);
148 this._proxyElement.remove();
149 delete this._proxyElement;
150 this._element.classList.remove("text-prompt");
151 WebInspector.restoreFocusFromElement(this._element);
155 * @return {string}
157 text: function()
159 return this._element.textContent;
163 * @param {string} x
165 setText: function(x)
167 this._removeSuggestionAids();
168 if (!x) {
169 // Append a break element instead of setting textContent to make sure the selection is inside the prompt.
170 this._element.removeChildren();
171 this._element.createChild("br");
172 } else {
173 this._element.textContent = x;
176 this.moveCaretToEndOfPrompt();
177 this._element.scrollIntoView();
180 _removeFromElement: function()
182 this.clearAutoComplete(true);
183 this._element.removeEventListener("keydown", this._boundOnKeyDown, false);
184 this._element.removeEventListener("input", this._boundOnInput, false);
185 this._element.removeEventListener("selectstart", this._boundSelectStart, false);
186 this._element.removeEventListener("blur", this._boundRemoveSuggestionAids, false);
187 this._element.ownerDocument.defaultView.removeEventListener("resize", this._boundRemoveSuggestionAids, false);
188 if (this._isEditing)
189 this._stopEditing();
190 if (this._suggestBox)
191 this._suggestBox.removeFromElement();
195 * @param {function(!Event)=} blurListener
197 _startEditing: function(blurListener)
199 this._isEditing = true;
200 this._contentElement.classList.add("text-prompt-editing");
201 if (blurListener) {
202 this._blurListener = blurListener;
203 this._element.addEventListener("blur", this._blurListener, false);
205 this._oldTabIndex = this._element.tabIndex;
206 if (this._element.tabIndex < 0)
207 this._element.tabIndex = 0;
208 WebInspector.setCurrentFocusElement(this._element);
209 if (!this.text())
210 this._updateAutoComplete();
213 _stopEditing: function()
215 this._element.tabIndex = this._oldTabIndex;
216 if (this._blurListener)
217 this._element.removeEventListener("blur", this._blurListener, false);
218 this._contentElement.classList.remove("text-prompt-editing");
219 delete this._isEditing;
222 _removeSuggestionAids: function()
224 this.clearAutoComplete();
225 this.hideSuggestBox();
228 _selectStart: function()
230 if (this._selectionTimeout)
231 clearTimeout(this._selectionTimeout);
233 this._removeSuggestionAids();
236 * @this {WebInspector.TextPrompt}
238 function moveBackIfOutside()
240 delete this._selectionTimeout;
241 if (!this.isCaretInsidePrompt() && this._element.isComponentSelectionCollapsed()) {
242 this.moveCaretToEndOfPrompt();
243 this.autoCompleteSoon();
247 this._selectionTimeout = setTimeout(moveBackIfOutside.bind(this), 100);
251 * @param {boolean=} force
253 _updateAutoComplete: function(force)
255 this.clearAutoComplete();
256 this.autoCompleteSoon(force);
260 * @param {!Event} event
262 onMouseWheel: function(event)
264 // Subclasses can implement.
268 * @param {!Event} event
270 onKeyDown: function(event)
272 var handled = false;
273 delete this._needUpdateAutocomplete;
275 switch (event.keyIdentifier) {
276 case "U+0009": // Tab
277 handled = this.tabKeyPressed(event);
278 break;
279 case "Left":
280 case "Home":
281 this._removeSuggestionAids();
282 break;
283 case "Right":
284 case "End":
285 if (this.isCaretAtEndOfPrompt())
286 handled = this.acceptAutoComplete();
287 else
288 this._removeSuggestionAids();
289 break;
290 case "U+001B": // Esc
291 if (this.isSuggestBoxVisible()) {
292 this._removeSuggestionAids();
293 handled = true;
295 break;
296 case "U+0020": // Space
297 if (event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey) {
298 this._updateAutoComplete(true);
299 handled = true;
301 break;
302 case "Alt":
303 case "Meta":
304 case "Shift":
305 case "Control":
306 break;
309 if (!handled && this.isSuggestBoxVisible())
310 handled = this._suggestBox.keyPressed(event);
312 if (!handled)
313 this._needUpdateAutocomplete = true;
315 if (handled)
316 event.consume(true);
320 * @param {!Event} event
322 onInput: function(event)
324 if (this._needUpdateAutocomplete)
325 this._updateAutoComplete();
329 * @return {boolean}
331 acceptAutoComplete: function()
333 var result = false;
334 if (this.isSuggestBoxVisible())
335 result = this._suggestBox.acceptSuggestion();
336 if (!result)
337 result = this._acceptSuggestionInternal();
339 return result;
343 * @param {boolean=} includeTimeout
345 clearAutoComplete: function(includeTimeout)
347 if (includeTimeout && this._completeTimeout) {
348 clearTimeout(this._completeTimeout);
349 delete this._completeTimeout;
351 delete this._waitingForCompletions;
353 if (!this.autoCompleteElement)
354 return;
356 this.autoCompleteElement.remove();
357 delete this.autoCompleteElement;
358 delete this._userEnteredRange;
359 delete this._userEnteredText;
363 * @param {boolean=} force
365 autoCompleteSoon: function(force)
367 var immediately = this.isSuggestBoxVisible() || force;
368 if (!this._completeTimeout)
369 this._completeTimeout = setTimeout(this.complete.bind(this, force), immediately ? 0 : this._autocompletionTimeout);
373 * @param {boolean=} force
374 * @param {boolean=} reverse
376 complete: function(force, reverse)
378 this.clearAutoComplete(true);
379 var selection = this._element.getComponentSelection();
380 if (!selection.rangeCount)
381 return;
383 var selectionRange = selection.getRangeAt(0);
384 var shouldExit;
386 if (!force && !this.isCaretAtEndOfPrompt() && !this.isSuggestBoxVisible())
387 shouldExit = true;
388 else if (!selection.isCollapsed)
389 shouldExit = true;
390 else if (!force) {
391 // BUG72018: Do not show suggest box if caret is followed by a non-stop character.
392 var wordSuffixRange = selectionRange.startContainer.rangeOfWord(selectionRange.endOffset, this._completionStopCharacters, this._element, "forward");
393 if (wordSuffixRange.toString().length)
394 shouldExit = true;
396 if (shouldExit) {
397 this.hideSuggestBox();
398 return;
401 var wordPrefixRange = selectionRange.startContainer.rangeOfWord(selectionRange.startOffset, this._completionStopCharacters, this._element, "backward");
402 this._waitingForCompletions = true;
403 this._loadCompletions(/** @type {!Element} */ (this._proxyElement), wordPrefixRange, force || false, this._completionsReady.bind(this, selection, wordPrefixRange, !!reverse));
406 disableDefaultSuggestionForEmptyInput: function()
408 this._disableDefaultSuggestionForEmptyInput = true;
412 * @param {!Selection} selection
413 * @param {!Range} textRange
415 _boxForAnchorAtStart: function(selection, textRange)
417 var rangeCopy = selection.getRangeAt(0).cloneRange();
418 var anchorElement = createElement("span");
419 anchorElement.textContent = "\u200B";
420 textRange.insertNode(anchorElement);
421 var box = anchorElement.boxInWindow(window);
422 anchorElement.remove();
423 selection.removeAllRanges();
424 selection.addRange(rangeCopy);
425 return box;
429 * @param {!Array.<string>} completions
430 * @param {number} wordPrefixLength
432 _buildCommonPrefix: function(completions, wordPrefixLength)
434 var commonPrefix = completions[0];
435 for (var i = 0; i < completions.length; ++i) {
436 var completion = completions[i];
437 var lastIndex = Math.min(commonPrefix.length, completion.length);
438 for (var j = wordPrefixLength; j < lastIndex; ++j) {
439 if (commonPrefix[j] !== completion[j]) {
440 commonPrefix = commonPrefix.substr(0, j);
441 break;
445 return commonPrefix;
449 * @return {?Range}
450 * @suppressGlobalPropertiesCheck
452 _createRange: function()
454 return document.createRange();
458 * @param {!Selection} selection
459 * @param {!Range} originalWordPrefixRange
460 * @param {boolean} reverse
461 * @param {!Array.<string>} completions
462 * @param {number=} selectedIndex
464 _completionsReady: function(selection, originalWordPrefixRange, reverse, completions, selectedIndex)
466 if (!this._waitingForCompletions || !completions.length) {
467 this.hideSuggestBox();
468 return;
470 delete this._waitingForCompletions;
472 var selectionRange = selection.getRangeAt(0);
474 var fullWordRange = this._createRange();
475 fullWordRange.setStart(originalWordPrefixRange.startContainer, originalWordPrefixRange.startOffset);
476 fullWordRange.setEnd(selectionRange.endContainer, selectionRange.endOffset);
478 if (originalWordPrefixRange.toString() + selectionRange.toString() !== fullWordRange.toString())
479 return;
481 selectedIndex = (this._disableDefaultSuggestionForEmptyInput && !this.text()) ? -1 : (selectedIndex || 0);
483 this._userEnteredRange = fullWordRange;
484 this._userEnteredText = fullWordRange.toString();
486 if (this._suggestBox)
487 this._suggestBox.updateSuggestions(this._boxForAnchorAtStart(selection, fullWordRange), completions, selectedIndex, !this.isCaretAtEndOfPrompt(), this._userEnteredText);
489 if (selectedIndex === -1)
490 return;
492 var wordPrefixLength = originalWordPrefixRange.toString().length;
493 this._commonPrefix = this._buildCommonPrefix(completions, wordPrefixLength);
495 if (this.isCaretAtEndOfPrompt()) {
496 var completionText = completions[selectedIndex];
497 var prefixText = this._userEnteredRange.toString();
498 var suffixText = completionText.substring(wordPrefixLength);
499 this._userEnteredRange.deleteContents();
500 this._element.normalize();
501 var finalSelectionRange = this._createRange();
503 var prefixTextNode = createTextNode(prefixText);
504 fullWordRange.insertNode(prefixTextNode);
506 this.autoCompleteElement = createElementWithClass("span", "auto-complete-text");
507 this.autoCompleteElement.textContent = suffixText;
509 prefixTextNode.parentNode.insertBefore(this.autoCompleteElement, prefixTextNode.nextSibling);
511 finalSelectionRange.setStart(prefixTextNode, wordPrefixLength);
512 finalSelectionRange.setEnd(prefixTextNode, wordPrefixLength);
513 selection.removeAllRanges();
514 selection.addRange(finalSelectionRange);
515 this.dispatchEventToListeners(WebInspector.TextPrompt.Events.ItemApplied);
519 _completeCommonPrefix: function()
521 if (!this.autoCompleteElement || !this._commonPrefix || !this._userEnteredText || !this._commonPrefix.startsWith(this._userEnteredText))
522 return;
524 if (!this.isSuggestBoxVisible()) {
525 this.acceptAutoComplete();
526 return;
529 this.autoCompleteElement.textContent = this._commonPrefix.substring(this._userEnteredText.length);
530 this._acceptSuggestionInternal(true);
534 * @override
535 * @param {string} completionText
536 * @param {boolean=} isIntermediateSuggestion
538 applySuggestion: function(completionText, isIntermediateSuggestion)
540 this._applySuggestion(completionText, isIntermediateSuggestion);
544 * @param {string} completionText
545 * @param {boolean=} isIntermediateSuggestion
546 * @param {!Range=} originalPrefixRange
548 _applySuggestion: function(completionText, isIntermediateSuggestion, originalPrefixRange)
550 var wordPrefixLength;
551 if (originalPrefixRange)
552 wordPrefixLength = originalPrefixRange.toString().length;
553 else
554 wordPrefixLength = this._userEnteredText ? this._userEnteredText.length : 0;
556 this._userEnteredRange.deleteContents();
557 this._element.normalize();
558 var finalSelectionRange = this._createRange();
559 var completionTextNode = createTextNode(completionText);
560 this._userEnteredRange.insertNode(completionTextNode);
561 if (this.autoCompleteElement) {
562 this.autoCompleteElement.remove();
563 delete this.autoCompleteElement;
566 if (isIntermediateSuggestion)
567 finalSelectionRange.setStart(completionTextNode, wordPrefixLength);
568 else
569 finalSelectionRange.setStart(completionTextNode, completionText.length);
571 finalSelectionRange.setEnd(completionTextNode, completionText.length);
573 var selection = this._element.getComponentSelection();
574 selection.removeAllRanges();
575 selection.addRange(finalSelectionRange);
576 if (isIntermediateSuggestion)
577 this.dispatchEventToListeners(WebInspector.TextPrompt.Events.ItemApplied, { itemText: completionText });
581 * @override
583 acceptSuggestion: function()
585 this._acceptSuggestionInternal();
589 * @param {boolean=} prefixAccepted
590 * @return {boolean}
592 _acceptSuggestionInternal: function(prefixAccepted)
594 if (!this.autoCompleteElement || !this.autoCompleteElement.parentNode)
595 return false;
597 var text = this.autoCompleteElement.textContent;
598 var textNode = createTextNode(text);
599 this.autoCompleteElement.parentNode.replaceChild(textNode, this.autoCompleteElement);
600 delete this.autoCompleteElement;
602 var finalSelectionRange = this._createRange();
603 finalSelectionRange.setStart(textNode, text.length);
604 finalSelectionRange.setEnd(textNode, text.length);
606 var selection = this._element.getComponentSelection();
607 selection.removeAllRanges();
608 selection.addRange(finalSelectionRange);
610 if (!prefixAccepted) {
611 this.hideSuggestBox();
612 this.dispatchEventToListeners(WebInspector.TextPrompt.Events.ItemAccepted);
613 } else
614 this.autoCompleteSoon(true);
616 return true;
619 hideSuggestBox: function()
621 if (this.isSuggestBoxVisible())
622 this._suggestBox.hide();
626 * @return {boolean}
628 isSuggestBoxVisible: function()
630 return this._suggestBox && this._suggestBox.visible();
634 * @return {boolean}
636 isCaretInsidePrompt: function()
638 return this._element.isInsertionCaretInside();
642 * @return {boolean}
644 isCaretAtEndOfPrompt: function()
646 var selection = this._element.getComponentSelection();
647 if (!selection.rangeCount || !selection.isCollapsed)
648 return false;
650 var selectionRange = selection.getRangeAt(0);
651 var node = selectionRange.startContainer;
652 if (!node.isSelfOrDescendant(this._element))
653 return false;
655 if (node.nodeType === Node.TEXT_NODE && selectionRange.startOffset < node.nodeValue.length)
656 return false;
658 var foundNextText = false;
659 while (node) {
660 if (node.nodeType === Node.TEXT_NODE && node.nodeValue.length) {
661 if (foundNextText && (!this.autoCompleteElement || !this.autoCompleteElement.isAncestor(node)))
662 return false;
663 foundNextText = true;
666 node = node.traverseNextNode(this._element);
669 return true;
673 * @return {boolean}
675 isCaretOnFirstLine: function()
677 var selection = this._element.getComponentSelection();
678 var focusNode = selection.focusNode;
679 if (!focusNode || focusNode.nodeType !== Node.TEXT_NODE || focusNode.parentNode !== this._element)
680 return true;
682 if (focusNode.textContent.substring(0, selection.focusOffset).indexOf("\n") !== -1)
683 return false;
684 focusNode = focusNode.previousSibling;
686 while (focusNode) {
687 if (focusNode.nodeType !== Node.TEXT_NODE)
688 return true;
689 if (focusNode.textContent.indexOf("\n") !== -1)
690 return false;
691 focusNode = focusNode.previousSibling;
694 return true;
698 * @return {boolean}
700 isCaretOnLastLine: function()
702 var selection = this._element.getComponentSelection();
703 var focusNode = selection.focusNode;
704 if (!focusNode || focusNode.nodeType !== Node.TEXT_NODE || focusNode.parentNode !== this._element)
705 return true;
707 if (focusNode.textContent.substring(selection.focusOffset).indexOf("\n") !== -1)
708 return false;
709 focusNode = focusNode.nextSibling;
711 while (focusNode) {
712 if (focusNode.nodeType !== Node.TEXT_NODE)
713 return true;
714 if (focusNode.textContent.indexOf("\n") !== -1)
715 return false;
716 focusNode = focusNode.nextSibling;
719 return true;
722 moveCaretToEndOfPrompt: function()
724 var selection = this._element.getComponentSelection();
725 var selectionRange = this._createRange();
727 var container = this._element;
728 while (container.childNodes.length)
729 container = container.lastChild;
730 var offset = container.nodeType === Node.TEXT_NODE ? container.textContent.length : 0;
731 selectionRange.setStart(container, offset);
732 selectionRange.setEnd(container, offset);
734 selection.removeAllRanges();
735 selection.addRange(selectionRange);
739 * @param {!Event} event
740 * @return {boolean}
742 tabKeyPressed: function(event)
744 this._completeCommonPrefix();
746 // Consume the key.
747 return true;
751 * @return {?Element}
753 proxyElementForTests: function()
755 return this._proxyElement || null;
758 __proto__: WebInspector.Object.prototype
763 * @constructor
764 * @extends {WebInspector.TextPrompt}
765 * @param {function(!Element, !Range, boolean, function(!Array.<string>, number=))} completions
766 * @param {string=} stopCharacters
768 WebInspector.TextPromptWithHistory = function(completions, stopCharacters)
770 WebInspector.TextPrompt.call(this, completions, stopCharacters);
773 * @type {!Array.<string>}
775 this._data = [];
778 * 1-based entry in the history stack.
779 * @type {number}
781 this._historyOffset = 1;
784 WebInspector.TextPromptWithHistory.prototype = {
786 * @return {!Array.<string>}
788 historyData: function()
790 // FIXME: do we need to copy this?
791 return this._data;
795 * @param {!Array.<string>} data
797 setHistoryData: function(data)
799 this._data = [].concat(data);
800 this._historyOffset = 1;
804 * Pushes a committed text into the history.
805 * @param {string} text
807 pushHistoryItem: function(text)
809 if (this._uncommittedIsTop) {
810 this._data.pop();
811 delete this._uncommittedIsTop;
814 this._historyOffset = 1;
815 if (text === this._currentHistoryItem())
816 return;
817 this._data.push(text);
821 * Pushes the current (uncommitted) text into the history.
823 _pushCurrentText: function()
825 if (this._uncommittedIsTop)
826 this._data.pop(); // Throw away obsolete uncommitted text.
827 this._uncommittedIsTop = true;
828 this.clearAutoComplete(true);
829 this._data.push(this.text());
833 * @return {string|undefined}
835 _previous: function()
837 if (this._historyOffset > this._data.length)
838 return undefined;
839 if (this._historyOffset === 1)
840 this._pushCurrentText();
841 ++this._historyOffset;
842 return this._currentHistoryItem();
846 * @return {string|undefined}
848 _next: function()
850 if (this._historyOffset === 1)
851 return undefined;
852 --this._historyOffset;
853 return this._currentHistoryItem();
857 * @return {string|undefined}
859 _currentHistoryItem: function()
861 return this._data[this._data.length - this._historyOffset];
865 * @override
867 onKeyDown: function(event)
869 var newText;
870 var isPrevious;
872 switch (event.keyIdentifier) {
873 case "Up":
874 if (!this.isCaretOnFirstLine() || this.isSuggestBoxVisible())
875 break;
876 newText = this._previous();
877 isPrevious = true;
878 break;
879 case "Down":
880 if (!this.isCaretOnLastLine() || this.isSuggestBoxVisible())
881 break;
882 newText = this._next();
883 break;
884 case "U+0050": // Ctrl+P = Previous
885 if (WebInspector.isMac() && event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey) {
886 newText = this._previous();
887 isPrevious = true;
889 break;
890 case "U+004E": // Ctrl+N = Next
891 if (WebInspector.isMac() && event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey)
892 newText = this._next();
893 break;
896 if (newText !== undefined) {
897 event.consume(true);
898 this.setText(newText);
900 if (isPrevious) {
901 var firstNewlineIndex = this.text().indexOf("\n");
902 if (firstNewlineIndex === -1)
903 this.moveCaretToEndOfPrompt();
904 else {
905 var selection = this._element.getComponentSelection();
906 var selectionRange = this._createRange();
908 selectionRange.setStart(this._element.firstChild, firstNewlineIndex);
909 selectionRange.setEnd(this._element.firstChild, firstNewlineIndex);
911 selection.removeAllRanges();
912 selection.addRange(selectionRange);
916 return;
919 WebInspector.TextPrompt.prototype.onKeyDown.apply(this, arguments);
922 __proto__: WebInspector.TextPrompt.prototype