OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 cr.define('options', function() { | 5 cr.define('options', function() { |
6 const OptionsPage = options.OptionsPage; | 6 const OptionsPage = options.OptionsPage; |
7 | 7 |
8 /** | 8 /** |
9 * Encapsulated handling of the search page. | 9 * Encapsulated handling of the search page. |
| 10 * @constructor |
10 */ | 11 */ |
11 function SearchPage() { | 12 function SearchPage() { |
12 OptionsPage.call(this, 'search', templateData.searchPage, 'searchPage'); | 13 OptionsPage.call(this, 'search', templateData.searchPage, 'searchPage'); |
| 14 this.searchActive = false; |
13 } | 15 } |
14 | 16 |
15 cr.addSingletonGetter(SearchPage); | 17 cr.addSingletonGetter(SearchPage); |
16 | 18 |
17 SearchPage.prototype = { | 19 SearchPage.prototype = { |
18 // Inherit SearchPage from OptionsPage. | 20 // Inherit SearchPage from OptionsPage. |
19 __proto__: OptionsPage.prototype, | 21 __proto__: OptionsPage.prototype, |
20 | 22 |
21 // Initialize SearchPage. | 23 /** |
| 24 * Initialize the page. |
| 25 */ |
22 initializePage: function() { | 26 initializePage: function() { |
23 // Call base class implementation to start preference initialization. | 27 // Call base class implementation to start preference initialization. |
24 OptionsPage.prototype.initializePage.call(this); | 28 OptionsPage.prototype.initializePage.call(this); |
25 | 29 |
| 30 var self = this; |
| 31 |
26 // Create a search field element. | 32 // Create a search field element. |
27 var searchField = document.createElement('input'); | 33 var searchField = document.createElement('input'); |
28 searchField.id = 'searchField'; | 34 searchField.id = 'searchField'; |
29 searchField.type = 'search'; | 35 searchField.type = 'search'; |
30 searchField.setAttribute('autosave', 'org.chromium.options.search'); | 36 searchField.setAttribute('autosave', 'org.chromium.options.search'); |
31 searchField.setAttribute('results', '10'); | 37 searchField.setAttribute('results', '10'); |
| 38 searchField.setAttribute('incremental', 'true'); |
32 | 39 |
33 // Replace the contents of the navigation tab with the search field. | 40 // Replace the contents of the navigation tab with the search field. |
34 this.tab.textContent = ''; | 41 self.tab.textContent = ''; |
35 this.tab.appendChild(searchField); | 42 self.tab.appendChild(searchField); |
| 43 |
| 44 // Handle search events. (No need to throttle, WebKit's search field |
| 45 // will do that automatically.) |
| 46 searchField.onsearch = function(e) { |
| 47 self.setSearchText_(this.value); |
| 48 }; |
36 }, | 49 }, |
37 }; | |
38 | 50 |
39 SearchPage.updateForEmptySearch = function() { | 51 /** |
40 $('searchPageInfo').classList.remove('hidden'); | 52 * Called after this page has shown. |
41 $('searchPageNoMatches').classList.add('hidden'); | 53 */ |
42 }; | 54 didShowPage: function() { |
| 55 // This method is called by the Options page after all pages have |
| 56 // had their visibilty attribute set. At this point we can perform the |
| 57 // search specific DOM manipulation. |
| 58 this.setSearchActive_(true); |
| 59 }, |
43 | 60 |
44 SearchPage.updateForNoSearchResults = function(message) { | 61 /** |
45 $('searchPageInfo').classList.add('hidden'); | 62 * Called before this page will be hidden. |
46 $('searchPageNoMatches').classList.remove('hidden'); | 63 */ |
47 }; | 64 willHidePage: function() { |
| 65 // This method is called by the Options page before all pages have |
| 66 // their visibilty attribute set. Before that happens, we need to |
| 67 // undo the search specific DOM manipulation that was performed in |
| 68 // didShowPage. |
| 69 this.setSearchActive_(false); |
| 70 }, |
48 | 71 |
49 SearchPage.updateForSuccessfulSearch = function(enable) { | 72 /** |
50 $('searchPageInfo').classList.add('hidden'); | 73 * Update the UI to reflect whether we are in a search state. |
51 $('searchPageNoMatches').classList.add('hidden'); | 74 * @param {boolean} active True if we are on the search page. |
| 75 * @private |
| 76 */ |
| 77 setSearchActive_: function(active) { |
| 78 // It's fine to exit if search wasn't active and we're not going to |
| 79 // activate it now. |
| 80 if (!this.searchActive_ && !active) |
| 81 return; |
| 82 |
| 83 if (this.searchActive_ != active) { |
| 84 this.searchActive_ = active; |
| 85 if (active) { |
| 86 // Reset the search criteria, effectively hiding all the sections. |
| 87 this.setSearchText_(''); |
| 88 } else { |
| 89 // Just wipe out any active search text since it's no longer relevant. |
| 90 $('searchField').value = ''; |
| 91 } |
| 92 } |
| 93 |
| 94 var page, length, childDiv; |
| 95 for (var name in OptionsPage.registeredPages) { |
| 96 if (name == this.name) |
| 97 continue; |
| 98 |
| 99 // Update the visible state of all top-level elements that are not |
| 100 // sections (ie titles, button strips). We do this before changing |
| 101 // the page visibility to avoid excessive re-draw. |
| 102 page = OptionsPage.registeredPages[name]; |
| 103 length = page.pageDiv.childNodes.length; |
| 104 for (var i = 0; i < length; i++) { |
| 105 childDiv = page.pageDiv.childNodes[i]; |
| 106 if (childDiv.nodeType == 1) { |
| 107 if (active) { |
| 108 if (childDiv.nodeName.toLowerCase() != 'section') |
| 109 childDiv.classList.add('search-hidden'); |
| 110 } else { |
| 111 childDiv.classList.remove('search-hidden'); |
| 112 } |
| 113 } |
| 114 } |
| 115 |
| 116 // Toggle the visibility state of the page. |
| 117 if (active) { |
| 118 // When search is active, remove the 'hidden' tag. This tag may have |
| 119 // been added by the OptionsPage. |
| 120 page.pageDiv.classList.remove('hidden'); |
| 121 } else { |
| 122 page.visible = false; |
| 123 } |
| 124 } |
| 125 }, |
| 126 |
| 127 /** |
| 128 * Set the current search criteria. |
| 129 * @param {string} text Search text. |
| 130 * @private |
| 131 */ |
| 132 setSearchText_: function(text) { |
| 133 var searchText = text.toLowerCase(); |
| 134 var foundMatches = false; |
| 135 |
| 136 // Build a list of pages to search. Omit the search page. |
| 137 var pagesToSearch = []; |
| 138 for (var name in OptionsPage.registeredPages) { |
| 139 if (name != this.name) |
| 140 pagesToSearch.push(OptionsPage.registeredPages[name]); |
| 141 } |
| 142 |
| 143 // Hide all sections. If the search string matches a title page, show |
| 144 // all sections of that page. |
| 145 for (var key in pagesToSearch) { |
| 146 var page = pagesToSearch[key]; |
| 147 var pageTitle = page.title.toLowerCase(); |
| 148 // Hide non-sections in each page. |
| 149 for (var i = 0; i < page.pageDiv.childNodes.length; i++) { |
| 150 var childDiv = page.pageDiv.childNodes[i]; |
| 151 if (childDiv.nodeType == 1 && |
| 152 childDiv.nodeName.toLowerCase() == 'section') { |
| 153 if (pageTitle == searchText) { |
| 154 childDiv.classList.remove('search-hidden'); |
| 155 foundMatches = true; |
| 156 } else { |
| 157 childDiv.classList.add('search-hidden'); |
| 158 } |
| 159 } |
| 160 } |
| 161 } |
| 162 |
| 163 // Now search all sections for anchored string matches. |
| 164 if (!foundMatches && searchText.length) { |
| 165 var searchRegEx = new RegExp('\\b' + searchText, 'i'); |
| 166 for (var key in pagesToSearch) { |
| 167 var page = pagesToSearch[key]; |
| 168 for (var i = 0; i < page.pageDiv.childNodes.length; i++) { |
| 169 var childDiv = page.pageDiv.childNodes[i]; |
| 170 if (childDiv.nodeType == 1 && |
| 171 childDiv.nodeName.toLowerCase() == 'section') { |
| 172 var isMatch = false; |
| 173 var sectionElements = childDiv.getElementsByTagName("*"); |
| 174 var length = sectionElements.length; |
| 175 var element; |
| 176 for (var j = 0; j < length; j++) { |
| 177 element = sectionElements[j]; |
| 178 if (searchRegEx.test(element.textContent)) { |
| 179 isMatch = true; |
| 180 break; |
| 181 } |
| 182 } |
| 183 if (isMatch) { |
| 184 childDiv.classList.remove('search-hidden'); |
| 185 foundMatches = true; |
| 186 } |
| 187 } |
| 188 } |
| 189 } |
| 190 } |
| 191 |
| 192 // Configure elements on the search results page based on search results. |
| 193 if (searchText.length == 0) { |
| 194 $('searchPageInfo').classList.remove('search-hidden'); |
| 195 $('searchPageNoMatches').classList.add('search-hidden'); |
| 196 } else if (foundMatches) { |
| 197 $('searchPageInfo').classList.add('search-hidden'); |
| 198 $('searchPageNoMatches').classList.add('search-hidden'); |
| 199 } else { |
| 200 $('searchPageInfo').classList.add('search-hidden'); |
| 201 $('searchPageNoMatches').classList.remove('search-hidden'); |
| 202 } |
| 203 } |
52 }; | 204 }; |
53 | 205 |
54 // Export | 206 // Export |
55 return { | 207 return { |
56 SearchPage: SearchPage | 208 SearchPage: SearchPage |
57 }; | 209 }; |
58 | 210 |
59 }); | 211 }); |
OLD | NEW |