OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 (function() { | 5 (function() { |
| 6 <include src="../../../../ui/webui/resources/js/assert.js"> |
6 | 7 |
7 /** | 8 /** |
8 * True if this a Google page and not some other search provider. Used to | 9 * True if this a Google page and not some other search provider. Used to |
9 * determine whether to show the logo and fakebox. | 10 * determine whether to show the logo and fakebox. |
10 * @type {boolean} | 11 * @type {boolean} |
11 * @const | 12 * @const |
12 */ | 13 */ |
13 var isGooglePage = location.href.indexOf('isGoogle') != -1; | 14 var isGooglePage = location.href.indexOf('isGoogle') != -1; |
14 | 15 |
15 // ========================================================== | 16 // ========================================================== |
16 // Enums | 17 // Enums |
17 // ========================================================== | 18 // ========================================================== |
18 | 19 |
19 /** | 20 /** |
20 * Enum for classnames. | 21 * Enum for classnames. |
21 * @enum {string} | 22 * @enum {string} |
22 * @const | 23 * @const |
23 */ | 24 */ |
24 var CLASSES = { | 25 var CLASSES = { |
| 26 ACTIVE_SUGGESTIONS_CONTAINER: 'active-suggestions-container', |
25 BLACKLIST: 'mv-blacklist', // triggers tile blacklist animation | 27 BLACKLIST: 'mv-blacklist', // triggers tile blacklist animation |
26 BLACKLIST_BUTTON: 'mv-x', | 28 BLACKLIST_BUTTON: 'mv-x', |
27 CUSTOM_THEME: 'custom-theme', | 29 CUSTOM_THEME: 'custom-theme', |
28 DELAYED_HIDE_NOTIFICATION: 'mv-notice-delayed-hide', | 30 DELAYED_HIDE_NOTIFICATION: 'mv-notice-delayed-hide', |
29 DOMAIN: 'mv-domain', | 31 DOMAIN: 'mv-domain', |
30 FAKEBOX_ANIMATE: 'fakebox-animate', // triggers fakebox animation | 32 FAKEBOX_ANIMATE: 'fakebox-animate', // triggers fakebox animation |
31 FAKEBOX_FOCUS: 'fakebox-focused', // Applies focus styles to the fakebox | 33 FAKEBOX_FOCUS: 'fakebox-focused', // Applies focus styles to the fakebox |
32 FAVICON: 'mv-favicon', | 34 FAVICON: 'mv-favicon', |
33 FILLER: 'mv-filler', // filler tiles | 35 FILLER: 'mv-filler', // filler tiles |
34 GOOGLE_PAGE: 'google-page', // shows the Google logo and fakebox | 36 GOOGLE_PAGE: 'google-page', // shows the Google logo and fakebox |
35 HIDE_BLACKLIST_BUTTON: 'mv-x-hide', // hides blacklist button during animation | 37 HIDE_BLACKLIST_BUTTON: 'mv-x-hide', // hides blacklist button during animation |
36 HIDE_NOTIFICATION: 'mv-notice-hide', | 38 HIDE_NOTIFICATION: 'mv-notice-hide', |
37 HIDE_TILE: 'mv-tile-hide', // hides tiles on small browser width | 39 HIDE_TILE: 'mv-tile-hide', // hides tiles on small browser width |
| 40 HOVERED: 'hovered', |
| 41 PENDING_SUGGESTIONS_CONTAINER: 'pending-suggestions-container', |
38 PAGE: 'mv-page', // page tiles | 42 PAGE: 'mv-page', // page tiles |
| 43 SEARCH: 'search', |
39 SELECTED: 'selected', // a selected suggestion (if any) | 44 SELECTED: 'selected', // a selected suggestion (if any) |
| 45 SUGGESTION: 'suggestion', |
| 46 SUGGESTION_CONTENTS: 'suggestion-contents', |
| 47 SUGGESTIONS_BOX: 'suggestions-box', |
40 THUMBNAIL: 'mv-thumb', | 48 THUMBNAIL: 'mv-thumb', |
41 TILE: 'mv-tile', | 49 TILE: 'mv-tile', |
42 TITLE: 'mv-title' | 50 TITLE: 'mv-title' |
43 }; | 51 }; |
44 | 52 |
45 /** | 53 /** |
46 * Enum for HTML element ids. | 54 * Enum for HTML element ids. |
47 * @enum {string} | 55 * @enum {string} |
48 * @const | 56 * @const |
49 */ | 57 */ |
50 var IDS = { | 58 var IDS = { |
51 ATTRIBUTION: 'attribution', | 59 ATTRIBUTION: 'attribution', |
52 CURSOR: 'cursor', | 60 CURSOR: 'cursor', |
53 FAKEBOX: 'fakebox', | 61 FAKEBOX: 'fakebox', |
54 LOGO: 'logo', | 62 LOGO: 'logo', |
55 NOTIFICATION: 'mv-notice', | 63 NOTIFICATION: 'mv-notice', |
56 NOTIFICATION_CLOSE_BUTTON: 'mv-notice-x', | 64 NOTIFICATION_CLOSE_BUTTON: 'mv-notice-x', |
57 NOTIFICATION_MESSAGE: 'mv-msg', | 65 NOTIFICATION_MESSAGE: 'mv-msg', |
58 NTP_CONTENTS: 'ntp-contents', | 66 NTP_CONTENTS: 'ntp-contents', |
59 RESTORE_ALL_LINK: 'mv-restore', | 67 RESTORE_ALL_LINK: 'mv-restore', |
60 SUGGESTIONS_BOX: 'suggestions-box', | 68 SUGGESTION_LOADER: 'suggestion-loader', |
61 SUGGESTIONS_CONTAINER: 'suggestions-box-container', | |
62 SUGGESTION_STYLE: 'suggestion-style', | 69 SUGGESTION_STYLE: 'suggestion-style', |
| 70 SUGGESTION_TEXT_PREFIX: 'suggestion-text-', |
63 TILES: 'mv-tiles', | 71 TILES: 'mv-tiles', |
64 TOP_MARGIN: 'mv-top-margin', | 72 TOP_MARGIN: 'mv-top-margin', |
65 UNDO_LINK: 'mv-undo' | 73 UNDO_LINK: 'mv-undo' |
66 }; | 74 }; |
67 | 75 |
68 // ============================================================================= | 76 // ============================================================================= |
69 // NTP implementation | 77 // NTP implementation |
70 // ============================================================================= | 78 // ============================================================================= |
71 | 79 |
72 /** | 80 /** |
(...skipping 545 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
618 | 626 |
619 /** | 627 /** |
620 * Assume any native suggestion with a score higher than this value has been | 628 * Assume any native suggestion with a score higher than this value has been |
621 * inlined by the browser. | 629 * inlined by the browser. |
622 * @type {number} | 630 * @type {number} |
623 * @const | 631 * @const |
624 */ | 632 */ |
625 var INLINE_SUGGESTION_THRESHOLD = 1200; | 633 var INLINE_SUGGESTION_THRESHOLD = 1200; |
626 | 634 |
627 /** | 635 /** |
| 636 * The color code for a query. |
| 637 * @type {number} |
| 638 * @const |
| 639 */ |
| 640 var SUGGESTION_QUERY_COLOR = 0x000000; |
| 641 |
| 642 /** |
| 643 * The color code for a suggestion display URL. |
| 644 * @type {number} |
| 645 * @const |
| 646 */ |
| 647 var SUGGESTION_URL_COLOR = 0x009933; |
| 648 |
| 649 /** |
| 650 * The color code for a suggestion title. |
| 651 * @type {number} |
| 652 * @const |
| 653 */ |
| 654 var SUGGESTION_TITLE_COLOR = 0x666666; |
| 655 |
| 656 /** |
| 657 * A top position which is off-screen. |
| 658 * @type {string} |
| 659 * @const |
| 660 */ |
| 661 var OFF_SCREEN = '-1000px'; |
| 662 |
| 663 /** |
| 664 * The expected origin of a suggestion iframe. |
| 665 * @type {string} |
| 666 * @const |
| 667 */ |
| 668 var SUGGESTION_ORIGIN = 'chrome-search://suggestion'; |
| 669 |
| 670 /** |
628 * Suggestion provider type corresponding to a verbatim URL suggestion. | 671 * Suggestion provider type corresponding to a verbatim URL suggestion. |
629 * @type {string} | 672 * @type {string} |
630 * @const | 673 * @const |
631 */ | 674 */ |
632 var VERBATIM_URL_TYPE = 'url-what-you-typed'; | 675 var VERBATIM_URL_TYPE = 'url-what-you-typed'; |
633 | 676 |
634 /** | 677 /** |
635 * Suggestion provider type corresponding to a verbatim search suggestion. | 678 * Suggestion provider type corresponding to a verbatim search suggestion. |
636 * @type {string} | 679 * @type {string} |
637 * @const | 680 * @const |
638 */ | 681 */ |
639 var VERBATIM_SEARCH_TYPE = 'search-what-you-typed'; | 682 var VERBATIM_SEARCH_TYPE = 'search-what-you-typed'; |
640 | 683 |
641 /** | 684 /** |
642 * The omnibox input value during the last onnativesuggestions event. | 685 * "Up" arrow keycode. |
643 * @type {string} | |
644 */ | |
645 var lastInputValue = ''; | |
646 | |
647 /** | |
648 * The ordered restricted ids of the currently displayed suggestions. Since the | |
649 * suggestions contain the user's personal data (browser history) the searchBox | |
650 * API embeds the content of the suggestion in a shadow dom, and assigns a | |
651 * random restricted id to each suggestion which is accessible to the JS. | |
652 * @type {!Array.<number>} | |
653 */ | |
654 | |
655 var restrictedIds = []; | |
656 | |
657 /** | |
658 * The index of the currently selected suggestion or -1 if none are selected. | |
659 * @type {number} | 686 * @type {number} |
660 */ | 687 * @const |
661 var selectedIndex = -1; | 688 */ |
662 | 689 var KEY_UP_ARROW = 38; |
663 /** | 690 |
664 * The browser embeddedSearch.searchBox object. | 691 /** |
665 * @type {Object} | 692 * "Down" arrow keycode. |
666 */ | 693 * @type {number} |
667 var searchboxApiHandle; | 694 * @const |
668 | 695 */ |
669 /** | 696 var KEY_DOWN_ARROW = 40; |
670 * Displays a suggestion. | 697 |
671 * @param {Object} suggestion The suggestion to render. | 698 /** |
672 * @param {HTMLElement} box The html element to add the suggestion to. | 699 * Pixels of padding inside a suggestion div for displaying its icon. |
673 * @param {boolean} select True to select the selection. | 700 * @type {number} |
674 */ | 701 * @const |
675 function addSuggestionToBox(suggestion, box, select) { | 702 */ |
676 var suggestionDiv = document.createElement('div'); | 703 var SUGGESTION_ICON_PADDING = 26; |
677 suggestionDiv.classList.add('suggestion'); | 704 |
678 suggestionDiv.classList.toggle(CLASSES.SELECTED, select); | 705 /** |
679 suggestionDiv.classList.toggle('search', suggestion.is_search); | 706 * Pixels by which iframes should be moved down relative to their wrapping |
680 | 707 * suggestion div. |
681 if (suggestion.destination_url) { // iframes. | 708 */ |
682 var suggestionIframe = document.createElement('iframe'); | 709 var SUGGESTION_TOP_OFFSET = 4; |
683 suggestionIframe.className = 'contents'; | 710 |
684 suggestionIframe.src = suggestion.destination_url; | 711 /** |
685 suggestionIframe.id = suggestion.rid; | 712 * The displayed suggestions. |
686 suggestionDiv.appendChild(suggestionIframe); | 713 * @type {SuggestionsBox} |
687 } else { | 714 */ |
688 var contentsContainer = document.createElement('div'); | 715 var activeBox; |
689 var contents = suggestion.combinedNode; | 716 |
690 contents.classList.add('contents'); | 717 /** |
691 contentsContainer.appendChild(contents); | 718 * The suggestions being rendered. |
692 suggestionDiv.appendChild(contentsContainer); | 719 * @type {SuggestionsBox} |
693 suggestionDiv.onclick = function(event) { | 720 */ |
694 handleSuggestionClick(suggestion.rid, event.button); | 721 var pendingBox; |
695 }; | 722 |
696 } | 723 /** |
697 | 724 * A pool of iframes to display suggestions. |
698 restrictedIds.push(suggestion.rid); | 725 * @type {IframePool} |
699 box.appendChild(suggestionDiv); | 726 */ |
700 } | 727 var iframePool; |
701 | 728 |
702 /** | 729 /** |
703 * Renders the input suggestions. | 730 * A serial number for the next suggestions rendered. |
704 * @param {!Array} nativeSuggestions An array of native suggestions to render. | 731 * @type {number} |
705 */ | 732 */ |
706 function renderSuggestions(nativeSuggestions) { | 733 var nextRequestId = 0; |
707 for (var i = 0, length = nativeSuggestions.length; | 734 |
708 i < Math.min(MAX_SUGGESTIONS_TO_SHOW, length); ++i) { | 735 /** |
709 // Don't add the search-what-you-typed suggestion if it's the top match. | 736 * @param {Object} suggestion A suggestion. |
710 if (i > 0 || nativeSuggestions[i].type != VERBATIM_SEARCH_TYPE) { | |
711 var box = $(IDS.SUGGESTIONS_BOX); | |
712 if (!box) { | |
713 box = document.createElement('div'); | |
714 box.id = IDS.SUGGESTIONS_BOX; | |
715 $(IDS.SUGGESTIONS_CONTAINER).appendChild(box); | |
716 } | |
717 addSuggestionToBox(nativeSuggestions[i], box, i == selectedIndex); | |
718 } | |
719 } | |
720 } | |
721 | |
722 /** | |
723 * Clears the suggestions being displayed. | |
724 */ | |
725 function clearSuggestions() { | |
726 $(IDS.SUGGESTIONS_CONTAINER).innerHTML = ''; | |
727 restrictedIds = []; | |
728 selectedIndex = -1; | |
729 } | |
730 | |
731 /** | |
732 * @return {number} The height of the dropdown. | |
733 */ | |
734 function getDropdownHeight() { | |
735 return $(IDS.SUGGESTIONS_CONTAINER).offsetHeight; | |
736 } | |
737 | |
738 /** | |
739 * @param {!Object} suggestion A suggestion. | |
740 * @param {boolean} inVerbatimMode Are we in verbatim mode? | 737 * @param {boolean} inVerbatimMode Are we in verbatim mode? |
741 * @return {boolean} True if the suggestion should be selected. | 738 * @return {boolean} True if the suggestion should be selected. |
742 */ | 739 */ |
743 function shouldSelectSuggestion(suggestion, inVerbatimMode) { | 740 function shouldSelectSuggestion(suggestion, inVerbatimMode) { |
744 var isVerbatimUrl = suggestion.type == VERBATIM_URL_TYPE; | 741 var isVerbatimUrl = suggestion.type == VERBATIM_URL_TYPE; |
745 var inlinableSuggestion = suggestion.type != VERBATIM_SEARCH_TYPE && | 742 var inlineableSuggestion = suggestion.type != VERBATIM_SEARCH_TYPE && |
746 suggestion.rankingData.relevance > INLINE_SUGGESTION_THRESHOLD; | 743 suggestion.rankingData.relevance > INLINE_SUGGESTION_THRESHOLD; |
747 // Verbatim URLs should always be selected. Otherwise, select suggestions | 744 // Verbatim URLs should always be selected. Otherwise, select suggestions |
748 // with a high enough score unless we are in verbatim mode (e.g. backspacing | 745 // with a high enough score unless we are in verbatim mode (e.g. backspacing |
749 // away). | 746 // away). |
750 return isVerbatimUrl || (!inVerbatimMode && inlinableSuggestion); | 747 return isVerbatimUrl || (!inVerbatimMode && inlineableSuggestion); |
751 } | 748 } |
752 | 749 |
753 /** | 750 /** |
754 * Updates selectedIndex, bounding it between -1 and the total number of | 751 * Extract the desired navigation behavior from a click button. |
755 * of suggestions - 1 (looping as necessary), and selects the corresponding | 752 * @param {number} button The Event#button property of a click event. |
756 * suggestion. | 753 * @return {WindowOpenDisposition} The desired behavior for |
757 * @param {boolean} increment True to increment the selected suggestion, false | 754 * navigateContentWindow. |
758 * to decrement. | 755 */ |
759 */ | 756 function getDispositionFromClickButton(button) { |
760 function updateSelectedSuggestion(increment) { | 757 if (button == MIDDLE_MOUSE_BUTTON) |
761 var numSuggestions = restrictedIds.length; | 758 return WindowOpenDisposition.NEW_BACKGROUND_TAB; |
762 if (!numSuggestions) | 759 return WindowOpenDisposition.CURRENT_TAB; |
763 return; | 760 } |
764 | 761 |
765 var oldSelection = $(IDS.SUGGESTIONS_BOX).querySelector('.selected'); | 762 |
766 if (oldSelection) | 763 /** |
767 oldSelection.classList.remove(CLASSES.SELECTED); | 764 * Manages a pool of chrome-search suggestion result iframes. |
768 if (increment) { | 765 * @constructor |
769 if (selectedIndex > numSuggestions) | 766 */ |
770 selectedIndex = -1; | 767 function IframePool() { |
771 else | 768 } |
772 ++selectedIndex; | 769 |
| 770 IframePool.prototype = { |
| 771 /** |
| 772 * HTML iframe elements. |
| 773 * @type {Array.<Element>} |
| 774 * @private |
| 775 */ |
| 776 iframes_: [], |
| 777 |
| 778 /** |
| 779 * Initializes the pool with blank result template iframes, positioned off |
| 780 * screen. |
| 781 */ |
| 782 init: function() { |
| 783 for (var i = 0; i < 2 * MAX_SUGGESTIONS_TO_SHOW; ++i) { |
| 784 var iframe = document.createElement('iframe'); |
| 785 iframe.className = CLASSES.SUGGESTION_CONTENTS; |
| 786 iframe.id = IDS.SUGGESTION_TEXT_PREFIX + i; |
| 787 iframe.src = 'chrome-search://suggestion/result.html'; |
| 788 iframe.style.top = OFF_SCREEN; |
| 789 iframe.addEventListener('mouseover', function(e) { |
| 790 if (activeBox) |
| 791 activeBox.hover(e.currentTarget.id); |
| 792 }, false); |
| 793 iframe.addEventListener('mouseout', function(e) { |
| 794 if (activeBox) |
| 795 activeBox.unhover(e.currentTarget.id); |
| 796 }, false); |
| 797 document.body.appendChild(iframe); |
| 798 this.iframes_.push(iframe); |
| 799 } |
| 800 }, |
| 801 |
| 802 /** |
| 803 * Reserves a free suggestion iframe from the pool. |
| 804 * @return {Element} An iframe suitable for holding a suggestion. |
| 805 */ |
| 806 reserve: function() { |
| 807 return this.iframes_.pop(); |
| 808 }, |
| 809 |
| 810 /** |
| 811 * Releases a suggestion iframe back into the pool. |
| 812 * @param {Element} iframe The iframe to return to the pool. |
| 813 */ |
| 814 release: function(iframe) { |
| 815 this.iframes_.push(iframe); |
| 816 iframe.style.top = OFF_SCREEN; |
| 817 }, |
| 818 }; |
| 819 |
| 820 |
| 821 /** |
| 822 * An individual suggestion. |
| 823 * @param {!Object} data Autocomplete fields for this suggestion. |
| 824 * @constructor |
| 825 */ |
| 826 function Suggestion(data) { |
| 827 assert(data); |
| 828 /** |
| 829 * Autocomplete fields for this suggestion. |
| 830 * @type {!Object} |
| 831 * @private |
| 832 */ |
| 833 this.data_ = data; |
| 834 } |
| 835 |
| 836 Suggestion.prototype = { |
| 837 /** |
| 838 * Releases the iframe reserved for this suggestion. |
| 839 */ |
| 840 destroy: function() { |
| 841 if (this.iframe_) |
| 842 iframePool.release(this.iframe_); |
| 843 }, |
| 844 |
| 845 /** |
| 846 * Creates and appends the placeholder div for this suggestion to box. |
| 847 * @param {Element} box A suggestions box. |
| 848 * @param {boolean} selected True if the suggestion should be drawn as |
| 849 * selected and false otherwise. |
| 850 */ |
| 851 appendToBox: function(box, selected) { |
| 852 var div = document.createElement('div'); |
| 853 div.classList.add(CLASSES.SUGGESTION); |
| 854 div.classList.toggle(CLASSES.SELECTED, selected); |
| 855 div.classList.toggle(CLASSES.SEARCH, this.data_.is_search); |
| 856 box.appendChild(div); |
| 857 this.div_ = div; |
| 858 }, |
| 859 |
| 860 /** |
| 861 * Repositions the suggestion iframe to align with its expected dropdown |
| 862 * position. |
| 863 * @param {boolean} isRtl True if rendering right-to-left and false if not. |
| 864 * @param {number} startMargin Leading space before suggestion. |
| 865 * @param {number} totalMargin Total non-content space on suggestion line. |
| 866 */ |
| 867 reposition: function(isRtl, startMargin, totalMargin) { |
| 868 // Add in the expected parent offset and the top margin. |
| 869 this.iframe_.style.top = this.div_.offsetTop + SUGGESTION_TOP_OFFSET + 'px'; |
| 870 // Call parseInt to enforce that startMargin and totalMargin are really |
| 871 // numbers since we're interpolating CSS. |
| 872 startMargin = parseInt(startMargin, 10); |
| 873 totalMargin = parseInt(totalMargin, 10); |
| 874 if (isFinite(startMargin) && isFinite(totalMargin)) { |
| 875 this.iframe_.style[isRtl ? 'right' : 'left'] = startMargin + 'px'; |
| 876 this.iframe_.style.width = '-webkit-calc(100% - ' + |
| 877 (totalMargin + SUGGESTION_ICON_PADDING) + 'px)'; |
| 878 } |
| 879 }, |
| 880 |
| 881 /** |
| 882 * Updates the suggestion selection state. |
| 883 * @param {boolean} selected True if drawn selected or false if not. |
| 884 */ |
| 885 select: function(selected) { |
| 886 this.div_.classList.toggle(CLASSES.SELECTED, selected); |
| 887 }, |
| 888 |
| 889 /** |
| 890 * Updates the suggestion hover state. |
| 891 * @param {boolean} hovered True if drawn hovered or false if not. |
| 892 */ |
| 893 hover: function(hovered) { |
| 894 this.div_.classList.toggle(CLASSES.HOVERED, hovered); |
| 895 }, |
| 896 |
| 897 /** |
| 898 * @param {Window} iframeWindow The content window of an iframe. |
| 899 * @return {boolean} True if this suggestion's iframe has the specified |
| 900 * window and false if not. |
| 901 */ |
| 902 hasIframeWindow: function(iframeWindow) { |
| 903 return this.iframe_.contentWindow == iframeWindow; |
| 904 }, |
| 905 |
| 906 /** |
| 907 * @param {string} id An element id. |
| 908 * @return {boolean} True if this suggestion's iframe has the specified id |
| 909 * and false if not. |
| 910 */ |
| 911 hasIframeId: function(id) { |
| 912 return this.iframe_.id == id; |
| 913 }, |
| 914 |
| 915 /** |
| 916 * The iframe element for this suggestion. |
| 917 * @type {Element} |
| 918 */ |
| 919 set iframe(iframe) { |
| 920 this.iframe_ = iframe; |
| 921 }, |
| 922 |
| 923 /** |
| 924 * The restricted id associated with this suggestion. |
| 925 * @type {number} |
| 926 */ |
| 927 get restrictedId() { |
| 928 return this.data_.rid; |
| 929 }, |
| 930 }; |
| 931 |
| 932 |
| 933 /** |
| 934 * Displays a suggestions box. |
| 935 * @param {string} inputValue The user text that prompted these suggestions. |
| 936 * @param {!Array.<!Object>} suggestionData Suggestion data to display. |
| 937 * @param {number} selectedIndex The index of the suggestion selected. |
| 938 * @constructor |
| 939 */ |
| 940 function SuggestionsBox(inputValue, suggestionData, selectedIndex) { |
| 941 /** |
| 942 * The user text that prompted these suggestions. |
| 943 * @type {string} |
| 944 * @private |
| 945 */ |
| 946 this.inputValue_ = inputValue; |
| 947 |
| 948 /** |
| 949 * The index of the suggestion currently selected, whether by default or |
| 950 * because the user arrowed down to it. |
| 951 * @type {number} |
| 952 * @private |
| 953 */ |
| 954 this.selectedIndex_ = selectedIndex; |
| 955 |
| 956 /** |
| 957 * The index of the suggestion currently under the mouse pointer. |
| 958 * @type {number} |
| 959 * @private |
| 960 */ |
| 961 this.hoveredIndex_ = -1; |
| 962 |
| 963 /** |
| 964 * A stamp to distinguish this suggestions box from others. |
| 965 * @type {number} |
| 966 * @private |
| 967 */ |
| 968 this.requestId_ = nextRequestId++; |
| 969 |
| 970 /** |
| 971 * The ordered suggestions this box is displaying. |
| 972 * @type {Array.<Suggestion>} |
| 973 * @private |
| 974 */ |
| 975 this.suggestions_ = []; |
| 976 for (var i = 0; i < suggestionData.length; ++i) { |
| 977 this.suggestions_.push(new Suggestion(suggestionData[i])); |
| 978 } |
| 979 |
| 980 /** |
| 981 * The container for this suggestions box. div.pending-suggestion-container |
| 982 * if inactive and div.active-suggestion-container if active. |
| 983 * @type {Element} |
| 984 * @private |
| 985 */ |
| 986 this.container_ = $qs('.' + CLASSES.PENDING_SUGGESTIONS_CONTAINER); |
| 987 assert(this.container_); |
| 988 } |
| 989 |
| 990 SuggestionsBox.prototype = { |
| 991 /** |
| 992 * Releases suggestion iframes and ignores any load done message for the |
| 993 * current suggestions. |
| 994 */ |
| 995 destroy: function() { |
| 996 while (this.suggestions_.length > 0) { |
| 997 this.suggestions_.pop().destroy(); |
| 998 } |
| 999 this.responseId = -1; |
| 1000 }, |
| 1001 |
| 1002 /** |
| 1003 * Starts rendering new suggestions. |
| 1004 */ |
| 1005 loadSuggestions: function() { |
| 1006 // Create a placeholder DOM in the invisible container. |
| 1007 this.container_.innerHTML = ''; |
| 1008 |
| 1009 var box = document.createElement('div'); |
| 1010 box.className = CLASSES.SUGGESTIONS_BOX; |
| 1011 this.container_.appendChild(box); |
| 1012 |
| 1013 var iframesToLoad = {}; |
| 1014 for (var i = 0; i < this.suggestions_.length; ++i) { |
| 1015 var suggestion = this.suggestions_[i]; |
| 1016 suggestion.appendToBox(box, i == this.selectedIndex_); |
| 1017 var iframe = iframePool.reserve(); |
| 1018 suggestion.iframe = iframe; |
| 1019 iframesToLoad[iframe.id] = suggestion.restrictedId; |
| 1020 } |
| 1021 |
| 1022 // Ask the loader iframe to populate the iframes just reserved. |
| 1023 var loadRequest = { |
| 1024 load: iframesToLoad, |
| 1025 requestId: this.requestId_, |
| 1026 style: { |
| 1027 queryColor: SUGGESTION_QUERY_COLOR, |
| 1028 urlColor: SUGGESTION_URL_COLOR, |
| 1029 titleColor: SUGGESTION_TITLE_COLOR |
| 1030 } |
| 1031 }; |
| 1032 $(IDS.SUGGESTION_LOADER).contentWindow.postMessage(loadRequest, |
| 1033 SUGGESTION_ORIGIN); |
| 1034 }, |
| 1035 |
| 1036 /** |
| 1037 * @param {number} responseId The id of a request that just finished |
| 1038 * rendering. |
| 1039 * @return {boolean} Whether the request is for the suggestions in this box. |
| 1040 */ |
| 1041 isResponseCurrent: function(responseId) { |
| 1042 return responseId == this.requestId_; |
| 1043 }, |
| 1044 |
| 1045 /** |
| 1046 * Moves suggestion iframes into position. |
| 1047 */ |
| 1048 repositionSuggestions: function() { |
| 1049 // Note: This may be called before margins are ready. In that case, |
| 1050 // suggestion iframes will initially be too large and then size down |
| 1051 // onresize. |
| 1052 var startMargin = searchboxApiHandle.startMargin; |
| 1053 var totalMargin = window.innerWidth - searchboxApiHandle.width; |
| 1054 var isRtl = searchboxApiHandle.isRtl; |
| 1055 for (var i = 0; i < this.suggestions_.length; ++i) { |
| 1056 this.suggestions_[i].reposition(isRtl, startMargin, totalMargin); |
| 1057 } |
| 1058 }, |
| 1059 |
| 1060 /** |
| 1061 * Selects the suggestion before the current selection. |
| 1062 */ |
| 1063 selectPrevious: function() { |
| 1064 this.changeSelection_(this.selectedIndex_ - 1); |
| 1065 }, |
| 1066 |
| 1067 /** |
| 1068 * Selects the suggestion after the current selection. |
| 1069 */ |
| 1070 selectNext: function() { |
| 1071 this.changeSelection_(this.selectedIndex_ + 1); |
| 1072 }, |
| 1073 |
| 1074 /** |
| 1075 * Changes the current selected suggestion index. |
| 1076 * @param {number} index The new selection to suggest. |
| 1077 * @private |
| 1078 */ |
| 1079 changeSelection_: function(index) { |
| 1080 var numSuggestions = this.suggestions_.length; |
| 1081 this.selectedIndex_ = Math.min(numSuggestions - 1, Math.max(-1, index)); |
| 1082 |
| 1083 this.redrawSelection_(); |
| 1084 this.redrawHover_(); |
| 1085 }, |
| 1086 |
| 1087 /** |
| 1088 * Redraws the selected suggestion. |
| 1089 * @private |
| 1090 */ |
| 1091 redrawSelection_: function() { |
| 1092 var oldSelection = this.container_.querySelector('.' + CLASSES.SELECTED); |
| 1093 if (oldSelection) |
| 1094 oldSelection.classList.remove(CLASSES.SELECTED); |
| 1095 if (this.selectedIndex_ == -1) { |
| 1096 searchboxApiHandle.setValue(this.inputValue_); |
| 1097 } else { |
| 1098 this.suggestions_[this.selectedIndex_].select(true); |
| 1099 searchboxApiHandle.setRestrictedValue( |
| 1100 this.suggestions_[this.selectedIndex_].restrictedId); |
| 1101 } |
| 1102 }, |
| 1103 |
| 1104 /** |
| 1105 * @param {!Window} iframeWindow The window of the iframe that was clicked. |
| 1106 * @return {?number} The restricted ID of the iframe that was clicked, or |
| 1107 * null if there was none. |
| 1108 */ |
| 1109 getClickTarget: function(iframeWindow) { |
| 1110 for (var i = 0; i < this.suggestions_.length; ++i) { |
| 1111 if (this.suggestions_[i].hasIframeWindow(iframeWindow)) |
| 1112 return this.suggestions_[i].restrictedId; |
| 1113 } |
| 1114 return null; |
| 1115 }, |
| 1116 |
| 1117 /** |
| 1118 * Called when the user hovers on the specified iframe to update hoveredIndex_ |
| 1119 * and draw a hover background. |
| 1120 * @param {string} iframeId The id of the iframe hovered. |
| 1121 */ |
| 1122 hover: function(iframeId) { |
| 1123 this.hoveredIndex_ = -1; |
| 1124 for (var i = 0; i < this.suggestions_.length; ++i) { |
| 1125 if (this.suggestions_[i].hasIframeId(iframeId)) { |
| 1126 this.hoveredIndex_ = i; |
| 1127 break; |
| 1128 } |
| 1129 } |
| 1130 this.redrawHover_(); |
| 1131 }, |
| 1132 |
| 1133 /** |
| 1134 * Called when the user unhovers the specified iframe to clear the current |
| 1135 * hover. |
| 1136 * @param {string} iframeId The id of the iframe hovered. |
| 1137 */ |
| 1138 unhover: function(iframeId) { |
| 1139 if (this.suggestions_[this.hoveredIndex_] && |
| 1140 this.suggestions_[this.hoveredIndex_].hasIframeId(iframeId)) { |
| 1141 this.clearHover(); |
| 1142 } |
| 1143 }, |
| 1144 |
| 1145 /** |
| 1146 * Clears the current hover. |
| 1147 */ |
| 1148 clearHover: function() { |
| 1149 this.hoveredIndex_ = -1; |
| 1150 this.redrawHover_(); |
| 1151 }, |
| 1152 |
| 1153 /** |
| 1154 * Redraws the mouse hover background. |
| 1155 * @private |
| 1156 */ |
| 1157 redrawHover_: function() { |
| 1158 var oldHover = this.container_.querySelector('.' + CLASSES.HOVERED); |
| 1159 if (oldHover) |
| 1160 oldHover.classList.remove(CLASSES.HOVERED); |
| 1161 if (this.hoveredIndex_ != -1 && this.hoveredIndex_ != this.selectedIndex_) |
| 1162 this.suggestions_[this.hoveredIndex_].hover(true); |
| 1163 }, |
| 1164 |
| 1165 /** |
| 1166 * Marks the suggestions container as active. |
| 1167 */ |
| 1168 activate: function() { |
| 1169 this.container_.className = CLASSES.ACTIVE_SUGGESTIONS_CONTAINER; |
| 1170 }, |
| 1171 |
| 1172 /** |
| 1173 * Marks the suggestions container as inactive. |
| 1174 */ |
| 1175 deactivate: function() { |
| 1176 this.container_.className = CLASSES.PENDING_SUGGESTIONS_CONTAINER; |
| 1177 this.container_.innerHTML = ''; |
| 1178 }, |
| 1179 |
| 1180 /** |
| 1181 * The height of the suggestions container. |
| 1182 * @type {number} |
| 1183 */ |
| 1184 get height() { |
| 1185 return this.container_.offsetHeight; |
| 1186 }, |
| 1187 }; |
| 1188 |
| 1189 |
| 1190 /** |
| 1191 * Clears the currently active suggestions and shows pending suggestions. |
| 1192 */ |
| 1193 function makePendingSuggestionsActive() { |
| 1194 if (activeBox) { |
| 1195 activeBox.deactivate(); |
| 1196 activeBox.destroy(); |
773 } else { | 1197 } else { |
774 if (selectedIndex < 0) | 1198 // Initially there will be no active suggestions, but we still want to use |
775 selectedIndex = numSuggestions - 1; | 1199 // div.active-container to load the next suggestions. |
776 else | 1200 $qs('.' + CLASSES.ACTIVE_SUGGESTIONS_CONTAINER).className = |
777 --selectedIndex; | 1201 CLASSES.PENDING_SUGGESTIONS_CONTAINER; |
778 } | 1202 } |
779 | 1203 pendingBox.activate(); |
780 if (selectedIndex == -1) { | 1204 activeBox = pendingBox; |
781 searchboxApiHandle.setValue(lastInputValue); | 1205 pendingBox = null; |
782 } else { | 1206 activeBox.repositionSuggestions(); |
783 var newSelection = $(IDS.SUGGESTIONS_BOX).querySelector( | 1207 searchboxApiHandle.showOverlay(activeBox.height); |
784 '.suggestion:nth-of-type(' + (selectedIndex + 1) + ')'); | 1208 } |
785 newSelection.classList.add(CLASSES.SELECTED); | 1209 |
786 searchboxApiHandle.setRestrictedValue(restrictedIds[selectedIndex]); | 1210 /** |
| 1211 * Hides the active suggestions box. |
| 1212 */ |
| 1213 function hideActiveSuggestions() { |
| 1214 searchboxApiHandle.showOverlay(0); |
| 1215 if (activeBox) { |
| 1216 $qs('.' + CLASSES.ACTIVE_SUGGESTIONS_CONTAINER).innerHTML = ''; |
| 1217 activeBox.destroy(); |
787 } | 1218 } |
788 } | 1219 activeBox = null; |
789 | 1220 } |
790 /** | 1221 |
| 1222 /** |
791 * Updates suggestions in response to a onchange or onnativesuggestions call. | 1223 * Updates suggestions in response to a onchange or onnativesuggestions call. |
792 */ | 1224 */ |
793 function updateSuggestions() { | 1225 function updateSuggestions() { |
794 appendSuggestionStyles(); | 1226 appendSuggestionStyles(); |
795 lastInputValue = searchboxApiHandle.value; | 1227 if (pendingBox) |
796 | 1228 pendingBox.destroy(); |
797 // Hide the NTP if input has made it into the omnibox. | 1229 pendingBox = null; |
798 var showNTP = lastInputValue == ''; | 1230 var suggestions = searchboxApiHandle.nativeSuggestions; |
799 updateNtpVisibility(showNTP); | 1231 if (suggestions.length) { |
800 | 1232 suggestions.sort(function(a, b) { |
801 clearSuggestions(); | 1233 return b.rankingData.relevance - a.rankingData.relevance; |
802 if (showNTP) { | 1234 }); |
803 searchboxApiHandle.showBars(); | 1235 var selectedIndex = -1; |
| 1236 if (shouldSelectSuggestion(suggestions[0], searchboxApiHandle.verbatim)) |
| 1237 selectedIndex = 0; |
| 1238 // Don't display a search-what-you-typed suggestion if it's the top match. |
| 1239 if (suggestions[0].type == VERBATIM_SEARCH_TYPE) |
| 1240 suggestions.shift(); |
| 1241 } |
| 1242 var inputValue = searchboxApiHandle.value; |
| 1243 if (!!inputValue && suggestions.length) { |
| 1244 pendingBox = new SuggestionsBox(inputValue, |
| 1245 suggestions.slice(0, MAX_SUGGESTIONS_TO_SHOW), selectedIndex); |
| 1246 pendingBox.loadSuggestions(); |
804 } else { | 1247 } else { |
805 var nativeSuggestions = searchboxApiHandle.nativeSuggestions; | 1248 hideActiveSuggestions(); |
806 if (nativeSuggestions.length) { | |
807 nativeSuggestions.sort(function(a, b) { | |
808 return b.rankingData.relevance - a.rankingData.relevance; | |
809 }); | |
810 if (shouldSelectSuggestion( | |
811 nativeSuggestions[0], searchboxApiHandle.verbatim)) { | |
812 selectedIndex = 0; | |
813 } | |
814 | |
815 renderSuggestions(nativeSuggestions); | |
816 searchboxApiHandle.hideBars(); | |
817 } else { | |
818 searchboxApiHandle.showBars(); | |
819 } | |
820 } | 1249 } |
821 | |
822 var height = getDropdownHeight(); | |
823 searchboxApiHandle.showOverlay(height); | |
824 } | 1250 } |
825 | 1251 |
826 /** | 1252 /** |
827 * Appends a style node for suggestion properties that depend on apiHandle. | 1253 * Appends a style node for suggestion properties that depend on apiHandle. |
828 */ | 1254 */ |
829 function appendSuggestionStyles() { | 1255 function appendSuggestionStyles() { |
830 if ($(IDS.SUGGESTION_STYLE)) | 1256 if ($(IDS.SUGGESTION_STYLE)) |
831 return; | 1257 return; |
832 | 1258 |
833 var isRtl = searchboxApiHandle.rtl; | 1259 var isRtl = searchboxApiHandle.rtl; |
834 var startMargin = searchboxApiHandle.startMargin; | 1260 var startMargin = searchboxApiHandle.startMargin; |
835 var style = document.createElement('style'); | 1261 var style = document.createElement('style'); |
836 style.type = 'text/css'; | 1262 style.type = 'text/css'; |
837 style.id = IDS.SUGGESTION_STYLE; | 1263 style.id = IDS.SUGGESTION_STYLE; |
838 style.textContent = | 1264 style.textContent = |
839 '.suggestion, ' + | 1265 '.suggestion, ' + |
840 '.suggestion.search {' + | 1266 '.suggestion.search {' + |
841 ' background-position: ' + | 1267 ' background-position: ' + |
842 (isRtl ? '-webkit-calc(100% - 5px)' : '5px') + ' 4px;' + | 1268 (isRtl ? '-webkit-calc(100% - 5px)' : '5px') + ' 4px;' + |
843 ' -webkit-margin-start: ' + startMargin + 'px;' + | 1269 ' -webkit-margin-start: ' + startMargin + 'px;' + |
844 ' -webkit-margin-end: ' + | 1270 ' -webkit-margin-end: ' + |
845 (window.innerWidth - searchboxApiHandle.width - startMargin) + 'px;' + | 1271 (window.innerWidth - searchboxApiHandle.width - startMargin) + 'px;' + |
846 ' font: ' + searchboxApiHandle.fontSize + 'px "' + | 1272 ' font: ' + searchboxApiHandle.fontSize + 'px "' + |
847 searchboxApiHandle.font + '";' + | 1273 searchboxApiHandle.font + '";' + |
848 '}'; | 1274 '}'; |
849 document.querySelector('head').appendChild(style); | 1275 document.querySelector('head').appendChild(style); |
850 } | 1276 } |
851 | 1277 |
852 /** | 1278 /** |
853 * Extract the desired navigation behavior from a click button. | 1279 * Makes keys navigate through suggestions. |
854 * @param {number} button The Event#button property of a click event. | 1280 * @param {Object} e The key being pressed. |
855 * @return {!WindowOpenDisposition} The desired behavior for | |
856 * navigateContentWindow. | |
857 */ | |
858 function getDispositionFromClickButton(button) { | |
859 if (button == MIDDLE_MOUSE_BUTTON) | |
860 return WindowOpenDisposition.NEW_BACKGROUND_TAB; | |
861 return WindowOpenDisposition.CURRENT_TAB; | |
862 } | |
863 | |
864 /** | |
865 * Handles suggestion clicks. | |
866 * @param {number} restrictedId The restricted id of the suggestion being | |
867 * clicked. | |
868 * @param {number} button The Event#button property of a click event. | |
869 * | |
870 */ | |
871 function handleSuggestionClick(restrictedId, button) { | |
872 clearSuggestions(); | |
873 searchboxApiHandle.navigateContentWindow( | |
874 restrictedId, getDispositionFromClickButton(button)); | |
875 } | |
876 | |
877 /** | |
878 * chrome.searchBox.onkeypress implementation. | |
879 * @param {!Event} e The key being pressed. | |
880 */ | 1281 */ |
881 function handleKeyPress(e) { | 1282 function handleKeyPress(e) { |
| 1283 if (!activeBox) |
| 1284 return; |
| 1285 |
882 switch (e.keyCode) { | 1286 switch (e.keyCode) { |
883 case 38: // Up arrow. | 1287 case KEY_UP_ARROW: |
884 updateSelectedSuggestion(false); | 1288 activeBox.selectPrevious(); |
885 break; | 1289 break; |
886 case 40: // Down arrow. | 1290 case KEY_DOWN_ARROW: |
887 updateSelectedSuggestion(true); | 1291 activeBox.selectNext(); |
888 break; | 1292 break; |
889 } | 1293 } |
890 } | 1294 } |
891 | 1295 |
892 /** | 1296 /** |
893 * Handles the postMessage calls from the result iframes. | 1297 * Handles postMessage calls from suggestion iframes. |
894 * @param {Object} message The message containing details of clicks the iframes. | 1298 * @param {Object} message A notification that all iframes are done loading or |
| 1299 * that an iframe was clicked. |
895 */ | 1300 */ |
896 function handleMessage(message) { | 1301 function handleMessage(message) { |
897 if (message.origin != 'null' || !message.data || | 1302 if (message.origin != SUGGESTION_ORIGIN) |
898 message.data.eventType != 'click') { | |
899 return; | 1303 return; |
900 } | |
901 | 1304 |
902 var iframes = document.getElementsByClassName('contents'); | 1305 if ('loaded' in message.data) { |
903 for (var i = 0; i < iframes.length; ++i) { | 1306 if (pendingBox && pendingBox.isResponseCurrent(message.data.loaded)) |
904 if (iframes[i].contentWindow == message.source) { | 1307 makePendingSuggestionsActive(); |
905 handleSuggestionClick(parseInt(iframes[i].id, 10), | 1308 } else if ('click' in message.data) { |
906 message.data.button); | 1309 if (activeBox) { |
907 break; | 1310 var restrictedId = activeBox.getClickTarget(message.source); |
| 1311 if (restrictedId != null) { |
| 1312 hideActiveSuggestions(); |
| 1313 searchboxApiHandle.navigateContentWindow(restrictedId, |
| 1314 getDispositionFromClickButton(message.data.click)); |
| 1315 } |
908 } | 1316 } |
909 } | 1317 } |
910 } | 1318 } |
911 | 1319 |
912 // ============================================================================= | 1320 // ============================================================================= |
913 // Utils | 1321 // Utils |
914 // ============================================================================= | 1322 // ============================================================================= |
915 | 1323 |
916 /** | 1324 /** |
917 * Shortcut for document.getElementById. | 1325 * Shortcut for document.getElementById. |
918 * @param {string} id of the element. | 1326 * @param {string} id of the element. |
919 * @return {HTMLElement} with the id. | 1327 * @return {HTMLElement} with the id. |
920 */ | 1328 */ |
921 function $(id) { | 1329 function $(id) { |
922 return document.getElementById(id); | 1330 return document.getElementById(id); |
923 } | 1331 } |
924 | 1332 |
925 /** | 1333 /** |
| 1334 * Shortcut for document.querySelector. |
| 1335 * @param {string} selector A selector to query the desired element. |
| 1336 * @return {HTMLElement} The first element to match |selector| or null. |
| 1337 */ |
| 1338 function $qs(selector) { |
| 1339 return document.querySelector(selector); |
| 1340 } |
| 1341 |
| 1342 /** |
926 * Utility function which creates an element with an optional classname and | 1343 * Utility function which creates an element with an optional classname and |
927 * appends it to the specified parent. | 1344 * appends it to the specified parent. |
928 * @param {Element} parent The parent to append the new element. | 1345 * @param {Element} parent The parent to append the new element. |
929 * @param {string} name The name of the new element. | 1346 * @param {string} name The name of the new element. |
930 * @param {string=} opt_class The optional classname of the new element. | 1347 * @param {string=} opt_class The optional classname of the new element. |
931 * @return {Element} The new element. | 1348 * @return {Element} The new element. |
932 */ | 1349 */ |
933 function createAndAppendElement(parent, name, opt_class) { | 1350 function createAndAppendElement(parent, name, opt_class) { |
934 var child = document.createElement(name); | 1351 var child = document.createElement(name); |
935 if (opt_class) | 1352 if (opt_class) |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
968 // ============================================================================= | 1385 // ============================================================================= |
969 // Initialization | 1386 // Initialization |
970 // ============================================================================= | 1387 // ============================================================================= |
971 | 1388 |
972 /** | 1389 /** |
973 * Prepares the New Tab Page by adding listeners, rendering the current | 1390 * Prepares the New Tab Page by adding listeners, rendering the current |
974 * theme, the most visited pages section, and Google-specific elements for a | 1391 * theme, the most visited pages section, and Google-specific elements for a |
975 * Google-provided page. | 1392 * Google-provided page. |
976 */ | 1393 */ |
977 function init() { | 1394 function init() { |
| 1395 iframePool = new IframePool(); |
| 1396 iframePool.init(); |
978 topMarginElement = $(IDS.TOP_MARGIN); | 1397 topMarginElement = $(IDS.TOP_MARGIN); |
979 tilesContainer = $(IDS.TILES); | 1398 tilesContainer = $(IDS.TILES); |
980 notification = $(IDS.NOTIFICATION); | 1399 notification = $(IDS.NOTIFICATION); |
981 attribution = $(IDS.ATTRIBUTION); | 1400 attribution = $(IDS.ATTRIBUTION); |
982 ntpContents = $(IDS.NTP_CONTENTS); | 1401 ntpContents = $(IDS.NTP_CONTENTS); |
983 | 1402 |
984 if (isGooglePage) { | 1403 if (isGooglePage) { |
985 document.body.classList.add(CLASSES.GOOGLE_PAGE); | 1404 document.body.classList.add(CLASSES.GOOGLE_PAGE); |
986 var logo = document.createElement('div'); | 1405 var logo = document.createElement('div'); |
987 logo.id = IDS.LOGO; | 1406 logo.id = IDS.LOGO; |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1024 onMostVisitedChange(); | 1443 onMostVisitedChange(); |
1025 | 1444 |
1026 searchboxApiHandle = topLevelHandle.searchBox; | 1445 searchboxApiHandle = topLevelHandle.searchBox; |
1027 searchboxApiHandle.onnativesuggestions = updateSuggestions; | 1446 searchboxApiHandle.onnativesuggestions = updateSuggestions; |
1028 searchboxApiHandle.onchange = updateSuggestions; | 1447 searchboxApiHandle.onchange = updateSuggestions; |
1029 searchboxApiHandle.onkeypress = handleKeyPress; | 1448 searchboxApiHandle.onkeypress = handleKeyPress; |
1030 searchboxApiHandle.onsubmit = function() { | 1449 searchboxApiHandle.onsubmit = function() { |
1031 var value = searchboxApiHandle.value; | 1450 var value = searchboxApiHandle.value; |
1032 if (!value) { | 1451 if (!value) { |
1033 // Interpret onsubmit with an empty query as an ESC key press. | 1452 // Interpret onsubmit with an empty query as an ESC key press. |
1034 clearSuggestions(); | 1453 hideActiveSuggestions(); |
1035 updateNtpVisibility(true); | 1454 updateNtpVisibility(true); |
1036 } | 1455 } |
1037 }; | 1456 }; |
1038 | 1457 $qs('.' + CLASSES.ACTIVE_SUGGESTIONS_CONTAINER).dir = |
1039 $(IDS.SUGGESTIONS_CONTAINER).dir = searchboxApiHandle.rtl ? 'rtl' : 'ltr'; | 1458 searchboxApiHandle.rtl ? 'rtl' : 'ltr'; |
| 1459 $qs('.' + CLASSES.PENDING_SUGGESTIONS_CONTAINER).dir = |
| 1460 searchboxApiHandle.rtl ? 'rtl' : 'ltr'; |
1040 | 1461 |
1041 if (!document.webkitHidden) | 1462 if (!document.webkitHidden) |
1042 window.addEventListener('resize', addDelayedTransitions); | 1463 window.addEventListener('resize', addDelayedTransitions); |
1043 else | 1464 else |
1044 document.addEventListener('webkitvisibilitychange', addDelayedTransitions); | 1465 document.addEventListener('webkitvisibilitychange', addDelayedTransitions); |
1045 | 1466 |
1046 if (fakebox) { | 1467 if (fakebox) { |
1047 // Listener for updating the fakebox focus. | 1468 // Listener for updating the fakebox focus. |
1048 document.body.onclick = function(event) { | 1469 document.body.onclick = function(event) { |
1049 if (isFakeboxClick(event)) { | 1470 if (isFakeboxClick(event)) { |
(...skipping 21 matching lines...) Expand all Loading... |
1071 '-webkit-transform 100ms linear, width 200ms ease'; | 1492 '-webkit-transform 100ms linear, width 200ms ease'; |
1072 } | 1493 } |
1073 | 1494 |
1074 tilesContainer.style.webkitTransition = 'width 200ms'; | 1495 tilesContainer.style.webkitTransition = 'width 200ms'; |
1075 window.removeEventListener('resize', addDelayedTransitions); | 1496 window.removeEventListener('resize', addDelayedTransitions); |
1076 document.removeEventListener('webkitvisibilitychange', addDelayedTransitions); | 1497 document.removeEventListener('webkitvisibilitychange', addDelayedTransitions); |
1077 } | 1498 } |
1078 | 1499 |
1079 document.addEventListener('DOMContentLoaded', init); | 1500 document.addEventListener('DOMContentLoaded', init); |
1080 window.addEventListener('message', handleMessage, false); | 1501 window.addEventListener('message', handleMessage, false); |
| 1502 window.addEventListener('blur', function() { |
| 1503 if (activeBox) |
| 1504 activeBox.clearHover(); |
| 1505 }, false); |
1081 })(); | 1506 })(); |
OLD | NEW |