123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- /*
- * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
- * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com).
- * Copyright (C) 2009 Joseph Pecoraro
- * Copyright (C) 2011 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:
- *
- * 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.
- */
- /**
- * @constructor
- */
- WebInspector.SearchController = function()
- {
- this._element = document.createElement("table");
- this._element.className = "toolbar-search";
- this._element.cellSpacing = 0;
- this._firstRowElement = this._element.createChild("tr");
- this._secondRowElement = this._element.createChild("tr", "hidden");
- // Column 1
- var searchControlElementColumn = this._firstRowElement.createChild("td");
- this._searchControlElement = searchControlElementColumn.createChild("span", "toolbar-search-control");
- this._searchInputElement = this._searchControlElement.createChild("input", "search-replace");
- this._searchInputElement.id = "search-input-field";
- this._matchesElement = this._searchControlElement.createChild("label", "search-results-matches");
- this._matchesElement.setAttribute("for", "search-input-field");
- this._searchNavigationElement = this._searchControlElement.createChild("div", "toolbar-search-navigation-controls");
- this._toggleFilterUI(false);
- this._searchNavigationPrevElement = this._searchNavigationElement.createChild("div", "toolbar-search-navigation toolbar-search-navigation-prev");
- this._searchNavigationPrevElement.addEventListener("click", this._onPrevButtonSearch.bind(this), false);
- this._searchNavigationPrevElement.title = WebInspector.UIString("Search Previous");
- this._searchNavigationNextElement = this._searchNavigationElement.createChild("div", "toolbar-search-navigation toolbar-search-navigation-next");
- this._searchNavigationNextElement.addEventListener("click", this._onNextButtonSearch.bind(this), false);
- this._searchNavigationNextElement.title = WebInspector.UIString("Search Next");
- this._searchInputElement.addEventListener("mousedown", this._onSearchFieldManualFocus.bind(this), false); // when the search field is manually selected
- this._searchInputElement.addEventListener("keydown", this._onKeyDown.bind(this), true);
- this._searchInputElement.addEventListener("input", this._onInput.bind(this), false);
- this._replaceInputElement = this._secondRowElement.createChild("td").createChild("input", "search-replace toolbar-replace-control");
- this._replaceInputElement.addEventListener("keydown", this._onKeyDown.bind(this), true);
- this._replaceInputElement.placeholder = WebInspector.UIString("Replace");
- // Column 2
- this._findButtonElement = this._firstRowElement.createChild("td").createChild("button", "hidden");
- this._findButtonElement.textContent = WebInspector.UIString("Find");
- this._findButtonElement.tabIndex = -1;
- this._findButtonElement.addEventListener("click", this._onNextButtonSearch.bind(this), false);
- this._replaceButtonElement = this._secondRowElement.createChild("td").createChild("button");
- this._replaceButtonElement.textContent = WebInspector.UIString("Replace");
- this._replaceButtonElement.disabled = true;
- this._replaceButtonElement.tabIndex = -1;
- this._replaceButtonElement.addEventListener("click", this._replace.bind(this), false);
- // Column 3
- this._prevButtonElement = this._firstRowElement.createChild("td").createChild("button", "hidden");
- this._prevButtonElement.textContent = WebInspector.UIString("Previous");
- this._prevButtonElement.disabled = true;
- this._prevButtonElement.tabIndex = -1;
- this._prevButtonElement.addEventListener("click", this._onPrevButtonSearch.bind(this), false);
- this._replaceAllButtonElement = this._secondRowElement.createChild("td").createChild("button");
- this._replaceAllButtonElement.textContent = WebInspector.UIString("Replace All");
- this._replaceAllButtonElement.addEventListener("click", this._replaceAll.bind(this), false);
- // Column 4
- this._replaceElement = this._firstRowElement.createChild("td").createChild("span");
- this._replaceCheckboxElement = this._replaceElement.createChild("input");
- this._replaceCheckboxElement.type = "checkbox";
- this._replaceCheckboxElement.id = "search-replace-trigger";
- this._replaceCheckboxElement.addEventListener("click", this._updateSecondRowVisibility.bind(this), false);
- this._replaceLabelElement = this._replaceElement.createChild("label");
- this._replaceLabelElement.textContent = WebInspector.UIString("Replace");
- this._replaceLabelElement.setAttribute("for", "search-replace-trigger");
- // Column 5
- this._filterCheckboxContainer = this._firstRowElement.createChild("td").createChild("label");
- this._filterCheckboxContainer.setAttribute("for", "filter-trigger");
- this._filterCheckboxElement = this._filterCheckboxContainer.createChild("input");
- this._filterCheckboxElement.type = "checkbox";
- this._filterCheckboxElement.id = "filter-trigger";
- this._filterCheckboxElement.addEventListener("click", this._filterCheckboxClick.bind(this), false);
- this._filterCheckboxContainer.createTextChild(WebInspector.UIString("Filter"));
- // Column 6
- var cancelButtonElement = this._firstRowElement.createChild("td").createChild("button");
- cancelButtonElement.textContent = WebInspector.UIString("Cancel");
- cancelButtonElement.tabIndex = -1;
- cancelButtonElement.addEventListener("click", this.closeSearch.bind(this), false);
- }
- WebInspector.SearchController.prototype = {
- /**
- * @param {number} matches
- * @param {WebInspector.Searchable} provider
- */
- updateSearchMatchesCount: function(matches, provider)
- {
- provider.currentSearchMatches = matches;
- if (provider === this._searchProvider)
- this._updateSearchMatchesCountAndCurrentMatchIndex(provider.currentQuery ? matches : 0, -1);
- },
- /**
- * @param {number} currentMatchIndex
- * @param {WebInspector.Searchable} provider
- */
- updateCurrentMatchIndex: function(currentMatchIndex, provider)
- {
- if (provider === this._searchProvider)
- this._updateSearchMatchesCountAndCurrentMatchIndex(provider.currentSearchMatches, currentMatchIndex);
- },
- isSearchVisible: function()
- {
- return this._searchIsVisible;
- },
- closeSearch: function()
- {
- this.cancelSearch();
- WebInspector.setCurrentFocusElement(WebInspector.previousFocusElement());
- },
- cancelSearch: function()
- {
- if (!this._searchIsVisible)
- return;
- if (this._filterCheckboxElement.checked) {
- this._filterCheckboxElement.checked = false;
- this._toggleFilterUI(false);
- this.resetFilter();
- } else
- this.resetSearch();
- delete this._searchIsVisible;
- this._searchHost.setFooterElement(null);
- this.resetSearch();
- delete this._searchHost;
- delete this._searchProvider;
- },
- resetSearch: function()
- {
- this._clearSearch();
- this._updateReplaceVisibility();
- this._matchesElement.textContent = "";
- },
- /**
- * @param {Event} event
- * @return {boolean}
- */
- handleShortcut: function(event)
- {
- var isMac = WebInspector.isMac();
- switch (event.keyIdentifier) {
- case "U+0046": // F key
- if (isMac)
- var isFindKey = event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey;
- else
- var isFindKey = event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey;
- if (isFindKey) {
- this.showSearchField();
- event.consume(true);
- return true;
- }
- break;
- case "F3":
- if (!isMac) {
- this.showSearchField();
- event.consume(true);
- return true;
- }
- break;
- case "U+0047": // G key
- if (isMac && event.metaKey && !event.ctrlKey && !event.altKey && this._searchHost) {
- if (event.shiftKey)
- this._searchProvider.jumpToPreviousSearchResult();
- else
- this._searchProvider.jumpToNextSearchResult();
- event.consume(true);
- return true;
- }
- break;
- }
- return false;
- },
- /**
- * @param {boolean} enabled
- */
- _updateSearchNavigationButtonState: function(enabled)
- {
- this._replaceButtonElement.disabled = !enabled;
- this._prevButtonElement.disabled = !enabled;
- if (enabled) {
- this._searchNavigationPrevElement.addStyleClass("enabled");
- this._searchNavigationNextElement.addStyleClass("enabled");
- } else {
- this._searchNavigationPrevElement.removeStyleClass("enabled");
- this._searchNavigationNextElement.removeStyleClass("enabled");
- }
- },
- /**
- * @param {number} matches
- * @param {number} currentMatchIndex
- */
- _updateSearchMatchesCountAndCurrentMatchIndex: function(matches, currentMatchIndex)
- {
- if (!this._currentQuery)
- this._matchesElement.textContent = "";
- else if (matches === 0 || currentMatchIndex >= 0)
- this._matchesElement.textContent = WebInspector.UIString("%d of %d", currentMatchIndex + 1, matches);
- else if (matches === 1)
- this._matchesElement.textContent = WebInspector.UIString("1 match");
- else
- this._matchesElement.textContent = WebInspector.UIString("%d matches", matches);
- this._updateSearchNavigationButtonState(matches > 0);
- },
- showSearchField: function()
- {
- if (this._searchIsVisible)
- this.cancelSearch();
- if (WebInspector.drawer.element.isAncestor(document.activeElement) && WebInspector.drawer.getSearchProvider())
- this._searchHost = WebInspector.drawer;
- else
- this._searchHost = WebInspector.inspectorView;
- this._searchProvider = this._searchHost.getSearchProvider();
- this._searchHost.setFooterElement(this._element);
- this._updateReplaceVisibility();
- this._updateFilterVisibility();
- if (WebInspector.currentFocusElement() !== this._searchInputElement) {
- var selection = window.getSelection();
- if (selection.rangeCount) {
- var queryCandidate = selection.toString().replace(/\r?\n.*/, "");
- if (queryCandidate)
- this._searchInputElement.value = queryCandidate;
- }
- }
- this._performSearch(false, false);
- this._searchInputElement.focus();
- this._searchInputElement.select();
- this._searchIsVisible = true;
- },
- /**
- * @param {boolean} filter
- */
- _toggleFilterUI: function(filter)
- {
- this._matchesElement.enableStyleClass("hidden", filter);
- this._searchNavigationElement.enableStyleClass("hidden", filter);
- this._searchInputElement.placeholder = filter ? WebInspector.UIString("Filter") : WebInspector.UIString("Find");
- },
- _updateFilterVisibility: function()
- {
- if (this._searchProvider.canFilter())
- this._filterCheckboxContainer.removeStyleClass("hidden");
- else
- this._filterCheckboxContainer.addStyleClass("hidden");
- },
- _updateReplaceVisibility: function()
- {
- if (!this._searchProvider)
- return;
- if (this._searchProvider.canSearchAndReplace())
- this._replaceElement.removeStyleClass("hidden");
- else {
- this._replaceElement.addStyleClass("hidden");
- this._replaceCheckboxElement.checked = false;
- this._updateSecondRowVisibility();
- }
- },
- /**
- * @param {Event} event
- */
- _onSearchFieldManualFocus: function(event)
- {
- WebInspector.setCurrentFocusElement(event.target);
- },
- /**
- * @param {KeyboardEvent} event
- */
- _onKeyDown: function(event)
- {
- if (isEnterKey(event)) {
- if (event.target === this._searchInputElement) {
- // FIXME: This won't start backwards search with Shift+Enter correctly.
- if (!this._currentQuery)
- this._performSearch(true, true);
- else
- this._jumpToNextSearchResult(event.shiftKey);
- } else if (event.target === this._replaceInputElement)
- this._replace();
- }
- },
- /**
- * @param {boolean=} isBackwardSearch
- */
- _jumpToNextSearchResult: function(isBackwardSearch)
- {
- if (!this._currentQuery || !this._searchNavigationPrevElement.hasStyleClass("enabled"))
- return;
- if (isBackwardSearch)
- this._searchProvider.jumpToPreviousSearchResult();
- else
- this._searchProvider.jumpToNextSearchResult();
- },
- _onNextButtonSearch: function(event)
- {
- if (!this._searchNavigationNextElement.hasStyleClass("enabled"))
- return;
- // Simulate next search on search-navigation-button click.
- this._jumpToNextSearchResult();
- this._searchInputElement.focus();
- },
- _onPrevButtonSearch: function(event)
- {
- if (!this._searchNavigationPrevElement.hasStyleClass("enabled"))
- return;
- // Simulate previous search on search-navigation-button click.
- this._jumpToNextSearchResult(true);
- this._searchInputElement.focus();
- },
- _clearSearch: function()
- {
- delete this._currentQuery;
- if (this._searchHost){
- var searchProvider = this._searchHost.getSearchProvider();
- if (searchProvider && !!searchProvider.currentQuery) {
- delete searchProvider.currentQuery;
- searchProvider.searchCanceled();
- }
- }
- this._updateSearchMatchesCountAndCurrentMatchIndex(0, -1);
- },
- /**
- * @param {boolean} forceSearch
- * @param {boolean} shouldJump
- */
- _performSearch: function(forceSearch, shouldJump)
- {
- var query = this._searchInputElement.value;
- var minimalSearchQuerySize = this._searchProvider.minimalSearchQuerySize();
- if (!query || !this._searchProvider || (!forceSearch && query.length < minimalSearchQuerySize && !this._currentQuery)) {
- this._clearSearch();
- return;
- }
- this._currentQuery = query;
- this._searchProvider.currentQuery = query;
- this._searchProvider.performSearch(query, shouldJump);
- },
- _updateSecondRowVisibility: function()
- {
- if (!this._searchIsVisible || !this._searchHost)
- return;
- if (this._replaceCheckboxElement.checked) {
- this._element.addStyleClass("toolbar-search-replace");
- this._secondRowElement.removeStyleClass("hidden");
- this._prevButtonElement.removeStyleClass("hidden");
- this._findButtonElement.removeStyleClass("hidden");
- this._replaceCheckboxElement.tabIndex = -1;
- this._replaceInputElement.focus();
- } else {
- this._element.removeStyleClass("toolbar-search-replace");
- this._secondRowElement.addStyleClass("hidden");
- this._prevButtonElement.addStyleClass("hidden");
- this._findButtonElement.addStyleClass("hidden");
- this._replaceCheckboxElement.tabIndex = 0;
- this._searchInputElement.focus();
- }
- this._searchHost.setFooterElement(this._element);
- },
- _replace: function()
- {
- this._searchProvider.replaceSelectionWith(this._replaceInputElement.value);
- delete this._currentQuery;
- this._performSearch(true, true);
- },
- _replaceAll: function()
- {
- this._searchProvider.replaceAllWith(this._searchInputElement.value, this._replaceInputElement.value);
- },
- _filterCheckboxClick: function()
- {
- this._searchInputElement.focus();
- this._searchInputElement.select();
- if (this._filterCheckboxElement.checked) {
- this._toggleFilterUI(true);
- this.resetSearch();
- this._performFilter(this._searchInputElement.value);
- } else {
- this._toggleFilterUI(false);
- this.resetFilter();
- this._performSearch(false, false);
- }
- },
- /**
- * @param {string} query
- */
- _performFilter: function(query)
- {
- this._searchProvider.performFilter(query);
- },
- _onInput: function(event)
- {
- if (this._filterCheckboxElement.checked)
- this._performFilter(event.target.value);
- else
- this._performSearch(false, true);
- },
- resetFilter: function()
- {
- this._performFilter("");
- }
- }
- /**
- * @type {?WebInspector.SearchController}
- */
- WebInspector.searchController = null;
- /**
- * @interface
- */
- WebInspector.Searchable = function()
- {
- }
- WebInspector.Searchable.prototype = {
- /**
- * @return {boolean}
- */
- canSearchAndReplace: function() { },
- /**
- * @return {boolean}
- */
- canFilter: function() { },
- searchCanceled: function() { },
- /**
- * @param {string} query
- * @param {boolean} shouldJump
- * @param {WebInspector.Searchable=} self
- */
- performSearch: function(query, shouldJump, self) { },
- /**
- * @return {number}
- */
- minimalSearchQuerySize: function() { },
- /**
- * @param {WebInspector.Searchable=} self
- */
- jumpToNextSearchResult: function(self) { },
- /**
- * @param {WebInspector.Searchable=} self
- */
- jumpToPreviousSearchResult: function(self) { },
- }
|