/* * 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. */ WebInspector.SearchController = function() { this.element = document.getElementById("search"); this._matchesElement = document.getElementById("search-results-matches"); this._toolbarLabelElement = document.getElementById("search-toolbar-label"); this.element.addEventListener("search", this._onSearch.bind(this), false); // when the search is emptied this.element.addEventListener("mousedown", this._onSearchFieldManualFocus.bind(this), false); // when the search field is manually selected this.element.addEventListener("keydown", this._onKeyDown.bind(this), true); } WebInspector.SearchController.prototype = { updateSearchMatchesCount: function(matches, panel) { if (!panel) panel = WebInspector.currentPanel; panel.currentSearchMatches = matches; if (panel === WebInspector.currentPanel) this._updateSearchMatchesCount(WebInspector.currentPanel.currentQuery && matches); }, updateSearchLabel: function() { var panelName = WebInspector.currentPanel && WebInspector.currentPanel.toolbarItemLabel; if (!panelName) return; var newLabel = WebInspector.UIString("Search %s", panelName); if (WebInspector.attached) this.element.setAttribute("placeholder", newLabel); else { this.element.removeAttribute("placeholder"); this._toolbarLabelElement.textContent = newLabel; } }, cancelSearch: function() { this.element.value = ""; this._performSearch(""); }, 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.focusSearchField(); event.handled = true; } break; case "F3": if (!isMac) { this.focusSearchField(); event.handled = true; } break; case "U+0047": // G key var currentPanel = WebInspector.currentPanel; if (isMac && event.metaKey && !event.ctrlKey && !event.altKey) { if (event.shiftKey) { if (currentPanel.jumpToPreviousSearchResult) currentPanel.jumpToPreviousSearchResult(); } else if (currentPanel.jumpToNextSearchResult) currentPanel.jumpToNextSearchResult(); event.handled = true; } break; } }, activePanelChanged: function() { this.updateSearchLabel(); if (!this._currentQuery) return; panel = WebInspector.currentPanel; if (panel.performSearch) { function performPanelSearch() { this._updateSearchMatchesCount(); panel.currentQuery = this._currentQuery; panel.performSearch(this._currentQuery); } // Perform the search on a timeout so the panel switches fast. setTimeout(performPanelSearch.bind(this), 0); } else { // Update to show Not found for panels that can't be searched. this._updateSearchMatchesCount(); } }, _updateSearchMatchesCount: function(matches) { if (matches == null) { this._matchesElement.addStyleClass("hidden"); return; } if (matches) { if (matches === 1) var matchesString = WebInspector.UIString("1 match"); else var matchesString = WebInspector.UIString("%d matches", matches); } else var matchesString = WebInspector.UIString("Not Found"); this._matchesElement.removeStyleClass("hidden"); this._matchesElement.textContent = matchesString; WebInspector.toolbar.resize(); }, focusSearchField: function() { this.element.focus(); this.element.select(); }, _onSearchFieldManualFocus: function(event) { WebInspector.currentFocusElement = event.target; }, _onKeyDown: function(event) { // Escape Key will clear the field and clear the search results if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) { // If focus belongs here and text is empty - nothing to do, return unhandled. // When search was selected manually and is currently blank, we'd like Esc stay unhandled // and hit console drawer handler. if (event.target.value === "" && this.currentFocusElement === this.previousFocusElement) return; event.preventDefault(); event.stopPropagation(); this.cancelSearch(event); WebInspector.currentFocusElement = WebInspector.previousFocusElement; if (WebInspector.currentFocusElement === event.target) WebInspector.currentFocusElement.currentFocusElement.select(); return false; } if (!isEnterKey(event)) return false; // Select all of the text so the user can easily type an entirely new query. event.target.select(); // Only call performSearch if the Enter key was pressed. Otherwise the search // performance is poor because of searching on every key. The search field has // the incremental attribute set, so we still get incremental searches. this._onSearch(event); // Call preventDefault since this was the Enter key. This prevents a "search" event // from firing for key down. This stops performSearch from being called twice in a row. event.preventDefault(); }, _onSearch: function(event) { var forceSearch = event.keyIdentifier === "Enter"; this._performSearch(event.target.value, forceSearch, event.shiftKey, false); }, _performSearch: function(query, forceSearch, isBackwardSearch, repeatSearch) { var isShortSearch = (query.length < 3); // Clear a leftover short search flag due to a non-conflicting forced search. if (isShortSearch && this._shortSearchWasForcedByKeyEvent && this._currentQuery !== query) delete this._shortSearchWasForcedByKeyEvent; // Indicate this was a forced search on a short query. if (isShortSearch && forceSearch) this._shortSearchWasForcedByKeyEvent = true; if (!query || !query.length || (!forceSearch && isShortSearch)) { // Prevent clobbering a short search forced by the user. if (this._shortSearchWasForcedByKeyEvent) { delete this._shortSearchWasForcedByKeyEvent; return; } delete this._currentQuery; for (var panelName in WebInspector.panels) { var panel = WebInspector.panels[panelName]; var hadCurrentQuery = !!panel.currentQuery; delete panel.currentQuery; if (hadCurrentQuery && panel.searchCanceled) panel.searchCanceled(); } this._updateSearchMatchesCount(); return; } var currentPanel = WebInspector.currentPanel; if (!repeatSearch && query === currentPanel.currentQuery && currentPanel.currentQuery === this._currentQuery) { // When this is the same query and a forced search, jump to the next // search result for a good user experience. if (forceSearch) { if (!isBackwardSearch && currentPanel.jumpToNextSearchResult) currentPanel.jumpToNextSearchResult(); else if (isBackwardSearch && currentPanel.jumpToPreviousSearchResult) currentPanel.jumpToPreviousSearchResult(); } return; } this._currentQuery = query; this._updateSearchMatchesCount(); if (!currentPanel.performSearch) return; currentPanel.currentQuery = query; currentPanel.performSearch(query); } }