OLD | NEW |
1 | 1 |
2 // Helpers | 2 // Helpers |
3 | 3 |
4 function $(id) { | 4 function $(id) { |
5 return document.getElementById(id); | 5 return document.getElementById(id); |
6 } | 6 } |
7 | 7 |
8 // TODO(arv): Remove these when classList is available in HTML5. | 8 // TODO(arv): Remove these when classList is available in HTML5. |
9 // https://bugs.webkit.org/show_bug.cgi?id=20709 | 9 // https://bugs.webkit.org/show_bug.cgi?id=20709 |
10 function hasClass(el, name) { | 10 function hasClass(el, name) { |
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
172 var newClassName = getThumbnailClassName(d); | 172 var newClassName = getThumbnailClassName(d); |
173 if (oldClassName != newClassName) { | 173 if (oldClassName != newClassName) { |
174 t.className = newClassName; | 174 t.className = newClassName; |
175 } | 175 } |
176 | 176 |
177 // No need to continue if this is a filler. | 177 // No need to continue if this is a filler. |
178 if (newClassName == 'thumbnail-container filler') { | 178 if (newClassName == 'thumbnail-container filler') { |
179 continue; | 179 continue; |
180 } | 180 } |
181 | 181 |
182 t.title = d.title; | |
183 t.href = d.url; | 182 t.href = d.url; |
184 t.querySelector('.pin').title = localStrings.getString(d.pinned ? | 183 t.querySelector('.pin').title = localStrings.getString(d.pinned ? |
185 'unpinthumbnailtooltip' : 'pinthumbnailtooltip'); | 184 'unpinthumbnailtooltip' : 'pinthumbnailtooltip'); |
186 t.querySelector('.remove').title = | 185 t.querySelector('.remove').title = |
187 localStrings.getString('removethumbnailtooltip'); | 186 localStrings.getString('removethumbnailtooltip'); |
188 | 187 |
189 // There was some concern that a malformed malicious URL could cause an XSS | 188 // There was some concern that a malformed malicious URL could cause an XSS |
190 // attack but setting style.backgroundImage = 'url(javascript:...)' does | 189 // attack but setting style.backgroundImage = 'url(javascript:...)' does |
191 // not execute the JavaScript in WebKit. | 190 // not execute the JavaScript in WebKit. |
192 t.querySelector('.thumbnail-wrapper').style.backgroundImage = | 191 t.querySelector('.thumbnail-wrapper').style.backgroundImage = |
193 'url(chrome://thumb/' + d.url + ')'; | 192 'url("chrome://thumb/' + d.url + '")'; |
194 var titleDiv = t.querySelector('.title > div'); | 193 var titleDiv = t.querySelector('.title > div'); |
195 titleDiv.textContent = d.title; | 194 titleDiv.title = titleDiv.textContent = d.title; |
196 titleDiv.style.backgroundImage = 'url(chrome://favicon/' + d.url + ')'; | 195 titleDiv.style.backgroundImage = 'url("chrome://favicon/' + d.url + '")'; |
197 titleDiv.dir = d.direction; | 196 titleDiv.dir = d.direction; |
198 } | 197 } |
199 } | 198 } |
200 | 199 |
201 /** | 200 /** |
202 * Calls chrome.send with a callback and restores the original afterwards. | 201 * Calls chrome.send with a callback and restores the original afterwards. |
203 */ | 202 */ |
204 function chromeSend(name, params, callbackName, callback) { | 203 function chromeSend(name, params, callbackName, callback) { |
205 var old = global[callbackName]; | 204 var old = global[callbackName]; |
206 global[callbackName] = function() { | 205 global[callbackName] = function() { |
(...skipping 520 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
727 * This handles the option menu. | 726 * This handles the option menu. |
728 * @param {Element} button The button element. | 727 * @param {Element} button The button element. |
729 * @param {Element} menu The menu element. | 728 * @param {Element} menu The menu element. |
730 * @constructor | 729 * @constructor |
731 */ | 730 */ |
732 function OptionMenu(button, menu) { | 731 function OptionMenu(button, menu) { |
733 this.button = button; | 732 this.button = button; |
734 this.menu = menu; | 733 this.menu = menu; |
735 this.button.onmousedown = bind(this.handleMouseDown, this); | 734 this.button.onmousedown = bind(this.handleMouseDown, this); |
736 this.button.onkeydown = bind(this.handleKeyDown, this); | 735 this.button.onkeydown = bind(this.handleKeyDown, this); |
737 this.boundHideMenu_ = bind(this.hideMenu, this); | 736 this.boundHideMenu_ = bind(this.hide, this); |
738 this.boundMaybeHide_ = bind(this.maybeHide_, this); | 737 this.boundMaybeHide_ = bind(this.maybeHide_, this); |
739 this.menu.onmouseover = bind(this.handleMouseOver, this); | 738 this.menu.onmouseover = bind(this.handleMouseOver, this); |
740 this.menu.onmouseout = bind(this.handleMouseOut, this); | 739 this.menu.onmouseout = bind(this.handleMouseOut, this); |
741 this.menu.onmouseup = bind(this.handleMouseUp, this); | 740 this.menu.onmouseup = bind(this.handleMouseUp, this); |
742 } | 741 } |
743 | 742 |
744 OptionMenu.prototype = { | 743 OptionMenu.prototype = { |
745 showMenu: function() { | 744 show: function() { |
| 745 windowMenu.hide(); |
| 746 |
746 this.menu.style.display = 'block'; | 747 this.menu.style.display = 'block'; |
747 this.button.focus(); | 748 this.button.focus(); |
748 | 749 |
749 // Listen to document and window events so that we hide the menu when the | 750 // Listen to document and window events so that we hide the menu when the |
750 // user clicks outside the menu or tabs away or the whole window is blurred. | 751 // user clicks outside the menu or tabs away or the whole window is blurred. |
751 document.addEventListener('focus', this.boundMaybeHide_, true); | 752 document.addEventListener('focus', this.boundMaybeHide_, true); |
752 document.addEventListener('mousedown', this.boundMaybeHide_, true); | 753 document.addEventListener('mousedown', this.boundMaybeHide_, true); |
753 window.addEventListener('blur', this.boundHideMenu_); | |
754 }, | 754 }, |
755 | 755 |
756 hideMenu: function() { | 756 hide: function() { |
757 this.menu.style.display = 'none'; | 757 this.menu.style.display = 'none'; |
758 this.setSelectedIndex(-1); | 758 this.setSelectedIndex(-1); |
759 | 759 |
760 document.removeEventListener('focus', this.boundMaybeHide_, true); | 760 document.removeEventListener('focus', this.boundMaybeHide_, true); |
761 document.removeEventListener('mousedown', this.boundMaybeHide_, true); | 761 document.removeEventListener('mousedown', this.boundMaybeHide_, true); |
762 window.removeEventListener('blur', this.boundHide_); | |
763 }, | 762 }, |
764 | 763 |
765 isMenuShown: function() { | 764 isShown: function() { |
766 return this.menu.style.display == 'block'; | 765 return this.menu.style.display == 'block'; |
767 }, | 766 }, |
768 | 767 |
769 /** | 768 /** |
770 * Callback for document mousedown and focus. It checks if the user tried to | 769 * Callback for document mousedown and focus. It checks if the user tried to |
771 * navigate to a different element on the page and if so hides the menu. | 770 * navigate to a different element on the page and if so hides the menu. |
772 * @param {Event} e The mouse or focus event. | 771 * @param {Event} e The mouse or focus event. |
773 * @private | 772 * @private |
774 */ | 773 */ |
775 maybeHide_: function(e) { | 774 maybeHide_: function(e) { |
776 if (!this.menu.contains(e.target) && !this.button.contains(e.target)) { | 775 if (!this.menu.contains(e.target) && !this.button.contains(e.target)) { |
777 this.hideMenu(); | 776 this.hide(); |
778 } | 777 } |
779 }, | 778 }, |
780 | 779 |
781 handleMouseDown: function(e) { | 780 handleMouseDown: function(e) { |
782 if (this.isMenuShown()) { | 781 if (this.isShown()) { |
783 this.hideMenu(); | 782 this.hide(); |
784 } else { | 783 } else { |
785 this.showMenu(); | 784 this.show(); |
786 } | 785 } |
787 }, | 786 }, |
788 | 787 |
789 handleMouseOver: function(e) { | 788 handleMouseOver: function(e) { |
790 var el = e.target; | 789 var el = e.target; |
791 var index = Array.prototype.indexOf.call(this.menu.children, el); | 790 var index = Array.prototype.indexOf.call(this.menu.children, el); |
792 this.setSelectedIndex(index); | 791 this.setSelectedIndex(index); |
793 }, | 792 }, |
794 | 793 |
795 handleMouseOut: function(e) { | 794 handleMouseOut: function(e) { |
(...skipping 26 matching lines...) Expand all Loading... |
822 break; | 821 break; |
823 } | 822 } |
824 } | 823 } |
825 if (item) { | 824 if (item) { |
826 self.setSelectedIndex(i); | 825 self.setSelectedIndex(i); |
827 } | 826 } |
828 } | 827 } |
829 | 828 |
830 switch (e.keyIdentifier) { | 829 switch (e.keyIdentifier) { |
831 case 'Down': | 830 case 'Down': |
832 if (!this.isMenuShown()) { | 831 if (!this.isShown()) { |
833 this.showMenu(); | 832 this.show(); |
834 } | 833 } |
835 selectNextVisible(1); | 834 selectNextVisible(1); |
| 835 e.preventDefault(); |
836 break; | 836 break; |
837 case 'Up': | 837 case 'Up': |
838 if (!this.isMenuShown()) { | 838 if (!this.isShown()) { |
839 this.showMenu(); | 839 this.show(); |
840 } | 840 } |
841 selectNextVisible(-1); | 841 selectNextVisible(-1); |
| 842 e.preventDefault(); |
842 break; | 843 break; |
843 case 'Esc': | 844 case 'Esc': |
844 case 'U+001B': // Maybe this is remote desktop playing a prank? | 845 case 'U+001B': // Maybe this is remote desktop playing a prank? |
845 this.hideMenu(); | 846 this.hide(); |
846 break; | 847 break; |
847 case 'Enter': | 848 case 'Enter': |
848 if (this.isMenuShown()) { | 849 case 'U+0020': // Space |
| 850 if (this.isShown()) { |
849 if (item) { | 851 if (item) { |
850 this.executeItem(item); | 852 this.executeItem(item); |
| 853 } else { |
| 854 this.hide(); |
851 } | 855 } |
852 } else { | 856 } else { |
853 this.showMenu(); | 857 this.show(); |
854 } | 858 } |
| 859 e.preventDefault(); |
855 break; | 860 break; |
856 } | 861 } |
857 }, | 862 }, |
858 | 863 |
859 selectedIndex_: -1, | 864 selectedIndex_: -1, |
860 setSelectedIndex: function(i) { | 865 setSelectedIndex: function(i) { |
861 if (i != this.selectedIndex_) { | 866 if (i != this.selectedIndex_) { |
862 var items = this.menu.children; | 867 var items = this.menu.children; |
863 var oldItem = items[this.selectedIndex_]; | 868 var oldItem = items[this.selectedIndex_]; |
864 if (oldItem) { | 869 if (oldItem) { |
(...skipping 13 matching lines...) Expand all Loading... |
878 | 883 |
879 executeItem: function(item) { | 884 executeItem: function(item) { |
880 var section = Section[item.getAttribute('section')]; | 885 var section = Section[item.getAttribute('section')]; |
881 var show = item.getAttribute('show') == 'true'; | 886 var show = item.getAttribute('show') == 'true'; |
882 if (show) { | 887 if (show) { |
883 showSection(section); | 888 showSection(section); |
884 } else { | 889 } else { |
885 hideSection(section); | 890 hideSection(section); |
886 } | 891 } |
887 | 892 |
888 this.hideMenu(); | 893 this.hide(); |
889 saveShownSections(); | 894 saveShownSections(); |
890 } | 895 } |
891 }; | 896 }; |
892 | 897 |
893 new OptionMenu($('option-button'), $('option-menu')); | 898 var optionMenu = new OptionMenu($('option-button'), $('option-menu')); |
894 | 899 |
895 $('most-visited').addEventListener('click', function(e) { | 900 $('most-visited').addEventListener('click', function(e) { |
896 var target = e.target; | 901 var target = e.target; |
897 if (hasClass(target, 'pin')) { | 902 if (hasClass(target, 'pin')) { |
898 mostVisited.togglePinned(mostVisited.getItem(target)); | 903 mostVisited.togglePinned(mostVisited.getItem(target)); |
899 e.preventDefault(); | 904 e.preventDefault(); |
900 } else if (hasClass(target, 'remove')) { | 905 } else if (hasClass(target, 'remove')) { |
901 mostVisited.blacklist(mostVisited.getItem(target)); | 906 mostVisited.blacklist(mostVisited.getItem(target)); |
902 e.preventDefault(); | 907 e.preventDefault(); |
903 } | 908 } |
(...skipping 29 matching lines...) Expand all Loading... |
933 return el.sessionId !== undefined; | 938 return el.sessionId !== undefined; |
934 }); | 939 }); |
935 if (el) { | 940 if (el) { |
936 chrome.send('reopenTab', [String(el.sessionId)]); | 941 chrome.send('reopenTab', [String(el.sessionId)]); |
937 e.preventDefault(); | 942 e.preventDefault(); |
938 } | 943 } |
939 } | 944 } |
940 | 945 |
941 recentTabs.addEventListener('mouseover', maybeShowWindowMenu); | 946 recentTabs.addEventListener('mouseover', maybeShowWindowMenu); |
942 recentTabs.addEventListener('focus', maybeShowWindowMenu, true); | 947 recentTabs.addEventListener('focus', maybeShowWindowMenu, true); |
943 recentTabs.addEventListener('mouseout', maybeHideWindowMenu); | 948 |
944 recentTabs.addEventListener('blur', maybeHideWindowMenu, true); | |
945 | 949 |
946 function maybeShowWindowMenu(e) { | 950 function maybeShowWindowMenu(e) { |
947 var el = findAncestor(e.target, function(el) { | 951 var el = findAncestor(e.target, function(el) { |
948 return el.tabItems !== undefined; | 952 return el.tabItems !== undefined; |
949 }); | 953 }); |
950 if (el) { | 954 if (el) { |
951 showWindowMenu(el, el.tabItems); | 955 windowMenu.show(e, el, el.tabItems); |
952 } | 956 } |
953 } | 957 } |
954 | 958 |
955 function maybeHideWindowMenu(e) { | 959 /** |
956 var el = findAncestor(e.target, function(el) { | 960 * This object represents a window/tooltip representing a closed window. It is |
957 return el.tabItems !== undefined; | 961 * shown when hovering over a closed window item or when the item is focused. It |
958 }); | 962 * gets hidden when blurred or when mousing out of the menu or the item. |
959 if (el) { | 963 * @param {Element} menuEl The element to use as the menu. |
960 $('window-menu').style.display = 'none'; | 964 * @constructor |
| 965 */ |
| 966 function WindowMenu(menuEl) { |
| 967 this.menuEl = menuEl; |
| 968 var self = this; |
| 969 this.boundHide_ = bind(this.hide, this); |
| 970 menuEl.onmouseover = function() { |
| 971 clearTimeout(self.timer); |
| 972 }; |
| 973 menuEl.onmouseout = this.boundHide_; |
| 974 } |
| 975 |
| 976 WindowMenu.prototype = { |
| 977 timer: 0, |
| 978 show: function(e, linkEl, tabs) { |
| 979 optionMenu.hide(); |
| 980 |
| 981 clearTimeout(this.timer); |
| 982 processData('#window-menu', tabs); |
| 983 var rect = linkEl.getBoundingClientRect(); |
| 984 var bodyRect = document.body.getBoundingClientRect() |
| 985 var rtl = document.documentElement.dir == 'rtl'; |
| 986 |
| 987 this.menuEl.style.display = 'block'; |
| 988 this.menuEl.style.left = (rtl ? |
| 989 rect.left + bodyRect.left + rect.width - this.menuEl.offsetWidth : |
| 990 rect.left + bodyRect.left) + 'px'; |
| 991 this.menuEl.style.top = rect.top + bodyRect.top + rect.height + 'px'; |
| 992 |
| 993 if (e.type == 'focus') { |
| 994 linkEl.onblur = this.boundHide_; |
| 995 } else { // mouseover |
| 996 linkEl.onmouseout = this.boundHide_; |
| 997 } |
| 998 }, |
| 999 hide: function() { |
| 1000 // Delay before hiding. |
| 1001 var self = this; |
| 1002 this.timer = setTimeout(function() { |
| 1003 self.menuEl.style.display = 'none'; |
| 1004 }, 100); |
| 1005 } |
| 1006 }; |
| 1007 |
| 1008 var windowMenu = new WindowMenu($('window-menu')); |
| 1009 |
| 1010 function getCheckboxHandler(section) { |
| 1011 return function(e) { |
| 1012 if (e.type == 'keydown') { |
| 1013 if (e.keyIdentifier == 'Enter') { |
| 1014 e.target.checked = !e.target.checked; |
| 1015 } else { |
| 1016 return; |
| 1017 } |
| 1018 } |
| 1019 if (e.target.checked) { |
| 1020 showSection(section); |
| 1021 } else { |
| 1022 hideSection(section); |
| 1023 } |
| 1024 saveShownSections(); |
961 } | 1025 } |
962 } | 1026 } |
963 | 1027 |
964 function showWindowMenu(el, tabs) { | 1028 $('thumb-checkbox').addEventListener('change', |
965 var menuEl = $('window-menu'); | 1029 getCheckboxHandler(Section.THUMB)); |
966 processData('#window-menu', tabs); | 1030 $('thumb-checkbox').addEventListener('keydown', |
967 var rect = el.getBoundingClientRect(); | 1031 getCheckboxHandler(Section.THUMB)); |
968 var bodyRect = document.body.getBoundingClientRect() | 1032 $('list-checkbox').addEventListener('change', |
969 var rtl = document.documentElement.dir == 'rtl'; | 1033 getCheckboxHandler(Section.LIST)); |
970 | 1034 $('list-checkbox').addEventListener('keydown', |
971 menuEl.style.display = 'block'; | 1035 getCheckboxHandler(Section.LIST)); |
972 menuEl.style.left = (rtl ? | |
973 rect.left + bodyRect.left + rect.width - menuEl.offsetWidth : | |
974 rect.left + bodyRect.left) + 'px'; | |
975 menuEl.style.top = rect.top + bodyRect.top + rect.height + 'px'; | |
976 | |
977 } | |
978 | |
979 $('thumb-checkbox').addEventListener('change', function(e) { | |
980 if (e.target.checked) { | |
981 showSection(Section.THUMB); | |
982 } else { | |
983 hideSection(Section.THUMB); | |
984 } | |
985 saveShownSections(); | |
986 }); | |
987 | |
988 $('list-checkbox').addEventListener('change', function(e) { | |
989 if (e.target.checked) { | |
990 showSection(Section.LIST); | |
991 } else { | |
992 hideSection(Section.LIST); | |
993 } | |
994 saveShownSections(); | |
995 }); | |
996 | 1036 |
997 window.addEventListener('load', bind(logEvent, global, 'onload fired')); | 1037 window.addEventListener('load', bind(logEvent, global, 'onload fired')); |
998 window.addEventListener('load', onDataLoaded); | 1038 window.addEventListener('load', onDataLoaded); |
999 window.addEventListener('resize', handleWindowResize); | 1039 window.addEventListener('resize', handleWindowResize); |
1000 document.addEventListener('DOMContentLoaded', bind(logEvent, global, | 1040 document.addEventListener('DOMContentLoaded', bind(logEvent, global, |
1001 'domcontentloaded fired')); | 1041 'domcontentloaded fired')); |
1002 | 1042 |
| 1043 function hideAllMenus() { |
| 1044 windowMenu.hide(); |
| 1045 optionMenu.hide(); |
| 1046 } |
| 1047 |
| 1048 window.addEventListener('blur', hideAllMenus); |
| 1049 window.addEventListener('keydown', function(e) { |
| 1050 if (e.keyIdentifier == 'Alt' || e.keyIdentifier == 'Meta') { |
| 1051 hideAllMenus(); |
| 1052 } |
| 1053 }, true); |
| 1054 |
1003 // DnD | 1055 // DnD |
1004 | 1056 |
1005 var dnd = { | 1057 var dnd = { |
1006 currentOverItem: null, | 1058 currentOverItem: null, |
1007 dragItem: null, | 1059 dragItem: null, |
1008 startX: 0, | 1060 startX: 0, |
1009 startY: 0, | 1061 startY: 0, |
1010 startScreenX: 0, | 1062 startScreenX: 0, |
1011 startScreenY: 0, | 1063 startScreenY: 0, |
1012 dragEndTimer: null, | 1064 dragEndTimer: null, |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1118 el.addEventListener('dragover', bind(this.handleDragOver, this)); | 1170 el.addEventListener('dragover', bind(this.handleDragOver, this)); |
1119 el.addEventListener('dragleave', bind(this.handleDragLeave, this)); | 1171 el.addEventListener('dragleave', bind(this.handleDragLeave, this)); |
1120 el.addEventListener('drop', bind(this.handleDrop, this)); | 1172 el.addEventListener('drop', bind(this.handleDrop, this)); |
1121 el.addEventListener('dragend', bind(this.handleDragEnd, this)); | 1173 el.addEventListener('dragend', bind(this.handleDragEnd, this)); |
1122 el.addEventListener('drag', bind(this.handleDrag, this)); | 1174 el.addEventListener('drag', bind(this.handleDrag, this)); |
1123 el.addEventListener('mousedown', bind(this.handleMouseDown, this)); | 1175 el.addEventListener('mousedown', bind(this.handleMouseDown, this)); |
1124 } | 1176 } |
1125 }; | 1177 }; |
1126 | 1178 |
1127 dnd.init(); | 1179 dnd.init(); |
OLD | NEW |