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 * @constructor |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
85 if (active) { | 85 if (active) { |
86 // Reset the search criteria, effectively hiding all the sections. | 86 // Reset the search criteria, effectively hiding all the sections. |
87 this.setSearchText_(''); | 87 this.setSearchText_(''); |
88 } else { | 88 } else { |
89 // Just wipe out any active search text since it's no longer relevant. | 89 // Just wipe out any active search text since it's no longer relevant. |
90 $('searchField').value = ''; | 90 $('searchField').value = ''; |
91 } | 91 } |
92 } | 92 } |
93 | 93 |
94 var page, length, childDiv; | 94 var page, length, childDiv; |
95 for (var name in OptionsPage.registeredPages) { | 95 var pagesToSearch = this.getSearchablePages_(); |
96 if (name == this.name) | 96 for (var key in pagesToSearch) { |
97 continue; | 97 var page = pagesToSearch[key]; |
98 | |
99 if (!active) { | |
100 page.visible = false; | |
101 this.unhighlightMatches_(page.tab); | |
102 this.unhighlightMatches_(page.pageDiv); | |
103 } | |
98 | 104 |
99 // Update the visible state of all top-level elements that are not | 105 // Update the visible state of all top-level elements that are not |
100 // sections (ie titles, button strips). We do this before changing | 106 // sections (ie titles, button strips). We do this before changing |
101 // the page visibility to avoid excessive re-draw. | 107 // the page visibility to avoid excessive re-draw. |
102 page = OptionsPage.registeredPages[name]; | |
103 length = page.pageDiv.childNodes.length; | 108 length = page.pageDiv.childNodes.length; |
104 for (var i = 0; i < length; i++) { | 109 for (var i = 0; i < length; i++) { |
105 childDiv = page.pageDiv.childNodes[i]; | 110 childDiv = page.pageDiv.childNodes[i]; |
106 if (childDiv.nodeType == 1) { | 111 if (childDiv.nodeType == 1) { |
107 if (active) { | 112 if (active) { |
108 if (childDiv.nodeName.toLowerCase() != 'section') | 113 if (childDiv.nodeName.toLowerCase() != 'section') |
109 childDiv.classList.add('search-hidden'); | 114 childDiv.classList.add('search-hidden'); |
110 } else { | 115 } else { |
111 childDiv.classList.remove('search-hidden'); | 116 childDiv.classList.remove('search-hidden'); |
112 } | 117 } |
113 } | 118 } |
114 } | 119 } |
115 | 120 |
116 // Toggle the visibility state of the page. | 121 // Toggle the visibility state of the page. |
117 if (active) { | 122 if (active) { |
118 // When search is active, remove the 'hidden' tag. This tag may have | 123 // When search is active, remove the 'hidden' tag. This tag may have |
119 // been added by the OptionsPage. | 124 // been added by the OptionsPage. |
120 page.pageDiv.classList.remove('hidden'); | 125 page.pageDiv.classList.remove('hidden'); |
121 } else { | |
122 page.visible = false; | |
123 } | 126 } |
124 } | 127 } |
125 }, | 128 }, |
126 | 129 |
127 /** | 130 /** |
128 * Set the current search criteria. | 131 * Set the current search criteria. |
129 * @param {string} text Search text. | 132 * @param {string} text Search text. |
130 * @private | 133 * @private |
131 */ | 134 */ |
132 setSearchText_: function(text) { | 135 setSearchText_: function(text) { |
133 var searchText = text.toLowerCase(); | |
134 var foundMatches = false; | 136 var foundMatches = false; |
135 | 137 |
136 // Build a list of pages to search. Omit the search page. | 138 // Generate search text by applying lowercase and escaping any characters |
137 var pagesToSearch = []; | 139 // that would be problematic for regular expressions. |
138 for (var name in OptionsPage.registeredPages) { | 140 var searchText = |
139 if (name != this.name) | 141 text.toLowerCase().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); |
140 pagesToSearch.push(OptionsPage.registeredPages[name]); | |
141 } | |
142 | 142 |
143 // Hide all sections. If the search string matches a title page, show | 143 // Generate a regular expression and transform string for searching the |
144 // all sections of that page. | 144 // navigation sidebar. |
145 var navRegEx = new RegExp('(\\b' + searchText + ')', 'ig'); | |
146 var navTransform = '<span class="search-highlighted">$1</span>'; | |
147 | |
148 // Generate a regular expression and transform string for searching the | |
149 // pages. | |
150 var sectionRegEx = | |
151 new RegExp('>([^<]*)?(\\b' + searchText + ')([^>]*)?<', 'ig'); | |
152 var sectionTransform = '>$1<span class="search-highlighted">$2</span>$3<'; | |
153 | |
154 // Initialize all sections. If the search string matches a title page, | |
155 // show sections for that page. | |
156 var pagesToSearch = this.getSearchablePages_(); | |
145 for (var key in pagesToSearch) { | 157 for (var key in pagesToSearch) { |
146 var page = pagesToSearch[key]; | 158 var page = pagesToSearch[key]; |
147 var pageTitle = page.title.toLowerCase(); | 159 this.unhighlightMatches_(page.tab); |
148 // Hide non-sections in each page. | 160 this.unhighlightMatches_(page.pageDiv); |
161 var pageMatch = false; | |
162 if (searchText.length) { | |
163 pageMatch = this.performReplace_(navRegEx, navTransform, page.tab); | |
164 } | |
165 if (pageMatch) | |
166 foundMatches = true; | |
149 for (var i = 0; i < page.pageDiv.childNodes.length; i++) { | 167 for (var i = 0; i < page.pageDiv.childNodes.length; i++) { |
150 var childDiv = page.pageDiv.childNodes[i]; | 168 var childDiv = page.pageDiv.childNodes[i]; |
151 if (childDiv.nodeType == 1 && | 169 if (childDiv.nodeType == 1 && |
152 childDiv.nodeName.toLowerCase() == 'section') { | 170 childDiv.nodeName.toLowerCase() == 'section') { |
153 if (pageTitle == searchText) { | 171 if (pageMatch) { |
154 childDiv.classList.remove('search-hidden'); | 172 childDiv.classList.remove('search-hidden'); |
155 foundMatches = true; | |
156 } else { | 173 } else { |
157 childDiv.classList.add('search-hidden'); | 174 childDiv.classList.add('search-hidden'); |
158 } | 175 } |
159 } | 176 } |
160 } | 177 } |
161 } | 178 } |
162 | 179 |
163 // Now search all sections for anchored string matches. | 180 // Search all sections for anchored string matches. |
164 if (!foundMatches && searchText.length) { | 181 if (searchText.length) { |
165 var searchRegEx = new RegExp('\\b' + searchText, 'i'); | |
166 for (var key in pagesToSearch) { | 182 for (var key in pagesToSearch) { |
167 var page = pagesToSearch[key]; | 183 var page = pagesToSearch[key]; |
168 for (var i = 0; i < page.pageDiv.childNodes.length; i++) { | 184 for (var i = 0; i < page.pageDiv.childNodes.length; i++) { |
169 var childDiv = page.pageDiv.childNodes[i]; | 185 var childDiv = page.pageDiv.childNodes[i]; |
170 if (childDiv.nodeType == 1 && | 186 if (childDiv.nodeType == 1 && |
171 childDiv.nodeName.toLowerCase() == 'section') { | 187 childDiv.nodeName.toLowerCase() == 'section' && |
172 var isMatch = false; | 188 this.performReplace_(sectionRegEx, sectionTransform, |
173 var sectionElements = childDiv.getElementsByTagName("*"); | 189 childDiv)) { |
174 var length = sectionElements.length; | 190 childDiv.classList.remove('search-hidden'); |
175 var element; | 191 foundMatches = true; |
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 } | 192 } |
188 } | 193 } |
189 } | 194 } |
190 } | 195 } |
191 | 196 |
192 // Configure elements on the search results page based on search results. | 197 // Configure elements on the search results page based on search results. |
193 if (searchText.length == 0) { | 198 if (searchText.length == 0) { |
194 $('searchPageInfo').classList.remove('search-hidden'); | 199 $('searchPageInfo').classList.remove('search-hidden'); |
195 $('searchPageNoMatches').classList.add('search-hidden'); | 200 $('searchPageNoMatches').classList.add('search-hidden'); |
196 } else if (foundMatches) { | 201 } else if (foundMatches) { |
197 $('searchPageInfo').classList.add('search-hidden'); | 202 $('searchPageInfo').classList.add('search-hidden'); |
198 $('searchPageNoMatches').classList.add('search-hidden'); | 203 $('searchPageNoMatches').classList.add('search-hidden'); |
199 } else { | 204 } else { |
200 $('searchPageInfo').classList.add('search-hidden'); | 205 $('searchPageInfo').classList.add('search-hidden'); |
201 $('searchPageNoMatches').classList.remove('search-hidden'); | 206 $('searchPageNoMatches').classList.remove('search-hidden'); |
202 } | 207 } |
208 }, | |
209 | |
210 /** | |
211 * Performs a string replacement based on a regex and transform. | |
212 * @param {RegEx} regex A regular expression for finding search matches. | |
213 * @param {String} transform A string to apply the replace operation. | |
214 * @param {Element} element An HTML container element. | |
215 * @returns {Boolean} true if the element was changed. | |
216 * @private | |
217 */ | |
218 performReplace_: function(regex, transform, element) { | |
219 var originalHTML = element.innerHTML; | |
220 var newHTML = originalHTML.replace(regex, transform); | |
221 if (originalHTML != newHTML) { | |
222 element.innerHTML = newHTML; | |
223 return true; | |
224 } else { | |
225 return false; | |
226 } | |
227 }, | |
228 | |
229 /** | |
230 * Removes all search highlight tags from a container element. | |
231 * @param {Element} element An HTML container element. | |
232 * @private | |
233 */ | |
234 unhighlightMatches_: function(element) { | |
235 var regex = | |
236 new RegExp('<span class="search-highlighted">(.*?)</span>', 'g'); | |
237 element.innerHTML = element.innerHTML.replace(regex, '$1'); | |
238 }, | |
239 | |
240 /** | |
241 * Build a list of pages to search. Omit the search page. | |
James Hawkins
2010/12/09 18:40:15
Builds
James Hawkins
2010/12/09 18:40:15
Omits
csilv
2010/12/09 18:55:10
Done.
csilv
2010/12/09 18:55:10
Done.
| |
242 * @returns {Array} An array of pages to search. | |
243 * @private | |
244 */ | |
245 getSearchablePages_: function() { | |
246 var pages = []; | |
247 for (var name in OptionsPage.registeredPages) { | |
248 if (name != this.name) | |
249 pages.push(OptionsPage.registeredPages[name]); | |
250 } | |
251 return pages; | |
203 } | 252 } |
204 }; | 253 }; |
205 | 254 |
206 // Export | 255 // Export |
207 return { | 256 return { |
208 SearchPage: SearchPage | 257 SearchPage: SearchPage |
209 }; | 258 }; |
210 | 259 |
211 }); | 260 }); |
OLD | NEW |