https://bugs.webkit.org/show_bug.cgi?id=109430
Patch by Andrey Lushnikov <lushnikov@chromium.org> on 2013-02-12
Reviewed by Alexander Pavlov.
Create WebInspector.SuggestBoxDelegate interface and
refactor TextPrompt to use this interface. Separate SuggestBox into
WebInspector.SuggestBox namespace and put it into its own file.
No new tests: no change in behaviour.
* WebCore.gypi:
* WebCore.vcproj/WebCore.vcproj:
* inspector/compile-front-end.py:
* inspector/front-end/SuggestBox.js: Added.
(WebInspector.SuggestBoxDelegate):
(WebInspector.SuggestBoxDelegate.prototype.applySuggestion):
(WebInspector.SuggestBoxDelegate.prototype.acceptSuggestion):
(WebInspector.SuggestBoxDelegate.prototype.userEnteredText):
(WebInspector.SuggestBox):
(WebInspector.SuggestBox.prototype.get visible):
(WebInspector.SuggestBox.prototype.get hasSelection):
(WebInspector.SuggestBox.prototype._onscrollresize):
(WebInspector.SuggestBox.prototype._updateBoxPositionWithExistingAnchor):
(WebInspector.SuggestBox.prototype._updateBoxPosition):
(WebInspector.SuggestBox.prototype._onboxmousedown):
(WebInspector.SuggestBox.prototype.hide):
(WebInspector.SuggestBox.prototype.removeFromElement):
(WebInspector.SuggestBox.prototype._applySuggestion):
(WebInspector.SuggestBox.prototype.acceptSuggestion):
(WebInspector.SuggestBox.prototype._selectClosest):
(WebInspector.SuggestBox.prototype.updateSuggestions):
(WebInspector.SuggestBox.prototype._onItemMouseDown):
(WebInspector.SuggestBox.prototype._createItemElement):
(WebInspector.SuggestBox.prototype._updateItems):
(WebInspector.SuggestBox.prototype._selectItem):
(WebInspector.SuggestBox.prototype._canShowBox):
(WebInspector.SuggestBox.prototype._rememberRowCountPerViewport):
(WebInspector.SuggestBox.prototype._completionsReady):
(WebInspector.SuggestBox.prototype.upKeyPressed):
(WebInspector.SuggestBox.prototype.downKeyPressed):
(WebInspector.SuggestBox.prototype.pageUpKeyPressed):
(WebInspector.SuggestBox.prototype.pageDownKeyPressed):
(WebInspector.SuggestBox.prototype.enterKeyPressed):
(WebInspector.SuggestBox.prototype.tabKeyPressed):
* inspector/front-end/TextPrompt.js:
(WebInspector.TextPrompt.prototype.userEnteredText):
(WebInspector.TextPrompt.prototype._attachInternal):
(WebInspector.TextPrompt.prototype._completionsReady):
(WebInspector.TextPrompt.prototype.applySuggestion):
(WebInspector.TextPrompt.prototype._applySuggestion):
(WebInspector.TextPrompt.prototype.enterKeyPressed):
(WebInspector.TextPrompt.prototype.upKeyPressed):
(WebInspector.TextPrompt.prototype.downKeyPressed):
(WebInspector.TextPrompt.prototype.pageUpKeyPressed):
(WebInspector.TextPrompt.prototype.pageDownKeyPressed):
* inspector/front-end/WebKit.qrc:
* inspector/front-end/inspector.html:
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@142610
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2013-02-12 Andrey Lushnikov <lushnikov@chromium.org>
+
+ Web Inspector: separate SuggestBox from TextPrompt
+ https://bugs.webkit.org/show_bug.cgi?id=109430
+
+ Reviewed by Alexander Pavlov.
+
+ Create WebInspector.SuggestBoxDelegate interface and
+ refactor TextPrompt to use this interface. Separate SuggestBox into
+ WebInspector.SuggestBox namespace and put it into its own file.
+
+ No new tests: no change in behaviour.
+
+ * WebCore.gypi:
+ * WebCore.vcproj/WebCore.vcproj:
+ * inspector/compile-front-end.py:
+ * inspector/front-end/SuggestBox.js: Added.
+ (WebInspector.SuggestBoxDelegate):
+ (WebInspector.SuggestBoxDelegate.prototype.applySuggestion):
+ (WebInspector.SuggestBoxDelegate.prototype.acceptSuggestion):
+ (WebInspector.SuggestBoxDelegate.prototype.userEnteredText):
+ (WebInspector.SuggestBox):
+ (WebInspector.SuggestBox.prototype.get visible):
+ (WebInspector.SuggestBox.prototype.get hasSelection):
+ (WebInspector.SuggestBox.prototype._onscrollresize):
+ (WebInspector.SuggestBox.prototype._updateBoxPositionWithExistingAnchor):
+ (WebInspector.SuggestBox.prototype._updateBoxPosition):
+ (WebInspector.SuggestBox.prototype._onboxmousedown):
+ (WebInspector.SuggestBox.prototype.hide):
+ (WebInspector.SuggestBox.prototype.removeFromElement):
+ (WebInspector.SuggestBox.prototype._applySuggestion):
+ (WebInspector.SuggestBox.prototype.acceptSuggestion):
+ (WebInspector.SuggestBox.prototype._selectClosest):
+ (WebInspector.SuggestBox.prototype.updateSuggestions):
+ (WebInspector.SuggestBox.prototype._onItemMouseDown):
+ (WebInspector.SuggestBox.prototype._createItemElement):
+ (WebInspector.SuggestBox.prototype._updateItems):
+ (WebInspector.SuggestBox.prototype._selectItem):
+ (WebInspector.SuggestBox.prototype._canShowBox):
+ (WebInspector.SuggestBox.prototype._rememberRowCountPerViewport):
+ (WebInspector.SuggestBox.prototype._completionsReady):
+ (WebInspector.SuggestBox.prototype.upKeyPressed):
+ (WebInspector.SuggestBox.prototype.downKeyPressed):
+ (WebInspector.SuggestBox.prototype.pageUpKeyPressed):
+ (WebInspector.SuggestBox.prototype.pageDownKeyPressed):
+ (WebInspector.SuggestBox.prototype.enterKeyPressed):
+ (WebInspector.SuggestBox.prototype.tabKeyPressed):
+ * inspector/front-end/TextPrompt.js:
+ (WebInspector.TextPrompt.prototype.userEnteredText):
+ (WebInspector.TextPrompt.prototype._attachInternal):
+ (WebInspector.TextPrompt.prototype._completionsReady):
+ (WebInspector.TextPrompt.prototype.applySuggestion):
+ (WebInspector.TextPrompt.prototype._applySuggestion):
+ (WebInspector.TextPrompt.prototype.enterKeyPressed):
+ (WebInspector.TextPrompt.prototype.upKeyPressed):
+ (WebInspector.TextPrompt.prototype.downKeyPressed):
+ (WebInspector.TextPrompt.prototype.pageUpKeyPressed):
+ (WebInspector.TextPrompt.prototype.pageDownKeyPressed):
+ * inspector/front-end/WebKit.qrc:
+ * inspector/front-end/inspector.html:
+
2013-02-12 Bruno de Oliveira Abinader <bruno.abinader@basyskom.com>
[TexMap] Apply frames-per-second debug counter to WK1.
'inspector/front-end/SplitView.js',
'inspector/front-end/StatusBarButton.js',
'inspector/front-end/StylesSourceMapping.js',
+ 'inspector/front-end/SuggestBox.js',
'inspector/front-end/TabbedPane.js',
'inspector/front-end/TestController.js',
'inspector/front-end/TextEditor.js',
RelativePath="..\inspector\front-end\textPrompt.css"
>
</File>
+ <File
+ RelativePath="..\inspector\front-end\SuggestBox.js"
+ >
+ </File>
<File
RelativePath="..\inspector\front-end\TextPrompt.js"
>
"SplitView.js",
"SidebarView.js",
"StatusBarButton.js",
+ "SuggestBox.js",
"TabbedPane.js",
"TextEditor.js",
"TextEditorHighlighter.js",
--- /dev/null
+/*
+ * Copyright (C) 2013 Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER OR 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.
+ */
+
+/**
+ * @interface
+ */
+WebInspector.SuggestBoxDelegate = function()
+{
+}
+
+WebInspector.SuggestBoxDelegate.prototype = {
+ /**
+ * @param {string} suggestion
+ * @param {boolean=} isIntermediateSuggestion
+ */
+ applySuggestion: function(suggestion, isIntermediateSuggestion) { },
+
+ /**
+ * acceptSuggestion will be always called after call to applySuggestion with isIntermediateSuggestion being equal to false.
+ */
+ acceptSuggestion: function() { },
+
+ /**
+ * @return {string}
+ */
+ userEnteredText: function() { }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.SuggestBoxDelegate} suggestBoxDelegate
+ * @param {Element} inputElement
+ * @param {string} className
+ */
+WebInspector.SuggestBox = function(suggestBoxDelegate, inputElement, className)
+{
+ this._suggestBoxDelegate = suggestBoxDelegate;
+ this._inputElement = inputElement;
+ this._length = 0;
+ this._selectedIndex = -1;
+ this._selectedElement = null;
+ this._boundOnScroll = this._onscrollresize.bind(this, true);
+ this._boundOnResize = this._onscrollresize.bind(this, false);
+ window.addEventListener("scroll", this._boundOnScroll, true);
+ window.addEventListener("resize", this._boundOnResize, true);
+
+ this._bodyElement = inputElement.ownerDocument.body;
+ this._element = inputElement.ownerDocument.createElement("div");
+ this._element.className = "suggest-box " + (className || "");
+ this._element.addEventListener("mousedown", this._onboxmousedown.bind(this), true);
+ this.containerElement = this._element.createChild("div", "container");
+ this.contentElement = this.containerElement.createChild("div", "content");
+}
+
+WebInspector.SuggestBox.prototype = {
+ get visible()
+ {
+ return !!this._element.parentElement;
+ },
+
+ get hasSelection()
+ {
+ return !!this._selectedElement;
+ },
+
+ _onscrollresize: function(isScroll, event)
+ {
+ if (isScroll && this._element.isAncestor(event.target) || !this.visible)
+ return;
+ this._updateBoxPositionWithExistingAnchor();
+ },
+
+ _updateBoxPositionWithExistingAnchor: function()
+ {
+ this._updateBoxPosition(this._anchorBox);
+ },
+
+ /**
+ * @param {AnchorBox} anchorBox
+ */
+ _updateBoxPosition: function(anchorBox)
+ {
+ // Measure the content element box.
+ this.contentElement.style.display = "inline-block";
+ document.body.appendChild(this.contentElement);
+ this.contentElement.positionAt(0, 0);
+ var contentWidth = this.contentElement.offsetWidth;
+ var contentHeight = this.contentElement.offsetHeight;
+ this.contentElement.style.display = "block";
+ this.containerElement.appendChild(this.contentElement);
+
+ // Lay out the suggest-box relative to the anchorBox.
+ this._anchorBox = anchorBox;
+ const spacer = 6;
+
+ const suggestBoxPaddingX = 21;
+ var maxWidth = document.body.offsetWidth - anchorBox.x - spacer;
+ var width = Math.min(contentWidth, maxWidth - suggestBoxPaddingX) + suggestBoxPaddingX;
+ var paddedWidth = contentWidth + suggestBoxPaddingX;
+ var boxX = anchorBox.x;
+ if (width < paddedWidth) {
+ // Shift the suggest box to the left to accommodate the content without trimming to the BODY edge.
+ maxWidth = document.body.offsetWidth - spacer;
+ width = Math.min(contentWidth, maxWidth - suggestBoxPaddingX) + suggestBoxPaddingX;
+ boxX = document.body.offsetWidth - width;
+ }
+
+ const suggestBoxPaddingY = 2;
+ var boxY;
+ var aboveHeight = anchorBox.y;
+ var underHeight = document.body.offsetHeight - anchorBox.y - anchorBox.height;
+ var maxHeight = Math.max(underHeight, aboveHeight) - spacer;
+ var height = Math.min(contentHeight, maxHeight - suggestBoxPaddingY) + suggestBoxPaddingY;
+ if (underHeight >= aboveHeight) {
+ // Locate the suggest box under the anchorBox.
+ boxY = anchorBox.y + anchorBox.height;
+ this._element.removeStyleClass("above-anchor");
+ this._element.addStyleClass("under-anchor");
+ } else {
+ // Locate the suggest box above the anchorBox.
+ boxY = anchorBox.y - height;
+ this._element.removeStyleClass("under-anchor");
+ this._element.addStyleClass("above-anchor");
+ }
+
+ this._element.positionAt(boxX, boxY);
+ this._element.style.width = width + "px";
+ this._element.style.height = height + "px";
+ },
+
+ _onboxmousedown: function(event)
+ {
+ event.preventDefault();
+ },
+
+ hide: function()
+ {
+ if (!this.visible)
+ return;
+
+ this._element.parentElement.removeChild(this._element);
+ delete this._selectedElement;
+ },
+
+ removeFromElement: function()
+ {
+ window.removeEventListener("scroll", this._boundOnScroll, true);
+ window.removeEventListener("resize", this._boundOnResize, true);
+ this.hide();
+ },
+
+ /**
+ * @param {string=} text
+ * @param {boolean=} isIntermediateSuggestion
+ */
+ _applySuggestion: function(text, isIntermediateSuggestion)
+ {
+ if (!this.visible || !(text || this._selectedElement))
+ return false;
+
+ var suggestion = text || this._selectedElement.textContent;
+ if (!suggestion)
+ return false;
+
+ this._suggestBoxDelegate.applySuggestion(suggestion, isIntermediateSuggestion);
+ return true;
+ },
+
+ /**
+ * @param {string=} text
+ */
+ acceptSuggestion: function(text)
+ {
+ var result = this._applySuggestion(text, false);
+ this.hide();
+ if (!result)
+ return false;
+
+ this._suggestBoxDelegate.acceptSuggestion();
+
+ return true;
+ },
+
+ /**
+ * @param {number} shift
+ * @param {boolean=} isCircular
+ * @return {boolean} is changed
+ */
+ _selectClosest: function(shift, isCircular)
+ {
+ if (!this._length)
+ return false;
+
+ var index = this._selectedIndex + shift;
+
+ if (isCircular)
+ index = (this._length + index) % this._length;
+ else
+ index = Number.constrain(index, 0, this._length - 1);
+
+ this._selectItem(index);
+ this._applySuggestion(undefined, true);
+ return true;
+ },
+
+ /**
+ * @param {AnchorBox} anchorBox
+ * @param {Array.<string>=} completions
+ * @param {number=} selectedIndex
+ * @param {boolean=} canShowForSingleItem
+ */
+ updateSuggestions: function(anchorBox, completions, selectedIndex, canShowForSingleItem)
+ {
+ if (this._suggestTimeout) {
+ clearTimeout(this._suggestTimeout);
+ delete this._suggestTimeout;
+ }
+ this._completionsReady(anchorBox, completions, selectedIndex, canShowForSingleItem);
+ },
+
+ _onItemMouseDown: function(text, event)
+ {
+ this.acceptSuggestion(text);
+ event.consume(true);
+ },
+
+ _createItemElement: function(prefix, text)
+ {
+ var element = document.createElement("div");
+ element.className = "suggest-box-content-item source-code";
+ element.tabIndex = -1;
+ if (prefix && prefix.length && !text.indexOf(prefix)) {
+ var prefixElement = element.createChild("span", "prefix");
+ prefixElement.textContent = prefix;
+ var suffixElement = element.createChild("span", "suffix");
+ suffixElement.textContent = text.substring(prefix.length);
+ } else {
+ var suffixElement = element.createChild("span", "suffix");
+ suffixElement.textContent = text;
+ }
+ element.addEventListener("mousedown", this._onItemMouseDown.bind(this, text), false);
+ return element;
+ },
+
+ /**
+ * @param {Array.<string>=} items
+ * @param {number=} selectedIndex
+ */
+ _updateItems: function(items, selectedIndex)
+ {
+ this._length = items.length;
+ this.contentElement.removeChildren();
+
+ var userEnteredText = this._suggestBoxDelegate.userEnteredText();
+ for (var i = 0; i < items.length; ++i) {
+ var item = items[i];
+ var currentItemElement = this._createItemElement(userEnteredText, item);
+ this.contentElement.appendChild(currentItemElement);
+ }
+
+ this._selectedElement = null;
+ if (typeof selectedIndex === "number")
+ this._selectItem(selectedIndex);
+ },
+
+ /**
+ * @param {number} index
+ */
+ _selectItem: function(index)
+ {
+ if (this._selectedElement)
+ this._selectedElement.classList.remove("selected");
+
+ this._selectedIndex = index;
+ this._selectedElement = this.contentElement.children[index];
+ this._selectedElement.classList.add("selected");
+
+ this._selectedElement.scrollIntoViewIfNeeded(false);
+ },
+
+ /**
+ * @param {Array.<string>=} completions
+ * @param {boolean=} canShowForSingleItem
+ */
+ _canShowBox: function(completions, canShowForSingleItem)
+ {
+ if (!completions || !completions.length)
+ return false;
+
+ if (completions.length > 1)
+ return true;
+
+ // Do not show a single suggestion if it is the same as user-entered prefix, even if allowed to show single-item suggest boxes.
+ return canShowForSingleItem && completions[0] !== this._suggestBoxDelegate.userEnteredText();
+ },
+
+ _rememberRowCountPerViewport: function()
+ {
+ if (!this.contentElement.firstChild)
+ return;
+
+ this._rowCountPerViewport = Math.floor(this.containerElement.offsetHeight / this.contentElement.firstChild.offsetHeight);
+ },
+
+ /**
+ * @param {AnchorBox} anchorBox
+ * @param {Array.<string>=} completions
+ * @param {number=} selectedIndex
+ * @param {boolean=} canShowForSingleItem
+ */
+ _completionsReady: function(anchorBox, completions, selectedIndex, canShowForSingleItem)
+ {
+ if (this._canShowBox(completions, canShowForSingleItem)) {
+ this._updateItems(completions, selectedIndex);
+ this._updateBoxPosition(anchorBox);
+ if (!this.visible)
+ this._bodyElement.appendChild(this._element);
+ this._rememberRowCountPerViewport();
+ } else
+ this.hide();
+ },
+
+ /**
+ * @return {boolean}
+ */
+ upKeyPressed: function()
+ {
+ return this._selectClosest(-1, true);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ downKeyPressed: function()
+ {
+ return this._selectClosest(1, true);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ pageUpKeyPressed: function()
+ {
+ return this._selectClosest(-this._rowCountPerViewport, false);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ pageDownKeyPressed: function()
+ {
+ return this._selectClosest(this._rowCountPerViewport, false);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ enterKeyPressed: function()
+ {
+ var hasSelectedItem = !!this._selectedElement;
+ this.acceptSuggestion();
+
+ // Report the event as non-handled if there is no selected item,
+ // to commit the input or handle it otherwise.
+ return hasSelectedItem;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ tabKeyPressed: function()
+ {
+ return this.enterKeyPressed();
+ }
+}
/**
* @constructor
* @extends WebInspector.Object
+ * @implements {WebInspector.SuggestBoxDelegate}
* @param {function(Element, Range, boolean, function(Array.<string>, number=))} completions
* @param {string=} stopCharacters
*/
};
WebInspector.TextPrompt.prototype = {
+ userEnteredText: function()
+ {
+ return this._userEnteredText;
+ },
+
get proxyElement()
{
return this._proxyElement;
this._element.addEventListener("selectstart", this._boundSelectStart, false);
if (typeof this._suggestBoxClassName === "string")
- this._suggestBox = new WebInspector.TextPrompt.SuggestBox(this, this._element, this._suggestBoxClassName);
+ this._suggestBox = new WebInspector.SuggestBox(this, this._element, this._suggestBoxClassName);
return this.proxyElement;
},
selection.addRange(finalSelectionRange);
}
} else
- this.applySuggestion(completionText, completions.length > 1, originalWordPrefixRange);
+ this._applySuggestion(completionText, completions.length > 1, originalWordPrefixRange);
},
_completeCommonPrefix: function()
},
/**
- * @param {Range=} originalPrefixRange
+ * @param {string} completionText
+ * @param {boolean=} isIntermediateSuggestion
+ */
+ applySuggestion: function(completionText, isIntermediateSuggestion)
+ {
+ this._applySuggestion(completionText, isIntermediateSuggestion);
+ },
+
+ /**
+ * @param {string} completionText
+ * @param {boolean=} isIntermediateSuggestion
+ * @param {Range} originalPrefixRange
*/
- applySuggestion: function(completionText, isIntermediateSuggestion, originalPrefixRange)
+ _applySuggestion: function(completionText, isIntermediateSuggestion, originalPrefixRange)
{
var wordPrefixLength;
if (originalPrefixRange)
enterKeyPressed: function(event)
{
if (this.isSuggestBoxVisible())
- return this._suggestBox.enterKeyPressed(event);
+ return this._suggestBox.enterKeyPressed();
return false;
},
upKeyPressed: function(event)
{
if (this.isSuggestBoxVisible())
- return this._suggestBox.upKeyPressed(event);
+ return this._suggestBox.upKeyPressed();
return false;
},
downKeyPressed: function(event)
{
if (this.isSuggestBoxVisible())
- return this._suggestBox.downKeyPressed(event);
+ return this._suggestBox.downKeyPressed();
return false;
},
pageUpKeyPressed: function(event)
{
if (this.isSuggestBoxVisible())
- return this._suggestBox.pageUpKeyPressed(event);
+ return this._suggestBox.pageUpKeyPressed();
return false;
},
pageDownKeyPressed: function(event)
{
if (this.isSuggestBoxVisible())
- return this._suggestBox.pageDownKeyPressed(event);
+ return this._suggestBox.pageDownKeyPressed();
return false;
},
__proto__: WebInspector.TextPrompt.prototype
}
-/**
- * @constructor
- */
-WebInspector.TextPrompt.SuggestBox = function(textPrompt, inputElement, className)
-{
- this._textPrompt = textPrompt;
- this._inputElement = inputElement;
- this._length = 0;
- this._selectedIndex = -1;
- this._selectedElement = null;
- this._boundOnScroll = this._onscrollresize.bind(this, true);
- this._boundOnResize = this._onscrollresize.bind(this, false);
- window.addEventListener("scroll", this._boundOnScroll, true);
- window.addEventListener("resize", this._boundOnResize, true);
-
- this._bodyElement = inputElement.ownerDocument.body;
- this._element = inputElement.ownerDocument.createElement("div");
- this._element.className = "suggest-box " + (className || "");
- this._element.addEventListener("mousedown", this._onboxmousedown.bind(this), true);
- this.containerElement = this._element.createChild("div", "container");
- this.contentElement = this.containerElement.createChild("div", "content");
-}
-
-WebInspector.TextPrompt.SuggestBox.prototype = {
- get visible()
- {
- return !!this._element.parentElement;
- },
-
- get hasSelection()
- {
- return !!this._selectedElement;
- },
-
- _onscrollresize: function(isScroll, event)
- {
- if (isScroll && this._element.isAncestor(event.target) || !this.visible)
- return;
- this._updateBoxPositionWithExistingAnchor();
- },
-
- _updateBoxPositionWithExistingAnchor: function()
- {
- this._updateBoxPosition(this._anchorBox);
- },
-
- /**
- * @param {AnchorBox} anchorBox
- */
- _updateBoxPosition: function(anchorBox)
- {
- // Measure the content element box.
- this.contentElement.style.display = "inline-block";
- document.body.appendChild(this.contentElement);
- this.contentElement.positionAt(0, 0);
- var contentWidth = this.contentElement.offsetWidth;
- var contentHeight = this.contentElement.offsetHeight;
- this.contentElement.style.display = "block";
- this.containerElement.appendChild(this.contentElement);
-
- // Lay out the suggest-box relative to the anchorBox.
- this._anchorBox = anchorBox;
- const spacer = 6;
-
- const suggestBoxPaddingX = 21;
- var maxWidth = document.body.offsetWidth - anchorBox.x - spacer;
- var width = Math.min(contentWidth, maxWidth - suggestBoxPaddingX) + suggestBoxPaddingX;
- var paddedWidth = contentWidth + suggestBoxPaddingX;
- var boxX = anchorBox.x;
- if (width < paddedWidth) {
- // Shift the suggest box to the left to accommodate the content without trimming to the BODY edge.
- maxWidth = document.body.offsetWidth - spacer;
- width = Math.min(contentWidth, maxWidth - suggestBoxPaddingX) + suggestBoxPaddingX;
- boxX = document.body.offsetWidth - width;
- }
-
- const suggestBoxPaddingY = 2;
- var boxY;
- var aboveHeight = anchorBox.y;
- var underHeight = document.body.offsetHeight - anchorBox.y - anchorBox.height;
- var maxHeight = Math.max(underHeight, aboveHeight) - spacer;
- var height = Math.min(contentHeight, maxHeight - suggestBoxPaddingY) + suggestBoxPaddingY;
- if (underHeight >= aboveHeight) {
- // Locate the suggest box under the anchorBox.
- boxY = anchorBox.y + anchorBox.height;
- this._element.removeStyleClass("above-anchor");
- this._element.addStyleClass("under-anchor");
- } else {
- // Locate the suggest box above the anchorBox.
- boxY = anchorBox.y - height;
- this._element.removeStyleClass("under-anchor");
- this._element.addStyleClass("above-anchor");
- }
-
- this._element.positionAt(boxX, boxY);
- this._element.style.width = width + "px";
- this._element.style.height = height + "px";
- },
-
- _onboxmousedown: function(event)
- {
- event.preventDefault();
- },
-
- hide: function()
- {
- if (!this.visible)
- return;
-
- this._element.parentElement.removeChild(this._element);
- delete this._selectedElement;
- },
-
- removeFromElement: function()
- {
- window.removeEventListener("scroll", this._boundOnScroll, true);
- window.removeEventListener("resize", this._boundOnResize, true);
- this.hide();
- },
-
- /**
- * @param {string=} text
- * @param {boolean=} isIntermediateSuggestion
- */
- _applySuggestion: function(text, isIntermediateSuggestion)
- {
- if (!this.visible || !(text || this._selectedElement))
- return false;
-
- var suggestion = text || this._selectedElement.textContent;
- if (!suggestion)
- return false;
-
- this._textPrompt.applySuggestion(suggestion, isIntermediateSuggestion);
- return true;
- },
-
- /**
- * @param {string=} text
- */
- acceptSuggestion: function(text)
- {
- var result = this._applySuggestion(text, false);
- this.hide();
- if (!result)
- return false;
-
- this._textPrompt.acceptSuggestion();
-
- return true;
- },
-
- /**
- * @param {number} shift
- * @param {boolean=} isCircular
- * @return {boolean} is changed
- */
- _selectClosest: function(shift, isCircular)
- {
- if (!this._length)
- return false;
-
- var index = this._selectedIndex + shift;
-
- if (isCircular)
- index = (this._length + index) % this._length;
- else
- index = Number.constrain(index, 0, this._length - 1);
-
- this._selectItem(index);
- this._applySuggestion(undefined, true);
- return true;
- },
-
- /**
- * @param {AnchorBox} anchorBox
- * @param {Array.<string>=} completions
- * @param {number=} selectedIndex
- * @param {boolean=} canShowForSingleItem
- */
- updateSuggestions: function(anchorBox, completions, selectedIndex, canShowForSingleItem)
- {
- if (this._suggestTimeout) {
- clearTimeout(this._suggestTimeout);
- delete this._suggestTimeout;
- }
- this._completionsReady(anchorBox, completions, selectedIndex, canShowForSingleItem);
- },
-
- _onItemMouseDown: function(text, event)
- {
- this.acceptSuggestion(text);
- event.consume(true);
- },
-
- _createItemElement: function(prefix, text)
- {
- var element = document.createElement("div");
- element.className = "suggest-box-content-item source-code";
- element.tabIndex = -1;
- if (prefix && prefix.length && !text.indexOf(prefix)) {
- var prefixElement = element.createChild("span", "prefix");
- prefixElement.textContent = prefix;
- var suffixElement = element.createChild("span", "suffix");
- suffixElement.textContent = text.substring(prefix.length);
- } else {
- var suffixElement = element.createChild("span", "suffix");
- suffixElement.textContent = text;
- }
- element.addEventListener("mousedown", this._onItemMouseDown.bind(this, text), false);
- return element;
- },
-
- /**
- * @param {Array.<string>=} items
- * @param {number=} selectedIndex
- */
- _updateItems: function(items, selectedIndex)
- {
- this._length = items.length;
- this.contentElement.removeChildren();
-
- var userEnteredText = this._textPrompt._userEnteredText;
- for (var i = 0; i < items.length; ++i) {
- var item = items[i];
- var currentItemElement = this._createItemElement(userEnteredText, item);
- this.contentElement.appendChild(currentItemElement);
- }
-
- this._selectedElement = null;
- if (typeof selectedIndex === "number")
- this._selectItem(selectedIndex);
- },
-
- /**
- * @param {number} index
- */
- _selectItem: function(index)
- {
- if (this._selectedElement)
- this._selectedElement.classList.remove("selected");
-
- this._selectedIndex = index;
- this._selectedElement = this.contentElement.children[index];
- this._selectedElement.classList.add("selected");
-
- this._selectedElement.scrollIntoViewIfNeeded(false);
- },
-
- /**
- * @param {Array.<string>=} completions
- * @param {boolean=} canShowForSingleItem
- */
- _canShowBox: function(completions, canShowForSingleItem)
- {
- if (!completions || !completions.length)
- return false;
-
- if (completions.length > 1)
- return true;
-
- // Do not show a single suggestion if it is the same as user-entered prefix, even if allowed to show single-item suggest boxes.
- return canShowForSingleItem && completions[0] !== this._textPrompt._userEnteredText;
- },
-
- _rememberRowCountPerViewport: function()
- {
- if (!this.contentElement.firstChild)
- return;
-
- this._rowCountPerViewport = Math.floor(this.containerElement.offsetHeight / this.contentElement.firstChild.offsetHeight);
- },
-
- /**
- * @param {AnchorBox} anchorBox
- * @param {Array.<string>=} completions
- * @param {number=} selectedIndex
- * @param {boolean=} canShowForSingleItem
- */
- _completionsReady: function(anchorBox, completions, selectedIndex, canShowForSingleItem)
- {
- if (this._canShowBox(completions, canShowForSingleItem)) {
- this._updateItems(completions, selectedIndex);
- this._updateBoxPosition(anchorBox);
- if (!this.visible)
- this._bodyElement.appendChild(this._element);
- this._rememberRowCountPerViewport();
- } else
- this.hide();
- },
-
- upKeyPressed: function(event)
- {
- return this._selectClosest(-1, true);
- },
-
- downKeyPressed: function(event)
- {
- return this._selectClosest(1, true);
- },
-
- pageUpKeyPressed: function(event)
- {
- return this._selectClosest(-this._rowCountPerViewport, false);
- },
-
- pageDownKeyPressed: function(event)
- {
- return this._selectClosest(this._rowCountPerViewport, false);
- },
-
- enterKeyPressed: function(event)
- {
- var hasSelectedItem = !!this._selectedElement;
- this.acceptSuggestion();
-
- // Report the event as non-handled if there is no selected item,
- // to commit the input or handle it otherwise.
- return hasSelectedItem;
- },
-
- tabKeyPressed: function(event)
- {
- return this.enterKeyPressed(event);
- }
-}
<file>StyleSheetOutlineDialog.js</file>
<file>StylesSourceMapping.js</file>
<file>StylesSidebarPane.js</file>
+ <file>SuggestBox.js</file>
<file>TabbedEditorContainer.js</file>
<file>TabbedPane.js</file>
<file>TestController.js</file>
<script type="text/javascript" src="ContextMenu.js"></script>
<script type="text/javascript" src="SoftContextMenu.js"></script>
<script type="text/javascript" src="KeyboardShortcut.js"></script>
+ <script type="text/javascript" src="SuggestBox.js"></script>
<script type="text/javascript" src="TextPrompt.js"></script>
<script type="text/javascript" src="Popover.js"></script>
<script type="text/javascript" src="Placard.js"></script>