| 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 |