1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/components/tabview/search.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,616 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/* ****************************** 1.9 + * 1.10 + * This file incorporates work from: 1.11 + * Quicksilver Score (qs_score): 1.12 + * http://rails-oceania.googlecode.com/svn/lachiecox/qs_score/trunk/qs_score.js 1.13 + * This incorporated work is covered by the following copyright and 1.14 + * permission notice: 1.15 + * Copyright 2008 Lachie Cox 1.16 + * Licensed under the MIT license. 1.17 + * http://jquery.org/license 1.18 + * 1.19 + * ***************************** */ 1.20 + 1.21 +// ********** 1.22 +// Title: search.js 1.23 +// Implementation for the search functionality of Firefox Panorama. 1.24 + 1.25 +// ########## 1.26 +// Class: TabUtils 1.27 +// 1.28 +// A collection of helper functions for dealing with both <TabItem>s and 1.29 +// <xul:tab>s without having to worry which one is which. 1.30 +let TabUtils = { 1.31 + // ---------- 1.32 + // Function: toString 1.33 + // Prints [TabUtils] for debug use. 1.34 + toString: function TabUtils_toString() { 1.35 + return "[TabUtils]"; 1.36 + }, 1.37 + 1.38 + // --------- 1.39 + // Function: nameOfTab 1.40 + // Given a <TabItem> or a <xul:tab> returns the tab's name. 1.41 + nameOf: function TabUtils_nameOf(tab) { 1.42 + // We can have two types of tabs: A <TabItem> or a <xul:tab> 1.43 + // because we have to deal with both tabs represented inside 1.44 + // of active Panoramas as well as for windows in which 1.45 + // Panorama has yet to be activated. We uses object sniffing to 1.46 + // determine the type of tab and then returns its name. 1.47 + return tab.label != undefined ? tab.label : tab.$tabTitle[0].textContent; 1.48 + }, 1.49 + 1.50 + // --------- 1.51 + // Function: URLOf 1.52 + // Given a <TabItem> or a <xul:tab> returns the URL of tab. 1.53 + URLOf: function TabUtils_URLOf(tab) { 1.54 + // Convert a <TabItem> to <xul:tab> 1.55 + if ("tab" in tab) 1.56 + tab = tab.tab; 1.57 + return tab.linkedBrowser.currentURI.spec; 1.58 + }, 1.59 + 1.60 + // --------- 1.61 + // Function: faviconURLOf 1.62 + // Given a <TabItem> or a <xul:tab> returns the URL of tab's favicon. 1.63 + faviconURLOf: function TabUtils_faviconURLOf(tab) { 1.64 + return tab.image != undefined ? tab.image : tab.$favImage[0].src; 1.65 + }, 1.66 + 1.67 + // --------- 1.68 + // Function: focus 1.69 + // Given a <TabItem> or a <xul:tab>, focuses it and it's window. 1.70 + focus: function TabUtils_focus(tab) { 1.71 + // Convert a <TabItem> to a <xul:tab> 1.72 + if ("tab" in tab) 1.73 + tab = tab.tab; 1.74 + tab.ownerDocument.defaultView.gBrowser.selectedTab = tab; 1.75 + tab.ownerDocument.defaultView.focus(); 1.76 + } 1.77 +}; 1.78 + 1.79 +// ########## 1.80 +// Class: TabMatcher 1.81 +// 1.82 +// A class that allows you to iterate over matching and not-matching tabs, 1.83 +// given a case-insensitive search term. 1.84 +function TabMatcher(term) { 1.85 + this.term = term; 1.86 +} 1.87 + 1.88 +TabMatcher.prototype = { 1.89 + // ---------- 1.90 + // Function: toString 1.91 + // Prints [TabMatcher (term)] for debug use. 1.92 + toString: function TabMatcher_toString() { 1.93 + return "[TabMatcher (" + this.term + ")]"; 1.94 + }, 1.95 + 1.96 + // --------- 1.97 + // Function: _filterAndSortForMatches 1.98 + // Given an array of <TabItem>s and <xul:tab>s returns a new array 1.99 + // of tabs whose name matched the search term, sorted by lexical 1.100 + // closeness. 1.101 + _filterAndSortForMatches: function TabMatcher__filterAndSortForMatches(tabs) { 1.102 + let self = this; 1.103 + tabs = tabs.filter(function TabMatcher__filterAndSortForMatches_filter(tab) { 1.104 + let name = TabUtils.nameOf(tab); 1.105 + let url = TabUtils.URLOf(tab); 1.106 + return name.match(self.term, "i") || url.match(self.term, "i"); 1.107 + }); 1.108 + 1.109 + tabs.sort(function TabMatcher__filterAndSortForMatches_sort(x, y) { 1.110 + let yScore = self._scorePatternMatch(self.term, TabUtils.nameOf(y)); 1.111 + let xScore = self._scorePatternMatch(self.term, TabUtils.nameOf(x)); 1.112 + return yScore - xScore; 1.113 + }); 1.114 + 1.115 + return tabs; 1.116 + }, 1.117 + 1.118 + // --------- 1.119 + // Function: _filterForUnmatches 1.120 + // Given an array of <TabItem>s returns an unsorted array of tabs whose name 1.121 + // does not match the the search term. 1.122 + _filterForUnmatches: function TabMatcher__filterForUnmatches(tabs) { 1.123 + let self = this; 1.124 + return tabs.filter(function TabMatcher__filterForUnmatches_filter(tab) { 1.125 + let name = tab.$tabTitle[0].textContent; 1.126 + let url = TabUtils.URLOf(tab); 1.127 + return !name.match(self.term, "i") && !url.match(self.term, "i"); 1.128 + }); 1.129 + }, 1.130 + 1.131 + // --------- 1.132 + // Function: _getTabsForOtherWindows 1.133 + // Returns an array of <TabItem>s and <xul:tabs>s representing tabs 1.134 + // from all windows but the current window. <TabItem>s will be returned 1.135 + // for windows in which Panorama has been activated at least once, while 1.136 + // <xul:tab>s will be returned for windows in which Panorama has never 1.137 + // been activated. 1.138 + _getTabsForOtherWindows: function TabMatcher__getTabsForOtherWindows() { 1.139 + let enumerator = Services.wm.getEnumerator("navigator:browser"); 1.140 + let allTabs = []; 1.141 + 1.142 + while (enumerator.hasMoreElements()) { 1.143 + let win = enumerator.getNext(); 1.144 + // This function gets tabs from other windows, not from the current window 1.145 + if (win != gWindow) 1.146 + allTabs.push.apply(allTabs, win.gBrowser.tabs); 1.147 + } 1.148 + return allTabs; 1.149 + }, 1.150 + 1.151 + // ---------- 1.152 + // Function: matchedTabsFromOtherWindows 1.153 + // Returns an array of <TabItem>s and <xul:tab>s that match the search term 1.154 + // from all windows but the current window. <TabItem>s will be returned for 1.155 + // windows in which Panorama has been activated at least once, while 1.156 + // <xul:tab>s will be returned for windows in which Panorama has never 1.157 + // been activated. 1.158 + // (new TabMatcher("app")).matchedTabsFromOtherWindows(); 1.159 + matchedTabsFromOtherWindows: function TabMatcher_matchedTabsFromOtherWindows() { 1.160 + if (this.term.length < 2) 1.161 + return []; 1.162 + 1.163 + let tabs = this._getTabsForOtherWindows(); 1.164 + return this._filterAndSortForMatches(tabs); 1.165 + }, 1.166 + 1.167 + // ---------- 1.168 + // Function: matched 1.169 + // Returns an array of <TabItem>s which match the current search term. 1.170 + // If the term is less than 2 characters in length, it returns nothing. 1.171 + matched: function TabMatcher_matched() { 1.172 + if (this.term.length < 2) 1.173 + return []; 1.174 + 1.175 + let tabs = TabItems.getItems(); 1.176 + return this._filterAndSortForMatches(tabs); 1.177 + }, 1.178 + 1.179 + // ---------- 1.180 + // Function: unmatched 1.181 + // Returns all of <TabItem>s that .matched() doesn't return. 1.182 + unmatched: function TabMatcher_unmatched() { 1.183 + let tabs = TabItems.getItems(); 1.184 + if (this.term.length < 2) 1.185 + return tabs; 1.186 + 1.187 + return this._filterForUnmatches(tabs); 1.188 + }, 1.189 + 1.190 + // ---------- 1.191 + // Function: doSearch 1.192 + // Performs the search. Lets you provide three functions. 1.193 + // The first is on all matched tabs in the window, the second on all unmatched 1.194 + // tabs in the window, and the third on all matched tabs in other windows. 1.195 + // The first two functions take two parameters: A <TabItem> and its integer index 1.196 + // indicating the absolute rank of the <TabItem> in terms of match to 1.197 + // the search term. The last function also takes two paramaters, but can be 1.198 + // passed both <TabItem>s and <xul:tab>s and the index is offset by the 1.199 + // number of matched tabs inside the window. 1.200 + doSearch: function TabMatcher_doSearch(matchFunc, unmatchFunc, otherFunc) { 1.201 + let matches = this.matched(); 1.202 + let unmatched = this.unmatched(); 1.203 + let otherMatches = this.matchedTabsFromOtherWindows(); 1.204 + 1.205 + matches.forEach(function(tab, i) { 1.206 + matchFunc(tab, i); 1.207 + }); 1.208 + 1.209 + otherMatches.forEach(function(tab,i) { 1.210 + otherFunc(tab, i+matches.length); 1.211 + }); 1.212 + 1.213 + unmatched.forEach(function(tab, i) { 1.214 + unmatchFunc(tab, i); 1.215 + }); 1.216 + }, 1.217 + 1.218 + // ---------- 1.219 + // Function: _scorePatternMatch 1.220 + // Given a pattern string, returns a score between 0 and 1 of how well 1.221 + // that pattern matches the original string. It mimics the heuristics 1.222 + // of the Mac application launcher Quicksilver. 1.223 + _scorePatternMatch: function TabMatcher__scorePatternMatch(pattern, matched, offset) { 1.224 + offset = offset || 0; 1.225 + pattern = pattern.toLowerCase(); 1.226 + matched = matched.toLowerCase(); 1.227 + 1.228 + if (pattern.length == 0) 1.229 + return 0.9; 1.230 + if (pattern.length > matched.length) 1.231 + return 0.0; 1.232 + 1.233 + for (let i = pattern.length; i > 0; i--) { 1.234 + let sub_pattern = pattern.substring(0,i); 1.235 + let index = matched.indexOf(sub_pattern); 1.236 + 1.237 + if (index < 0) 1.238 + continue; 1.239 + if (index + pattern.length > matched.length + offset) 1.240 + continue; 1.241 + 1.242 + let next_string = matched.substring(index+sub_pattern.length); 1.243 + let next_pattern = null; 1.244 + 1.245 + if (i >= pattern.length) 1.246 + next_pattern = ''; 1.247 + else 1.248 + next_pattern = pattern.substring(i); 1.249 + 1.250 + let remaining_score = this._scorePatternMatch(next_pattern, next_string, offset + index); 1.251 + 1.252 + if (remaining_score > 0) { 1.253 + let score = matched.length-next_string.length; 1.254 + 1.255 + if (index != 0) { 1.256 + let c = matched.charCodeAt(index-1); 1.257 + if (c == 32 || c == 9) { 1.258 + for (let j = (index - 2); j >= 0; j--) { 1.259 + c = matched.charCodeAt(j); 1.260 + score -= ((c == 32 || c == 9) ? 1 : 0.15); 1.261 + } 1.262 + } else { 1.263 + score -= index; 1.264 + } 1.265 + } 1.266 + 1.267 + score += remaining_score * next_string.length; 1.268 + score /= matched.length; 1.269 + return score; 1.270 + } 1.271 + } 1.272 + return 0.0; 1.273 + } 1.274 +}; 1.275 + 1.276 +// ########## 1.277 +// Class: TabHandlers 1.278 +// 1.279 +// A object that handles all of the event handlers. 1.280 +let TabHandlers = { 1.281 + _mouseDownLocation: null, 1.282 + 1.283 + // --------- 1.284 + // Function: onMatch 1.285 + // Adds styles and event listeners to the matched tab items. 1.286 + onMatch: function TabHandlers_onMatch(tab, index) { 1.287 + tab.addClass("onTop"); 1.288 + index != 0 ? tab.addClass("notMainMatch") : tab.removeClass("notMainMatch"); 1.289 + 1.290 + // Remove any existing handlers before adding the new ones. 1.291 + // If we don't do this, then we may add more handlers than 1.292 + // we remove. 1.293 + tab.$canvas 1.294 + .unbind("mousedown", TabHandlers._hideHandler) 1.295 + .unbind("mouseup", TabHandlers._showHandler); 1.296 + 1.297 + tab.$canvas 1.298 + .mousedown(TabHandlers._hideHandler) 1.299 + .mouseup(TabHandlers._showHandler); 1.300 + }, 1.301 + 1.302 + // --------- 1.303 + // Function: onUnmatch 1.304 + // Removes styles and event listeners from the unmatched tab items. 1.305 + onUnmatch: function TabHandlers_onUnmatch(tab, index) { 1.306 + tab.$container.removeClass("onTop"); 1.307 + tab.removeClass("notMainMatch"); 1.308 + 1.309 + tab.$canvas 1.310 + .unbind("mousedown", TabHandlers._hideHandler) 1.311 + .unbind("mouseup", TabHandlers._showHandler); 1.312 + }, 1.313 + 1.314 + // --------- 1.315 + // Function: onOther 1.316 + // Removes styles and event listeners from the unmatched tabs. 1.317 + onOther: function TabHandlers_onOther(tab, index) { 1.318 + // Unlike the other on* functions, in this function tab can 1.319 + // either be a <TabItem> or a <xul:tab>. In other functions 1.320 + // it is always a <TabItem>. Also note that index is offset 1.321 + // by the number of matches within the window. 1.322 + let item = iQ("<div/>") 1.323 + .addClass("inlineMatch") 1.324 + .click(function TabHandlers_onOther_click(event) { 1.325 + Search.hide(event); 1.326 + TabUtils.focus(tab); 1.327 + }); 1.328 + 1.329 + iQ("<img/>") 1.330 + .attr("src", TabUtils.faviconURLOf(tab)) 1.331 + .appendTo(item); 1.332 + 1.333 + iQ("<span/>") 1.334 + .text(TabUtils.nameOf(tab)) 1.335 + .appendTo(item); 1.336 + 1.337 + index != 0 ? item.addClass("notMainMatch") : item.removeClass("notMainMatch"); 1.338 + item.appendTo("#results"); 1.339 + iQ("#otherresults").show(); 1.340 + }, 1.341 + 1.342 + // --------- 1.343 + // Function: _hideHandler 1.344 + // Performs when mouse down on a canvas of tab item. 1.345 + _hideHandler: function TabHandlers_hideHandler(event) { 1.346 + iQ("#search").fadeOut(); 1.347 + iQ("#searchshade").fadeOut(); 1.348 + TabHandlers._mouseDownLocation = {x:event.clientX, y:event.clientY}; 1.349 + }, 1.350 + 1.351 + // --------- 1.352 + // Function: _showHandler 1.353 + // Performs when mouse up on a canvas of tab item. 1.354 + _showHandler: function TabHandlers_showHandler(event) { 1.355 + // If the user clicks on a tab without moving the mouse then 1.356 + // they are zooming into the tab and we need to exit search 1.357 + // mode. 1.358 + if (TabHandlers._mouseDownLocation.x == event.clientX && 1.359 + TabHandlers._mouseDownLocation.y == event.clientY) { 1.360 + Search.hide(); 1.361 + return; 1.362 + } 1.363 + 1.364 + iQ("#searchshade").show(); 1.365 + iQ("#search").show(); 1.366 + iQ("#searchbox")[0].focus(); 1.367 + // Marshal the search. 1.368 + setTimeout(Search.perform, 0); 1.369 + } 1.370 +}; 1.371 + 1.372 +// ########## 1.373 +// Class: Search 1.374 +// 1.375 +// A object that handles the search feature. 1.376 +let Search = { 1.377 + _initiatedBy: "", 1.378 + _blockClick: false, 1.379 + _currentHandler: null, 1.380 + 1.381 + // ---------- 1.382 + // Function: toString 1.383 + // Prints [Search] for debug use. 1.384 + toString: function Search_toString() { 1.385 + return "[Search]"; 1.386 + }, 1.387 + 1.388 + // ---------- 1.389 + // Function: init 1.390 + // Initializes the searchbox to be focused, and everything else to be hidden, 1.391 + // and to have everything have the appropriate event handlers. 1.392 + init: function Search_init() { 1.393 + let self = this; 1.394 + 1.395 + iQ("#search").hide(); 1.396 + iQ("#searchshade").hide().mousedown(function Search_init_shade_mousedown(event) { 1.397 + if (event.target.id != "searchbox" && !self._blockClick) 1.398 + self.hide(); 1.399 + }); 1.400 + 1.401 + iQ("#searchbox").keyup(function Search_init_box_keyup() { 1.402 + self.perform(); 1.403 + }) 1.404 + .attr("title", tabviewString("button.searchTabs")); 1.405 + 1.406 + iQ("#searchbutton").mousedown(function Search_init_button_mousedown() { 1.407 + self._initiatedBy = "buttonclick"; 1.408 + self.ensureShown(); 1.409 + self.switchToInMode(); 1.410 + }) 1.411 + .attr("title", tabviewString("button.searchTabs")); 1.412 + 1.413 + window.addEventListener("focus", function Search_init_window_focus() { 1.414 + if (self.isEnabled()) { 1.415 + self._blockClick = true; 1.416 + setTimeout(function() { 1.417 + self._blockClick = false; 1.418 + }, 0); 1.419 + } 1.420 + }, false); 1.421 + 1.422 + this.switchToBeforeMode(); 1.423 + }, 1.424 + 1.425 + // ---------- 1.426 + // Function: _beforeSearchKeyHandler 1.427 + // Handles all keydown before the search interface is brought up. 1.428 + _beforeSearchKeyHandler: function Search__beforeSearchKeyHandler(event) { 1.429 + // Only match reasonable text-like characters for quick search. 1.430 + if (event.altKey || event.ctrlKey || event.metaKey) 1.431 + return; 1.432 + 1.433 + if ((event.keyCode > 0 && event.keyCode <= event.DOM_VK_DELETE) || 1.434 + event.keyCode == event.DOM_VK_CONTEXT_MENU || 1.435 + event.keyCode == event.DOM_VK_SLEEP || 1.436 + (event.keyCode >= event.DOM_VK_F1 && 1.437 + event.keyCode <= event.DOM_VK_SCROLL_LOCK) || 1.438 + event.keyCode == event.DOM_VK_META || 1.439 + event.keyCode == 91 || // 91 = left windows key 1.440 + event.keyCode == 92 || // 92 = right windows key 1.441 + (!event.keyCode && !event.charCode)) { 1.442 + return; 1.443 + } 1.444 + 1.445 + // If we are already in an input field, allow typing as normal. 1.446 + if (event.target.nodeName == "INPUT") 1.447 + return; 1.448 + 1.449 + // / is used to activate the search feature so the key shouldn't be entered 1.450 + // into the search box. 1.451 + if (event.keyCode == KeyEvent.DOM_VK_SLASH) { 1.452 + event.stopPropagation(); 1.453 + event.preventDefault(); 1.454 + } 1.455 + 1.456 + this.switchToInMode(); 1.457 + this._initiatedBy = "keydown"; 1.458 + this.ensureShown(true); 1.459 + }, 1.460 + 1.461 + // ---------- 1.462 + // Function: _inSearchKeyHandler 1.463 + // Handles all keydown while search mode. 1.464 + _inSearchKeyHandler: function Search__inSearchKeyHandler(event) { 1.465 + let term = iQ("#searchbox").val(); 1.466 + if ((event.keyCode == event.DOM_VK_ESCAPE) || 1.467 + (event.keyCode == event.DOM_VK_BACK_SPACE && term.length <= 1 && 1.468 + this._initiatedBy == "keydown")) { 1.469 + this.hide(event); 1.470 + return; 1.471 + } 1.472 + 1.473 + let matcher = this.createSearchTabMatcher(); 1.474 + let matches = matcher.matched(); 1.475 + let others = matcher.matchedTabsFromOtherWindows(); 1.476 + if (event.keyCode == event.DOM_VK_RETURN && 1.477 + (matches.length > 0 || others.length > 0)) { 1.478 + this.hide(event); 1.479 + if (matches.length > 0) 1.480 + matches[0].zoomIn(); 1.481 + else 1.482 + TabUtils.focus(others[0]); 1.483 + } 1.484 + }, 1.485 + 1.486 + // ---------- 1.487 + // Function: switchToBeforeMode 1.488 + // Make sure the event handlers are appropriate for the before-search mode. 1.489 + switchToBeforeMode: function Search_switchToBeforeMode() { 1.490 + let self = this; 1.491 + if (this._currentHandler) 1.492 + iQ(window).unbind("keydown", this._currentHandler); 1.493 + this._currentHandler = function Search_switchToBeforeMode_handler(event) { 1.494 + self._beforeSearchKeyHandler(event); 1.495 + } 1.496 + iQ(window).keydown(this._currentHandler); 1.497 + }, 1.498 + 1.499 + // ---------- 1.500 + // Function: switchToInMode 1.501 + // Make sure the event handlers are appropriate for the in-search mode. 1.502 + switchToInMode: function Search_switchToInMode() { 1.503 + let self = this; 1.504 + if (this._currentHandler) 1.505 + iQ(window).unbind("keydown", this._currentHandler); 1.506 + this._currentHandler = function Search_switchToInMode_handler(event) { 1.507 + self._inSearchKeyHandler(event); 1.508 + } 1.509 + iQ(window).keydown(this._currentHandler); 1.510 + }, 1.511 + 1.512 + createSearchTabMatcher: function Search_createSearchTabMatcher() { 1.513 + return new TabMatcher(iQ("#searchbox").val()); 1.514 + }, 1.515 + 1.516 + // ---------- 1.517 + // Function: isEnabled 1.518 + // Checks whether search mode is enabled or not. 1.519 + isEnabled: function Search_isEnabled() { 1.520 + return iQ("#search").css("display") != "none"; 1.521 + }, 1.522 + 1.523 + // ---------- 1.524 + // Function: hide 1.525 + // Hides search mode. 1.526 + hide: function Search_hide(event) { 1.527 + if (!this.isEnabled()) 1.528 + return; 1.529 + 1.530 + iQ("#searchbox").val(""); 1.531 + iQ("#searchshade").hide(); 1.532 + iQ("#search").hide(); 1.533 + 1.534 + iQ("#searchbutton").css({ opacity:.8 }); 1.535 + 1.536 +#ifdef XP_MACOSX 1.537 + UI.setTitlebarColors(true); 1.538 +#endif 1.539 + 1.540 + this.perform(); 1.541 + this.switchToBeforeMode(); 1.542 + 1.543 + if (event) { 1.544 + // when hiding the search mode, we need to prevent the keypress handler 1.545 + // in UI__setTabViewFrameKeyHandlers to handle the key press again. e.g. Esc 1.546 + // which is already handled by the key down in this class. 1.547 + if (event.type == "keydown") 1.548 + UI.ignoreKeypressForSearch = true; 1.549 + event.preventDefault(); 1.550 + event.stopPropagation(); 1.551 + } 1.552 + 1.553 + // Return focus to the tab window 1.554 + UI.blurAll(); 1.555 + gTabViewFrame.contentWindow.focus(); 1.556 + 1.557 + let newEvent = document.createEvent("Events"); 1.558 + newEvent.initEvent("tabviewsearchdisabled", false, false); 1.559 + dispatchEvent(newEvent); 1.560 + }, 1.561 + 1.562 + // ---------- 1.563 + // Function: perform 1.564 + // Performs a search. 1.565 + perform: function Search_perform() { 1.566 + let matcher = this.createSearchTabMatcher(); 1.567 + 1.568 + // Remove any previous other-window search results and 1.569 + // hide the display area. 1.570 + iQ("#results").empty(); 1.571 + iQ("#otherresults").hide(); 1.572 + iQ("#otherresults>.label").text(tabviewString("search.otherWindowTabs")); 1.573 + 1.574 + matcher.doSearch(TabHandlers.onMatch, TabHandlers.onUnmatch, TabHandlers.onOther); 1.575 + }, 1.576 + 1.577 + // ---------- 1.578 + // Function: ensureShown 1.579 + // Ensures the search feature is displayed. If not, display it. 1.580 + // Parameters: 1.581 + // - a boolean indicates whether this is triggered by a keypress or not 1.582 + ensureShown: function Search_ensureShown(activatedByKeypress) { 1.583 + let $search = iQ("#search"); 1.584 + let $searchShade = iQ("#searchshade"); 1.585 + let $searchbox = iQ("#searchbox"); 1.586 + iQ("#searchbutton").css({ opacity: 1 }); 1.587 + 1.588 + // NOTE: when this function is called by keydown handler, next keypress 1.589 + // event or composition events of IME will be fired on the focused editor. 1.590 + function dispatchTabViewSearchEnabledEvent() { 1.591 + let newEvent = document.createEvent("Events"); 1.592 + newEvent.initEvent("tabviewsearchenabled", false, false); 1.593 + dispatchEvent(newEvent); 1.594 + }; 1.595 + 1.596 + if (!this.isEnabled()) { 1.597 + $searchShade.show(); 1.598 + $search.show(); 1.599 + 1.600 +#ifdef XP_MACOSX 1.601 + UI.setTitlebarColors({active: "#717171", inactive: "#EDEDED"}); 1.602 +#endif 1.603 + 1.604 + if (activatedByKeypress) { 1.605 + // set the focus so key strokes are entered into the textbox. 1.606 + $searchbox[0].focus(); 1.607 + dispatchTabViewSearchEnabledEvent(); 1.608 + } else { 1.609 + // marshal the focusing, otherwise it ends up with searchbox[0].focus gets 1.610 + // called before the search button gets the focus after being pressed. 1.611 + setTimeout(function setFocusAndDispatchSearchEnabledEvent() { 1.612 + $searchbox[0].focus(); 1.613 + dispatchTabViewSearchEnabledEvent(); 1.614 + }, 0); 1.615 + } 1.616 + } 1.617 + } 1.618 +}; 1.619 +