OLD | NEW |
---|---|
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 /** | |
6 * @typedef {{ | |
7 * id: number, | |
8 * rawQuery: ?string, | |
9 * regExp: ?RegExp | |
10 * }} | |
Dan Beam
2016/07/12 00:08:42
nit: /** @typedef {{id: number, rawQuery: ?string,
dpapad
2016/07/12 02:20:28
Done. I don't think this is consistent with other
Dan Beam
2016/07/13 01:26:11
blame
english
for
reading
from
left
to
right
rathe
dpapad
2016/07/13 02:27:01
[sarcasm follows]
Thanks for the insightful answer
| |
11 */ | |
12 var SearchContext; | |
13 | |
5 cr.define('settings', function() { | 14 cr.define('settings', function() { |
6 /** @const {string} */ | 15 /** @const {string} */ |
7 var WRAPPER_CSS_CLASS = 'search-highlight-wrapper'; | 16 var WRAPPER_CSS_CLASS = 'search-highlight-wrapper'; |
8 | 17 |
9 /** @const {string} */ | 18 /** @const {string} */ |
10 var HIT_CSS_CLASS = 'search-highlight-hit'; | 19 var HIT_CSS_CLASS = 'search-highlight-hit'; |
11 | 20 |
12 /** @const {!RegExp} */ | 21 /** @const {!RegExp} */ |
13 var SANITIZE_REGEX = /[-[\]{}()*+?.,\\^$|#\s]/g; | 22 var SANITIZE_REGEX = /[-[\]{}()*+?.,\\^$|#\s]/g; |
Dan Beam
2016/07/12 00:08:42
can this live inside of SearchManager#search?
dpapad
2016/07/12 02:20:28
Moved it to SearchManager.SANITIZE_REGEX. Trying t
| |
14 | 23 |
15 /** | 24 /** |
16 * List of elements types that should not be searched at all. | 25 * List of elements types that should not be searched at all. |
17 * The only DOM-MODULE node is in <body> which is not searched, therefore | 26 * The only DOM-MODULE node is in <body> which is not searched, therefore |
18 * DOM-MODULE is not needed in this set. | 27 * DOM-MODULE is not needed in this set. |
19 * @const {!Set<string>} | 28 * @const {!Set<string>} |
20 */ | 29 */ |
21 var IGNORED_ELEMENTS = new Set([ | 30 var IGNORED_ELEMENTS = new Set([ |
Dan Beam
2016/07/12 00:08:42
this should probably live inside of SearchTask, no
dpapad
2016/07/12 02:20:27
This is accessed by the top level private function
| |
22 'CONTENT', | 31 'CONTENT', |
23 'CR-EVENTS', | 32 'CR-EVENTS', |
24 'IMG', | 33 'IMG', |
25 'IRON-ICON', | 34 'IRON-ICON', |
26 'IRON-LIST', | 35 'IRON-LIST', |
27 'PAPER-ICON-BUTTON', | 36 'PAPER-ICON-BUTTON', |
28 /* TODO(dpapad): paper-item is used for dynamically populated dropdown | 37 /* TODO(dpapad): paper-item is used for dynamically populated dropdown |
29 * menus. Perhaps a better approach is to mark the entire dropdown menu such | 38 * menus. Perhaps a better approach is to mark the entire dropdown menu such |
30 * that search algorithm can skip it as a whole instead. | 39 * that search algorithm can skip it as a whole instead. |
31 */ | 40 */ |
32 'PAPER-ITEM', | 41 'PAPER-ITEM', |
33 'PAPER-RIPPLE', | 42 'PAPER-RIPPLE', |
34 'PAPER-SLIDER', | 43 'PAPER-SLIDER', |
35 'PAPER-SPINNER', | 44 'PAPER-SPINNER', |
36 'STYLE', | 45 'STYLE', |
37 'TEMPLATE', | 46 'TEMPLATE', |
38 ]); | 47 ]); |
39 | 48 |
40 /** | 49 /** |
41 * Finds all previous highlighted nodes under |node| (both within self and | 50 * Finds all previous highlighted nodes under |node| (both within self and |
42 * children's Shadow DOM) and removes the highlight (yellow rectangle). | 51 * children's Shadow DOM) and removes the highlight (yellow rectangle). |
43 * @param {!Node} node | 52 * @param {!Node} node |
44 * @private | 53 * @private |
45 */ | 54 */ |
46 function findAndRemoveHighlights_(node) { | 55 function findAndRemoveHighlights_(node) { |
Dan Beam
2016/07/12 00:08:43
why is this used from only one place? it looks li
dpapad
2016/07/12 02:20:27
As discussed, it is pulled into a function because
| |
47 var wrappers = node.querySelectorAll('* /deep/ .' + WRAPPER_CSS_CLASS); | 56 var wrappers = node.querySelectorAll('* /deep/ .' + WRAPPER_CSS_CLASS); |
48 | 57 |
49 for (var wrapper of wrappers) { | 58 for (var wrapper of wrappers) { |
50 var hitElements = wrapper.querySelectorAll('.' + HIT_CSS_CLASS); | 59 var hitElements = wrapper.querySelectorAll('.' + HIT_CSS_CLASS); |
51 // For each hit element, remove the highlighting. | 60 // For each hit element, remove the highlighting. |
52 for (var hitElement of hitElements) { | 61 for (var hitElement of hitElements) { |
53 wrapper.replaceChild(hitElement.firstChild, hitElement); | 62 wrapper.replaceChild(hitElement.firstChild, hitElement); |
54 } | 63 } |
55 | 64 |
56 // Normalize so that adjacent text nodes will be combined. | 65 // Normalize so that adjacent text nodes will be combined. |
(...skipping 12 matching lines...) Expand all Loading... | |
69 * Applies the highlight UI (yellow rectangle) around all matches in |node|. | 78 * Applies the highlight UI (yellow rectangle) around all matches in |node|. |
70 * @param {!Node} node The text node to be highlighted. |node| ends up | 79 * @param {!Node} node The text node to be highlighted. |node| ends up |
71 * being removed from the DOM tree. | 80 * being removed from the DOM tree. |
72 * @param {!Array<string>} tokens The string tokens after splitting on the | 81 * @param {!Array<string>} tokens The string tokens after splitting on the |
73 * relevant regExp. Even indices hold text that doesn't need highlighting, | 82 * relevant regExp. Even indices hold text that doesn't need highlighting, |
74 * odd indices hold the text to be highlighted. For example: | 83 * odd indices hold the text to be highlighted. For example: |
75 * var r = new RegExp('(foo)', 'i'); | 84 * var r = new RegExp('(foo)', 'i'); |
76 * 'barfoobar foo bar'.split(r) => ['bar', 'foo', 'bar ', 'foo', ' bar'] | 85 * 'barfoobar foo bar'.split(r) => ['bar', 'foo', 'bar ', 'foo', ' bar'] |
77 * @private | 86 * @private |
78 */ | 87 */ |
79 function highlight_(node, tokens) { | 88 function highlight_(node, tokens) { |
Dan Beam
2016/07/12 00:08:42
why is this used only once?
dpapad
2016/07/12 02:20:27
Used only once because it is needed once. Reason i
| |
80 var wrapper = document.createElement('span'); | 89 var wrapper = document.createElement('span'); |
81 wrapper.classList.add(WRAPPER_CSS_CLASS); | 90 wrapper.classList.add(WRAPPER_CSS_CLASS); |
82 // Use existing node as placeholder to determine where to insert the | 91 // Use existing node as placeholder to determine where to insert the |
83 // replacement content. | 92 // replacement content. |
84 node.parentNode.replaceChild(wrapper, node); | 93 node.parentNode.replaceChild(wrapper, node); |
85 | 94 |
86 for (var i = 0; i < tokens.length; ++i) { | 95 for (var i = 0; i < tokens.length; ++i) { |
87 if (i % 2 == 0) { | 96 if (i % 2 == 0) { |
88 wrapper.appendChild(document.createTextNode(tokens[i])); | 97 wrapper.appendChild(document.createTextNode(tokens[i])); |
89 } else { | 98 } else { |
90 var span = document.createElement('span'); | 99 var span = document.createElement('span'); |
91 span.classList.add(HIT_CSS_CLASS); | 100 span.classList.add(HIT_CSS_CLASS); |
92 span.style.backgroundColor = 'yellow'; | 101 span.style.backgroundColor = 'yellow'; |
93 span.textContent = tokens[i]; | 102 span.textContent = tokens[i]; |
94 wrapper.appendChild(span); | 103 wrapper.appendChild(span); |
95 } | 104 } |
96 } | 105 } |
97 } | 106 } |
98 | 107 |
99 /** | 108 /** |
109 * Checks whether the given |node| requires force rendering. | |
110 * | |
111 * @param {!SearchContext} context | |
112 * @param {!Node} node | |
113 * @return {boolean} Whether a forced rendering task was scheduled. | |
114 * @private | |
115 */ | |
116 function forceRenderNeeded_(context, node) { | |
117 if (node.nodeName != 'TEMPLATE' || | |
118 !node.hasAttribute('name') || | |
119 node.if) { | |
Dan Beam
2016/07/12 00:08:42
nit:
if (node.nodeName != 'TEMPLATE' || !node.has
dpapad
2016/07/12 02:20:28
Done.
| |
120 return false; | |
121 } | |
122 | |
123 // TODO(dpapad): Temporarily ignore site-settings because it throws an | |
124 // assertion error during force-rendering. | |
125 if (node.getAttribute('name').indexOf('site-') != -1) | |
126 return false; | |
127 | |
128 return true; | |
Dan Beam
2016/07/12 00:08:42
nit:
return node.getAttribute('name').indexOf('si
dpapad
2016/07/12 02:20:27
Done.
| |
129 } | |
130 | |
131 /** | |
100 * Traverses the entire DOM (including Shadow DOM), finds text nodes that | 132 * Traverses the entire DOM (including Shadow DOM), finds text nodes that |
101 * match the given regular expression and applies the highlight UI. It also | 133 * match the given regular expression and applies the highlight UI. It also |
102 * ensures that <settings-section> instances become visible if any matches | 134 * ensures that <settings-section> instances become visible if any matches |
103 * occurred under their subtree. | 135 * occurred under their subtree. |
104 * | 136 * |
105 * @param {!Element} page The page to be searched, should be either | 137 * @param {!SearchContext} context |
106 * <settings-basic-page> or <settings-advanced-page>. | 138 * @param {!Node} root The root of the sub-tree to be searched |
107 * @param {!RegExp} regExp The regular expression to detect matches. | |
108 * @private | 139 * @private |
109 */ | 140 */ |
110 function findAndHighlightMatches_(page, regExp) { | 141 function findAndHighlightMatches_(context, root) { |
111 function doSearch(node) { | 142 function doSearch(node) { |
112 if (IGNORED_ELEMENTS.has(node.tagName)) | 143 if (forceRenderNeeded_(context, node)) { |
144 SearchManager.getInstance().queue_.addRenderTask( | |
145 new RenderTask(context, node)); | |
146 return; | |
147 } | |
148 | |
149 | |
150 if (IGNORED_ELEMENTS.has(node.nodeName)) | |
113 return; | 151 return; |
114 | 152 |
115 if (node.nodeType == Node.TEXT_NODE) { | 153 if (node.nodeType == Node.TEXT_NODE) { |
116 var textContent = node.nodeValue.trim(); | 154 var textContent = node.nodeValue.trim(); |
117 if (textContent.length == 0) | 155 if (textContent.length == 0) |
118 return; | 156 return; |
119 | 157 |
120 if (regExp.test(textContent)) { | 158 if (context.regExp.test(textContent)) { |
121 revealParentSection_(node); | 159 revealParentSection_(node); |
122 highlight_(node, textContent.split(regExp)); | 160 highlight_(node, textContent.split(context.regExp)); |
123 } | 161 } |
124 // Returning early since TEXT_NODE nodes never have children. | 162 // Returning early since TEXT_NODE nodes never have children. |
125 return; | 163 return; |
126 } | 164 } |
127 | 165 |
128 var child = node.firstChild; | 166 var child = node.firstChild; |
129 while (child !== null) { | 167 while (child !== null) { |
130 // Getting a reference to the |nextSibling| before calling doSearch() | 168 // Getting a reference to the |nextSibling| before calling doSearch() |
131 // because |child| could be removed from the DOM within doSearch(). | 169 // because |child| could be removed from the DOM within doSearch(). |
132 var nextSibling = child.nextSibling; | 170 var nextSibling = child.nextSibling; |
133 doSearch(child); | 171 doSearch(child); |
134 child = nextSibling; | 172 child = nextSibling; |
135 } | 173 } |
136 | 174 |
137 var shadowRoot = node.shadowRoot; | 175 var shadowRoot = node.shadowRoot; |
138 if (shadowRoot) | 176 if (shadowRoot) |
139 doSearch(shadowRoot); | 177 doSearch(shadowRoot); |
140 } | 178 } |
141 | 179 |
142 doSearch(page); | 180 doSearch(root); |
143 } | 181 } |
144 | 182 |
145 /** | 183 /** |
146 * Finds and makes visible the <settings-section> parent of |node|. | 184 * Finds and makes visible the <settings-section> parent of |node|. |
147 * @param {!Node} node | 185 * @param {!Node} node |
148 */ | 186 */ |
149 function revealParentSection_(node) { | 187 function revealParentSection_(node) { |
Dan Beam
2016/07/12 00:08:43
why is this used only once?
dpapad
2016/07/12 02:20:27
It is used only once, because it is needed only on
| |
150 // Find corresponding SETTINGS-SECTION parent and make it visible. | 188 // Find corresponding SETTINGS-SECTION parent and make it visible. |
151 var parent = node; | 189 var parent = node; |
152 while (parent && parent.tagName !== 'SETTINGS-SECTION') { | 190 while (parent && parent.nodeName !== 'SETTINGS-SECTION') { |
153 parent = parent.nodeType == Node.DOCUMENT_FRAGMENT_NODE ? | 191 parent = parent.nodeType == Node.DOCUMENT_FRAGMENT_NODE ? |
154 parent.host : parent.parentNode; | 192 parent.host : parent.parentNode; |
155 } | 193 } |
156 if (parent) | 194 if (parent) |
157 parent.hidden = false; | 195 parent.hidden = false; |
158 } | 196 } |
159 | 197 |
160 /** | 198 /** |
161 * @param {!Element} page | 199 * @param {!Node} page |
162 * @param {boolean} visible | 200 * @param {boolean} visible |
163 * @private | 201 * @private |
164 */ | 202 */ |
165 function setSectionsVisibility_(page, visible) { | 203 function setSectionsVisibility_(page, visible) { |
Dan Beam
2016/07/12 00:08:42
why is this used only once?
dpapad
2016/07/12 02:20:27
Inlined.
| |
166 var sections = Polymer.dom(page.root).querySelectorAll('settings-section'); | 204 var sections = Polymer.dom(page.root).querySelectorAll('settings-section'); |
167 for (var i = 0; i < sections.length; i++) | 205 for (var i = 0; i < sections.length; i++) |
168 sections[i].hidden = !visible; | 206 sections[i].hidden = !visible; |
169 } | 207 } |
170 | 208 |
171 /** | 209 /** |
172 * Performs hierarchical search, starting at the given page element. | 210 * @constructor |
211 * | |
212 * @param {!SearchContext} context | |
213 * @param {!Node} node | |
214 */ | |
215 function Task(context, node) { | |
216 /** @protected {!SearchContext} */ | |
217 this.context = context; | |
218 | |
219 /** @protected {!Node} */ | |
220 this.node = node; | |
221 } | |
222 | |
223 Task.prototype = { | |
224 /** | |
225 * @abstract | |
226 * @return {!Promise} | |
227 */ | |
228 exec: function() {}, | |
229 }; | |
230 | |
231 /** | |
232 * @constructor | |
233 * @extends {Task} | |
234 * | |
235 * @param {!SearchContext} context | |
236 * @param {!Node} node | |
237 */ | |
238 function RenderTask(context, node) { | |
239 Task.call(this, context, node); | |
240 } | |
241 | |
242 RenderTask.prototype = { | |
Dan Beam
2016/07/12 00:08:42
can you make this clearer that we're rendering dom
dpapad
2016/07/12 02:20:27
I added a description in the constructor. Given th
| |
243 /** @override */ | |
244 exec: function() { | |
245 var subpageTemplate = | |
246 this.node['_content'].querySelector('settings-subpage'); | |
Dan Beam
2016/07/12 00:08:42
why can't you use HTMLTemplateElement#content?
ht
dpapad
2016/07/12 02:20:28
They are not the same. I don't know the details of
| |
247 subpageTemplate.id = subpageTemplate.id || this.node.getAttribute('name'); | |
248 assert(!this.node.if); | |
249 this.node.if = true; | |
250 | |
251 return new Promise(function(resolve, reject) { | |
252 var parent = this.node.parentNode; | |
253 parent.async(function() { | |
254 var renderedNode = parent.querySelector('#' + subpageTemplate.id); | |
255 // Register a SearchTask for the part of the DOM that was just | |
256 // rendered. | |
257 SearchManager.getInstance().queue_.addSearchTask( | |
258 new SearchTask(this.context, renderedNode)); | |
Dan Beam
2016/07/12 00:08:42
I still think it'd be nice if RenderTask didn't ne
dpapad
2016/07/12 02:20:27
Acknowledged.
| |
259 resolve(); | |
260 }.bind(this)); | |
261 }.bind(this)); | |
262 }, | |
263 }; | |
264 | |
265 /** | |
266 * @constructor | |
267 * @extends {Task} | |
268 * | |
269 * @param {!SearchContext} context | |
270 * @param {!Node} node | |
271 */ | |
272 function SearchTask(context, node) { | |
Dan Beam
2016/07/12 00:08:42
can this be a HighlightMatches task?
dpapad
2016/07/12 02:20:27
I find the proposed name confusing, for the follow
Dan Beam
2016/07/13 01:26:11
I find the existing name confusing that it doesn't
dpapad
2016/07/13 02:27:01
Renamed to SearchAndHighlightTask. Technically wit
| |
273 Task.call(this, context, node); | |
274 } | |
275 | |
276 SearchTask.prototype = { | |
277 /** @override */ | |
278 exec: function() { | |
279 findAndHighlightMatches_(this.context, this.node); | |
280 return Promise.resolve(); | |
281 }, | |
282 }; | |
283 | |
284 /** | |
285 * @constructor | |
286 * @extends {Task} | |
287 * | |
288 * @param {!SearchContext} context | |
289 * @param {!Node} page | |
290 */ | |
291 function TopLevelSearchTask(context, page) { | |
292 Task.call(this, context, page); | |
293 } | |
294 | |
295 TopLevelSearchTask.prototype = { | |
296 /** @override */ | |
297 exec: function() { | |
298 findAndRemoveHighlights_(this.node); | |
299 | |
300 var shouldSearch = this.context.regExp !== null; | |
301 setSectionsVisibility_(this.node, !shouldSearch); | |
302 if (shouldSearch) | |
303 findAndHighlightMatches_(this.context, this.node); | |
Dan Beam
2016/07/12 00:08:42
how is this part different than starting a SearchT
dpapad
2016/07/12 02:20:27
SearchTask and TopLevelSearchTask have a small ove
| |
304 | |
305 return Promise.resolve(); | |
306 }, | |
307 }; | |
308 | |
309 /** | |
310 * @constructor | |
311 */ | |
312 function TaskQueue() { | |
313 /** | |
314 * @private {{ | |
315 * high: !Array<!Task>, | |
316 * middle: !Array<!Task>, | |
317 * low: !Array<!Task> | |
318 * }} | |
319 */ | |
320 this.queues_ = {high: [], middle: [], low: []}; | |
321 | |
322 /** @private {?Task} */ | |
323 this.activeTask_ = null; | |
324 } | |
325 | |
326 TaskQueue.prototype = { | |
327 /** Drops all tasks. */ | |
328 reset: function() { | |
329 this.queues_.high.length = 0; | |
330 this.queues_.middle.length = 0; | |
331 this.queues_.low.length = 0; | |
332 }, | |
333 | |
334 /** @param {!TopLevelSearchTask} task */ | |
335 addTopLevelSearchTask: function(task) { | |
336 this.queues_.high.push(task); | |
337 this.consumePending_(); | |
338 }, | |
339 | |
340 /** @param {!SearchTask} task */ | |
341 addSearchTask: function(task) { | |
342 this.queues_.middle.push(task); | |
343 this.consumePending_(); | |
344 }, | |
345 | |
346 /** @param {!RenderTask} task */ | |
347 addRenderTask: function(task) { | |
348 this.queues_.low.push(task); | |
349 this.consumePending_(); | |
350 }, | |
351 | |
352 /** | |
353 * @return {?Task} | |
Dan Beam
2016/07/12 00:08:42
is this type still correct?
dpapad
2016/07/12 02:20:27
Compiler seems to accept it. I am not 100% if the
| |
354 * @private | |
355 */ | |
356 popNextTask_: function() { | |
357 return this.queues_.high.shift() || | |
358 this.queues_.middle.shift() || | |
359 this.queues_.low.shift(); | |
360 }, | |
361 | |
362 /** | |
363 * @param {!Task} task | |
364 * @return {boolean} Whether the given task is now obsolete (refers to a | |
365 * previous search query). | |
366 * @private | |
367 */ | |
368 isTaskObsolete_: function(task) { | |
369 return task.context.id != SearchManager.getInstance().activeContext_.id; | |
370 }, | |
371 | |
372 /** @private */ | |
373 consumePending_: function() { | |
374 if (this.activeTask_) | |
375 return; | |
376 | |
377 while (1) { | |
378 this.activeTask_ = this.popNextTask_(); | |
379 if (!this.activeTask_) | |
380 return; | |
381 | |
382 if (this.isTaskObsolete_(this.activeTask_)) { | |
383 // Dropping this task without ever executing it, since a new search | |
384 // has been issued since this task was queued. | |
385 continue; | |
386 } | |
387 | |
388 window.requestIdleCallback(function() { | |
389 // Need to check if this task is obsolete again, since a new search | |
390 // might have been issued after requestIdleCallback() was called. | |
391 if (this.isTaskObsolete_(this.activeTask_)) { | |
392 this.activeTask_ = null; | |
393 this.consumePending_(); | |
394 } else { | |
395 this.activeTask_.exec().then(function(result) { | |
396 this.activeTask_ = null; | |
397 this.consumePending_(); | |
398 }.bind(this)); | |
399 } | |
400 }.bind(this)); | |
401 return; | |
402 } | |
403 }, | |
404 }; | |
405 | |
406 /** | |
407 * @constructor | |
408 */ | |
409 var SearchManager = function() { | |
410 /** @private {!TaskQueue} */ | |
411 this.queue_ = new TaskQueue(); | |
412 | |
413 /** @private {!SearchContext} */ | |
414 this.activeContext_ = { | |
415 id: 0, rawQuery: null, regExp: null, | |
416 }; | |
Dan Beam
2016/07/12 00:08:42
nit: fits in 1 line
dpapad
2016/07/12 02:20:28
Done.
| |
417 }; | |
418 cr.addSingletonGetter(SearchManager); | |
419 | |
420 SearchManager.prototype = { | |
421 /** | |
422 * @param {string} text The text to search for. | |
423 * @param {!Node} page | |
424 */ | |
425 search: function(text, page) { | |
426 if (this.activeContext_.rawQuery != text) { | |
Dan Beam
2016/07/12 00:08:42
why are we doing anything if text == this.activeCo
dpapad
2016/07/12 02:20:27
We are not. <cr-toolbar> guarantees that no event
| |
427 var newId = this.activeContext_.id + 1; | |
428 | |
429 var regExp = null; | |
430 // Generate search text by escaping any characters that would be | |
431 // problematic for regular expressions. | |
432 var searchText = text.trim().replace(SANITIZE_REGEX, '\\$&'); | |
433 if (searchText.length > 0) | |
434 regExp = new RegExp('(' + searchText + ')', 'i'); | |
435 | |
436 this.activeContext_ = { | |
437 id: newId, rawQuery: text, regExp: regExp, | |
438 }; | |
Dan Beam
2016/07/12 00:08:42
nit: fits in 1 line?
dpapad
2016/07/12 02:20:27
Done.
| |
439 | |
440 // Drop all previously scheduled tasks, since a new search was just | |
441 // issued. | |
442 this.queue_.reset(); | |
443 } | |
444 | |
445 this.queue_.addTopLevelSearchTask( | |
446 new TopLevelSearchTask(this.activeContext_, page)); | |
447 }, | |
448 }; | |
449 | |
450 /** | |
173 * @param {string} text | 451 * @param {string} text |
174 * @param {!Element} page Must be either <settings-basic-page> or | 452 * @param {!Node} page |
175 * <settings-advanced-page>. | |
176 */ | 453 */ |
177 function search(text, page) { | 454 function search(text, page) { |
178 findAndRemoveHighlights_(page); | 455 SearchManager.getInstance().search(text, page); |
179 | |
180 // Generate search text by escaping any characters that would be problematic | |
181 // for regular expressions. | |
182 var searchText = text.trim().replace(SANITIZE_REGEX, '\\$&'); | |
183 if (searchText.length == 0) { | |
184 setSectionsVisibility_(page, true); | |
185 return; | |
186 } | |
187 | |
188 setSectionsVisibility_(page, false); | |
189 findAndHighlightMatches_(page, new RegExp('(' + searchText + ')', 'i')); | |
190 } | 456 } |
191 | 457 |
192 return { | 458 return { |
193 search: search, | 459 search: search, |
194 }; | 460 }; |
195 }); | 461 }); |
OLD | NEW |