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