OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 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 /** | 5 /** |
6 * Section IDs use for metrics. The integer values should match up with the | 6 * Section IDs use for metrics. The integer values should match up with the |
7 * |SettingsSections| in histograms.xml. | 7 * |SettingsSections| in histograms.xml. |
8 * @type {Object<string, number>} | 8 * @type {Object<string, number>} |
9 */ | 9 */ |
10 var SettingsSections = { | 10 var SettingsSections = { |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
148 */ | 148 */ |
149 updatePosition: function() { | 149 updatePosition: function() { |
150 // This bubble is 'owned' by the next sibling. | 150 // This bubble is 'owned' by the next sibling. |
151 var owner = (this.wrapper || this).nextSibling; | 151 var owner = (this.wrapper || this).nextSibling; |
152 | 152 |
153 // If there isn't an offset parent, we have nothing to do. | 153 // If there isn't an offset parent, we have nothing to do. |
154 if (!owner.offsetParent) | 154 if (!owner.offsetParent) |
155 return; | 155 return; |
156 | 156 |
157 // Position the bubble below the location of the owner. | 157 // Position the bubble below the location of the owner. |
158 var left = owner.offsetLeft + owner.offsetWidth / 2 - | 158 var left = |
159 this.offsetWidth / 2; | 159 owner.offsetLeft + owner.offsetWidth / 2 - this.offsetWidth / 2; |
160 | 160 |
161 var BUBBLE_EDGE_OFFSET = 5; | 161 var BUBBLE_EDGE_OFFSET = 5; |
162 var top = owner.offsetTop; | 162 var top = owner.offsetTop; |
163 if (this.innards_.classList.contains('above')) | 163 if (this.innards_.classList.contains('above')) |
164 top -= this.offsetHeight + BUBBLE_EDGE_OFFSET; | 164 top -= this.offsetHeight + BUBBLE_EDGE_OFFSET; |
165 else | 165 else |
166 top += owner.offsetHeight + BUBBLE_EDGE_OFFSET; | 166 top += owner.offsetHeight + BUBBLE_EDGE_OFFSET; |
167 | 167 |
168 // Update the position in the CSS. Cache the last values for | 168 // Update the position in the CSS. Cache the last values for |
169 // best performance. | 169 // best performance. |
170 if (left != this.lastLeft) { | 170 if (left != this.lastLeft) { |
171 this.style.left = left + 'px'; | 171 this.style.left = left + 'px'; |
172 this.lastLeft = left; | 172 this.lastLeft = left; |
173 } | 173 } |
174 if (top != this.lastTop) { | 174 if (top != this.lastTop) { |
175 this.style.top = top + 'px'; | 175 this.style.top = top + 'px'; |
176 this.lastTop = top; | 176 this.lastTop = top; |
177 } | 177 } |
178 }, | 178 }, |
179 }; | 179 }; |
180 | 180 |
181 /** | 181 /** |
182 * Encapsulated handling of the search page. | 182 * Encapsulated handling of the search page. |
183 * @constructor | 183 * @constructor |
184 * @extends {cr.ui.pageManager.Page} | 184 * @extends {cr.ui.pageManager.Page} |
185 */ | 185 */ |
186 function SearchPage() { | 186 function SearchPage() { |
187 Page.call(this, 'search', | 187 Page.call( |
188 loadTimeData.getString('searchPageTabTitle'), | 188 this, 'search', loadTimeData.getString('searchPageTabTitle'), |
189 'searchPage'); | 189 'searchPage'); |
190 } | 190 } |
191 | 191 |
192 cr.addSingletonGetter(SearchPage); | 192 cr.addSingletonGetter(SearchPage); |
193 | 193 |
194 SearchPage.prototype = { | 194 SearchPage.prototype = { |
195 // Inherit SearchPage from Page. | 195 // Inherit SearchPage from Page. |
196 __proto__: Page.prototype, | 196 __proto__: Page.prototype, |
197 | 197 |
198 /** | 198 /** |
199 * Wait a bit to see if the user is still entering search text. | 199 * Wait a bit to see if the user is still entering search text. |
(...skipping 25 matching lines...) Expand all Loading... |
225 | 225 |
226 this.searchField = $('search-field'); | 226 this.searchField = $('search-field'); |
227 | 227 |
228 // Handle search events. (No need to throttle, WebKit's search field | 228 // Handle search events. (No need to throttle, WebKit's search field |
229 // will do that automatically.) | 229 // will do that automatically.) |
230 this.searchField.onsearch = function(e) { | 230 this.searchField.onsearch = function(e) { |
231 this.setSearchText_(e.currentTarget.value); | 231 this.setSearchText_(e.currentTarget.value); |
232 }.bind(this); | 232 }.bind(this); |
233 | 233 |
234 // Install handler for key presses. | 234 // Install handler for key presses. |
235 document.addEventListener('keydown', | 235 document.addEventListener( |
236 this.keyDownEventHandler_.bind(this)); | 236 'keydown', this.keyDownEventHandler_.bind(this)); |
237 }, | 237 }, |
238 | 238 |
239 /** @override */ | 239 /** @override */ |
240 get sticky() { | 240 get sticky() { |
241 return true; | 241 return true; |
242 }, | 242 }, |
243 | 243 |
244 /** @override */ | 244 /** @override */ |
245 didShowPage: function() { | 245 didShowPage: function() { |
246 // This method is called by the PageManager after all pages have had their | 246 // This method is called by the PageManager after all pages have had their |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
348 /** | 348 /** |
349 * Set the current search criteria. | 349 * Set the current search criteria. |
350 * @param {string} text Search text. | 350 * @param {string} text Search text. |
351 * @private | 351 * @private |
352 */ | 352 */ |
353 setSearchText_: function(text) { | 353 setSearchText_: function(text) { |
354 if (!ShouldEnableSearch()) | 354 if (!ShouldEnableSearch()) |
355 return; | 355 return; |
356 | 356 |
357 // Prevent recursive execution of this method. | 357 // Prevent recursive execution of this method. |
358 if (this.insideSetSearchText_) return; | 358 if (this.insideSetSearchText_) |
| 359 return; |
359 this.insideSetSearchText_ = true; | 360 this.insideSetSearchText_ = true; |
360 | 361 |
361 // Cleanup the search query string. | 362 // Cleanup the search query string. |
362 text = SearchPage.canonicalizeQuery(text); | 363 text = SearchPage.canonicalizeQuery(text); |
363 | 364 |
364 // If the search string becomes empty, flip back to the default page. | 365 // If the search string becomes empty, flip back to the default page. |
365 if (!text) { | 366 if (!text) { |
366 if (this.searchActive_) | 367 if (this.searchActive_) |
367 PageManager.showDefaultPage(); | 368 PageManager.showDefaultPage(); |
368 this.insideSetSearchText_ = false; | 369 this.insideSetSearchText_ = false; |
369 return; | 370 return; |
370 } | 371 } |
371 | 372 |
372 if (!this.hasSentFirstSearchTime_) { | 373 if (!this.hasSentFirstSearchTime_) { |
373 this.hasSentFirstSearchTime_ = true; | 374 this.hasSentFirstSearchTime_ = true; |
374 chrome.metricsPrivate.recordMediumTime('Settings.TimeToFirstSearch', | 375 chrome.metricsPrivate.recordMediumTime( |
375 Date.now() - this.createdTimestamp_); | 376 'Settings.TimeToFirstSearch', Date.now() - this.createdTimestamp_); |
376 } | 377 } |
377 | 378 |
378 // Toggle the search page if necessary. Otherwise, update the hash. | 379 // Toggle the search page if necessary. Otherwise, update the hash. |
379 var hash = '#' + encodeURIComponent(text); | 380 var hash = '#' + encodeURIComponent(text); |
380 if (this.searchActive_) { | 381 if (this.searchActive_) { |
381 if (this.hash != hash) | 382 if (this.hash != hash) |
382 this.setHash(hash); | 383 this.setHash(hash); |
383 } else { | 384 } else { |
384 PageManager.showPageByName(this.name, true, {hash: hash}); | 385 PageManager.showPageByName(this.name, true, {hash: hash}); |
385 } | 386 } |
(...skipping 22 matching lines...) Expand all Loading... |
408 // that would be problematic for regular expressions. | 409 // that would be problematic for regular expressions. |
409 var searchText = | 410 var searchText = |
410 text.toLowerCase().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); | 411 text.toLowerCase().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); |
411 // Generate a regular expression for hilighting search terms. | 412 // Generate a regular expression for hilighting search terms. |
412 var regExp = new RegExp('(' + searchText + ')', 'ig'); | 413 var regExp = new RegExp('(' + searchText + ')', 'ig'); |
413 | 414 |
414 if (searchText.length) { | 415 if (searchText.length) { |
415 // Search all top-level sections for anchored string matches. | 416 // Search all top-level sections for anchored string matches. |
416 for (var i = 0; i < pagesToSearch.length; i++) { | 417 for (var i = 0; i < pagesToSearch.length; i++) { |
417 var page = pagesToSearch[i]; | 418 var page = pagesToSearch[i]; |
418 var elements = | 419 var elements = page.pageDiv.querySelectorAll('section'); |
419 page.pageDiv.querySelectorAll('section'); | |
420 for (var i = 0, node; node = elements[i]; i++) { | 420 for (var i = 0, node; node = elements[i]; i++) { |
421 if (this.highlightMatches_(regExp, node)) { | 421 if (this.highlightMatches_(regExp, node)) { |
422 node.classList.remove('search-hidden'); | 422 node.classList.remove('search-hidden'); |
423 if (!node.hidden) { | 423 if (!node.hidden) { |
424 foundMatches = true; | 424 foundMatches = true; |
425 pageMatchesForMetrics += 1; | 425 pageMatchesForMetrics += 1; |
426 var section = SettingsSections[node.id] || | 426 var section = |
427 SettingsSections['Unknown']; | 427 SettingsSections[node.id] || SettingsSections['Unknown']; |
428 sectionMatchesForMetrics[section] = section; | 428 sectionMatchesForMetrics[section] = section; |
429 } | 429 } |
430 } | 430 } |
431 } | 431 } |
432 } | 432 } |
433 | 433 |
434 // Search all sub-pages, generating an array of top-level sections that | 434 // Search all sub-pages, generating an array of top-level sections that |
435 // we need to make visible. | 435 // we need to make visible. |
436 var subPagesToSearch = this.getSearchableSubPages_(); | 436 var subPagesToSearch = this.getSearchableSubPages_(); |
437 var control, node; | 437 var control, node; |
(...skipping 17 matching lines...) Expand all Loading... |
455 // Create search balloons for sub-page results. | 455 // Create search balloons for sub-page results. |
456 var bubbleCount = bubbleControls.length; | 456 var bubbleCount = bubbleControls.length; |
457 for (var i = 0; i < bubbleCount; i++) | 457 for (var i = 0; i < bubbleCount; i++) |
458 this.createSearchBubble_(bubbleControls[i], text); | 458 this.createSearchBubble_(bubbleControls[i], text); |
459 | 459 |
460 // If the search doesn't change for one second, send some metrics. | 460 // If the search doesn't change for one second, send some metrics. |
461 clearTimeout(this.delayedSearchMetric_); | 461 clearTimeout(this.delayedSearchMetric_); |
462 this.delayedSearchMetric_ = setTimeout(function() { | 462 this.delayedSearchMetric_ = setTimeout(function() { |
463 if (!foundMatches) { | 463 if (!foundMatches) { |
464 chrome.metricsPrivate.recordSmallCount( | 464 chrome.metricsPrivate.recordSmallCount( |
465 'Settings.SearchLengthNoMatch', text.length); | 465 'Settings.SearchLengthNoMatch', text.length); |
466 chrome.metricsPrivate.recordSmallCount( | 466 chrome.metricsPrivate.recordSmallCount( |
467 'Settings.SearchSections', SettingsSections['None']); | 467 'Settings.SearchSections', SettingsSections['None']); |
468 } else { | 468 } else { |
469 for (var section in sectionMatchesForMetrics) { | 469 for (var section in sectionMatchesForMetrics) { |
470 var sectionId = sectionMatchesForMetrics[section]; | 470 var sectionId = sectionMatchesForMetrics[section]; |
471 assert(sectionId !== undefined); | 471 assert(sectionId !== undefined); |
472 chrome.metricsPrivate.recordSmallCount( | 472 chrome.metricsPrivate.recordSmallCount( |
473 'Settings.SearchSections', sectionId); | 473 'Settings.SearchSections', sectionId); |
474 } | 474 } |
475 } | 475 } |
476 | 476 |
477 chrome.metricsPrivate.recordUserAction('Settings.Searching'); | 477 chrome.metricsPrivate.recordUserAction('Settings.Searching'); |
478 chrome.metricsPrivate.recordSmallCount( | 478 chrome.metricsPrivate.recordSmallCount( |
479 'Settings.SearchLength', text.length); | 479 'Settings.SearchLength', text.length); |
480 chrome.metricsPrivate.recordSmallCount( | 480 chrome.metricsPrivate.recordSmallCount( |
481 'Settings.SearchPageMatchCount', pageMatchesForMetrics); | 481 'Settings.SearchPageMatchCount', pageMatchesForMetrics); |
482 chrome.metricsPrivate.recordSmallCount( | 482 chrome.metricsPrivate.recordSmallCount( |
483 'Settings.SearchSubpageMatchCount', subpageMatchesForMetrics); | 483 'Settings.SearchSubpageMatchCount', subpageMatchesForMetrics); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
521 * @param {Element} element An HTML container element to recursively search | 521 * @param {Element} element An HTML container element to recursively search |
522 * within. | 522 * within. |
523 * @return {boolean} true if the element was changed. | 523 * @return {boolean} true if the element was changed. |
524 * @private | 524 * @private |
525 */ | 525 */ |
526 highlightMatches_: function(regExp, element) { | 526 highlightMatches_: function(regExp, element) { |
527 var found = false; | 527 var found = false; |
528 var div, child, tmp; | 528 var div, child, tmp; |
529 | 529 |
530 // Walk the tree, searching each TEXT node. | 530 // Walk the tree, searching each TEXT node. |
531 var walker = document.createTreeWalker(element, | 531 var walker = |
532 NodeFilter.SHOW_TEXT, | 532 document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null, false); |
533 null, | |
534 false); | |
535 var node = walker.nextNode(); | 533 var node = walker.nextNode(); |
536 while (node) { | 534 while (node) { |
537 var textContent = node.nodeValue; | 535 var textContent = node.nodeValue; |
538 // Perform a search and replace on the text node value. | 536 // Perform a search and replace on the text node value. |
539 var split = textContent.split(regExp); | 537 var split = textContent.split(regExp); |
540 if (split.length > 1) { | 538 if (split.length > 1) { |
541 found = true; | 539 found = true; |
542 var nextNode = walker.nextNode(); | 540 var nextNode = walker.nextNode(); |
543 var parentNode = node.parentNode; | 541 var parentNode = node.parentNode; |
544 // Use existing node as placeholder to determine where to insert the | 542 // Use existing node as placeholder to determine where to insert the |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
587 | 585 |
588 /** | 586 /** |
589 * Creates a search result bubble attached to an element. | 587 * Creates a search result bubble attached to an element. |
590 * @param {Element} element An HTML element, usually a button. | 588 * @param {Element} element An HTML element, usually a button. |
591 * @param {string} text A string to show in the bubble. | 589 * @param {string} text A string to show in the bubble. |
592 * @private | 590 * @private |
593 */ | 591 */ |
594 createSearchBubble_: function(element, text) { | 592 createSearchBubble_: function(element, text) { |
595 // avoid appending multiple bubbles to a button. | 593 // avoid appending multiple bubbles to a button. |
596 var sibling = element.previousElementSibling; | 594 var sibling = element.previousElementSibling; |
597 if (sibling && (sibling.classList.contains('search-bubble') || | 595 if (sibling && |
598 sibling.classList.contains('search-bubble-wrapper'))) | 596 (sibling.classList.contains('search-bubble') || |
| 597 sibling.classList.contains('search-bubble-wrapper'))) |
599 return; | 598 return; |
600 | 599 |
601 var parent = element.parentElement; | 600 var parent = element.parentElement; |
602 if (parent) { | 601 if (parent) { |
603 var bubble = new SearchBubble(text); | 602 var bubble = new SearchBubble(text); |
604 bubble.attachTo(element); | 603 bubble.attachTo(element); |
605 bubble.updatePosition(); | 604 bubble.updatePosition(); |
606 } | 605 } |
607 }, | 606 }, |
608 | 607 |
(...skipping 29 matching lines...) Expand all Loading... |
638 /** | 637 /** |
639 * Builds a list of sub-pages (and overlay pages) to search. Ignore pages | 638 * Builds a list of sub-pages (and overlay pages) to search. Ignore pages |
640 * that have no associated controls, or whose controls are hidden. | 639 * that have no associated controls, or whose controls are hidden. |
641 * @return {Array} An array of pages to search. | 640 * @return {Array} An array of pages to search. |
642 * @private | 641 * @private |
643 */ | 642 */ |
644 getSearchableSubPages_: function() { | 643 getSearchableSubPages_: function() { |
645 var name, pageInfo, page, pages = []; | 644 var name, pageInfo, page, pages = []; |
646 for (name in PageManager.registeredPages) { | 645 for (name in PageManager.registeredPages) { |
647 page = PageManager.registeredPages[name]; | 646 page = PageManager.registeredPages[name]; |
648 if (page.parentPage && | 647 if (page.parentPage && page.associatedSection && |
649 page.associatedSection && | |
650 !page.associatedSection.hidden) { | 648 !page.associatedSection.hidden) { |
651 pages.push(page); | 649 pages.push(page); |
652 } | 650 } |
653 } | 651 } |
654 for (name in PageManager.registeredOverlayPages) { | 652 for (name in PageManager.registeredOverlayPages) { |
655 page = PageManager.registeredOverlayPages[name]; | 653 page = PageManager.registeredOverlayPages[name]; |
656 if (page.associatedSection && | 654 if (page.associatedSection && !page.associatedSection.hidden && |
657 !page.associatedSection.hidden && | |
658 page.pageDiv != undefined) { | 655 page.pageDiv != undefined) { |
659 pages.push(page); | 656 pages.push(page); |
660 } | 657 } |
661 } | 658 } |
662 return pages; | 659 return pages; |
663 }, | 660 }, |
664 | 661 |
665 /** | 662 /** |
666 * A function to handle key press events. | 663 * A function to handle key press events. |
667 * @param {Event} event A keydown event. | 664 * @param {Event} event A keydown event. |
(...skipping 28 matching lines...) Expand all Loading... |
696 * Standardizes a user-entered text query by removing extra whitespace. | 693 * Standardizes a user-entered text query by removing extra whitespace. |
697 * @param {string} text The user-entered text. | 694 * @param {string} text The user-entered text. |
698 * @return {string} The trimmed query. | 695 * @return {string} The trimmed query. |
699 */ | 696 */ |
700 SearchPage.canonicalizeQuery = function(text) { | 697 SearchPage.canonicalizeQuery = function(text) { |
701 // Trim beginning and ending whitespace. | 698 // Trim beginning and ending whitespace. |
702 return text.replace(/^\s+|\s+$/g, ''); | 699 return text.replace(/^\s+|\s+$/g, ''); |
703 }; | 700 }; |
704 | 701 |
705 // Export | 702 // Export |
706 return { | 703 return {SearchPage: SearchPage}; |
707 SearchPage: SearchPage | |
708 }; | |
709 | 704 |
710 }); | 705 }); |
OLD | NEW |