| 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 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 87 | 87 |
| 88 gotMostVisited = true; | 88 gotMostVisited = true; |
| 89 onDataLoaded(); | 89 onDataLoaded(); |
| 90 } | 90 } |
| 91 | 91 |
| 92 function downloadsList(data) { | 92 function downloadsList(data) { |
| 93 logEvent('received downloads'); | 93 logEvent('received downloads'); |
| 94 | 94 |
| 95 // We should only show complete downloads. | 95 // We should only show complete downloads. |
| 96 data = data.filter(function(d) { | 96 data = data.filter(function(d) { |
| 97 d.type = 'download'; |
| 98 d.timestamp = d.started; |
| 97 return d.state == 'COMPLETE'; | 99 return d.state == 'COMPLETE'; |
| 98 }); | 100 }); |
| 99 data.length = Math.min(data.length, 5); | 101 |
| 100 processData('#download-items', data); | 102 gotRecentItems(data, 'download'); |
| 101 } | 103 } |
| 102 | 104 |
| 103 function recentlyClosedTabs(data) { | 105 function recentlyClosedTabs(data) { |
| 104 logEvent('received recently closed tabs'); | 106 logEvent('received recently closed tabs'); |
| 105 data.length = Math.min(data.length, 5); | 107 |
| 106 processData('#tab-items', data); | 108 // We handle timestamp 0 as now |
| 109 data.forEach(function(d) { |
| 110 if (d.timestamp == 0) { |
| 111 d.timestamp = Date.now(); |
| 112 } |
| 113 }); |
| 114 |
| 115 gotRecentItems(data); |
| 116 } |
| 117 |
| 118 var recentItems = []; |
| 119 var recentItemKeys = {}; |
| 120 |
| 121 function getRecentItemKey(d) { |
| 122 // type == window does not have a URL |
| 123 return d.type + (d.url || d.sessionId) + d.timestamp; |
| 124 } |
| 125 |
| 126 function gotRecentItems(data) { |
| 127 // Add new items |
| 128 for (var i = 0; i < data.length; i++) { |
| 129 var d = data[i]; |
| 130 var key = getRecentItemKey(d); |
| 131 if (!(key in recentItemKeys)) { |
| 132 recentItems.push(d); |
| 133 recentItemKeys[key] = true; |
| 134 } |
| 135 } |
| 136 |
| 137 recentItems.sort(function(d1, d2) { |
| 138 return d2.timestamp - d1.timestamp; |
| 139 }); |
| 140 |
| 141 renderRecentItems(); |
| 142 } |
| 143 |
| 144 function renderRecentItems() { |
| 145 // When tips are shown we only show 10 items |
| 146 var desiredCount = shownSections & Section.TIPS ? 10 : 20; |
| 147 desiredCount -= 2; // Show all downloads and all history uses two rows. |
| 148 |
| 149 processData('#recent-activities > .item-container', |
| 150 recentItems.slice(0, desiredCount)); |
| 107 } | 151 } |
| 108 | 152 |
| 109 function onShownSections(mask) { | 153 function onShownSections(mask) { |
| 110 logEvent('received shown sections'); | 154 logEvent('received shown sections'); |
| 111 if (mask != shownSections) { | 155 if (mask != shownSections) { |
| 156 var oldShownSections = shownSections; |
| 157 shownSections = mask; |
| 112 | 158 |
| 113 // Only invalidate most visited if needed. | 159 // Only invalidate most visited if needed. |
| 114 if ((mask & Section.THUMB) != (shownSections & Section.THUMB) || | 160 if ((mask & Section.THUMB) != (oldShownSections & Section.THUMB) || |
| 115 (mask & Section.LIST) != (shownSections & Section.LIST)) { | 161 (mask & Section.LIST) != (oldShownSections & Section.LIST)) { |
| 116 mostVisited.invalidate(); | 162 mostVisited.invalidate(); |
| 117 } | 163 } |
| 118 | 164 |
| 119 shownSections = mask; | 165 if ((mask & Section.RECENT) != (oldShownSections & Section.RECENT)) { |
| 166 notifyLowerSectionForChange(Section.RECENT); |
| 167 } |
| 168 |
| 169 if ((mask & Section.TIPS) != (oldShownSections & Section.TIPS)) { |
| 170 notifyLowerSectionForChange(Section.TIPS); |
| 171 } |
| 172 |
| 120 mostVisited.updateDisplayMode(); | 173 mostVisited.updateDisplayMode(); |
| 121 layoutLowerSections(); | 174 layoutLowerSections(); |
| 122 updateOptionMenu(); | 175 updateOptionMenu(); |
| 123 } | 176 } |
| 124 | 177 |
| 125 gotShownSections = true; | 178 gotShownSections = true; |
| 126 onDataLoaded(); | 179 onDataLoaded(); |
| 127 } | 180 } |
| 128 | 181 |
| 129 function saveShownSections() { | 182 function saveShownSections() { |
| 130 chrome.send('setShownSections', [String(shownSections)]); | 183 chrome.send('setShownSections', [String(shownSections)]); |
| 131 } | 184 } |
| 132 | 185 |
| 133 function tips(data) { | 186 function tips(data) { |
| 134 logEvent('received tips data'); | 187 logEvent('received tips data'); |
| 135 data.length = Math.min(data.length, 5); | 188 data.length = Math.min(data.length, 5); |
| 136 processData('#tip-items', data); | 189 processData('#tip-items', data); |
| 137 } | 190 } |
| 138 | 191 |
| 139 // This global variable is used to skip parts of the DOM tree for the global | |
| 140 // jst processing done by the i18n. | |
| 141 var processing = false; | |
| 142 | |
| 143 function processData(selector, data) { | 192 function processData(selector, data) { |
| 144 var output = document.querySelector(selector); | 193 var output = document.querySelector(selector); |
| 145 | 194 |
| 146 // Wait until ready | 195 // Wait until ready |
| 147 if (typeof JsEvalContext !== 'function' || !output) { | 196 if (typeof JsEvalContext !== 'function' || !output) { |
| 148 logEvent('JsEvalContext is not yet available, ' + selector); | 197 logEvent('JsEvalContext is not yet available, ' + selector); |
| 149 document.addEventListener('DOMContentLoaded', function() { | 198 document.addEventListener('DOMContentLoaded', function() { |
| 150 processData(selector, data); | 199 processData(selector, data); |
| 151 }); | 200 }); |
| 152 } else { | 201 } else { |
| 153 var d0 = Date.now(); | 202 var d0 = Date.now(); |
| 154 var input = new JsEvalContext(data); | 203 var input = new JsEvalContext(data); |
| 155 processing = true; | |
| 156 jstProcess(input, output); | 204 jstProcess(input, output); |
| 157 processing = false; | |
| 158 logEvent('processData: ' + selector + ', ' + (Date.now() - d0)); | 205 logEvent('processData: ' + selector + ', ' + (Date.now() - d0)); |
| 159 } | 206 } |
| 160 } | 207 } |
| 161 | 208 |
| 162 function getThumbnailClassName(data) { | 209 function getThumbnailClassName(data) { |
| 163 return 'thumbnail-container' + | 210 return 'thumbnail-container' + |
| 164 (data.pinned ? ' pinned' : '') + | 211 (data.pinned ? ' pinned' : '') + |
| 165 (data.filler ? ' filler' : ''); | 212 (data.filler ? ' filler' : ''); |
| 166 } | 213 } |
| 167 | 214 |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 265 // THUMBS and LIST are mutually exclusive. | 312 // THUMBS and LIST are mutually exclusive. |
| 266 if (section == Section.THUMB) { | 313 if (section == Section.THUMB) { |
| 267 // hide LIST | 314 // hide LIST |
| 268 shownSections &= ~Section.LIST; | 315 shownSections &= ~Section.LIST; |
| 269 mostVisited.invalidate(); | 316 mostVisited.invalidate(); |
| 270 } else if (section == Section.LIST) { | 317 } else if (section == Section.LIST) { |
| 271 // hide THUMB | 318 // hide THUMB |
| 272 shownSections &= ~Section.THUMB; | 319 shownSections &= ~Section.THUMB; |
| 273 mostVisited.invalidate(); | 320 mostVisited.invalidate(); |
| 274 } else { | 321 } else { |
| 275 notifyLowerSectionForChange(section, false); | 322 notifyLowerSectionForChange(section); |
| 276 layoutLowerSections(); | 323 layoutLowerSections(); |
| 277 } | 324 } |
| 278 | 325 |
| 279 updateOptionMenu(); | 326 updateOptionMenu(); |
| 280 mostVisited.updateDisplayMode(); | 327 mostVisited.updateDisplayMode(); |
| 281 mostVisited.layout(); | 328 mostVisited.layout(); |
| 282 } | 329 } |
| 283 } | 330 } |
| 284 | 331 |
| 285 function hideSection(section) { | 332 function hideSection(section) { |
| 286 if (section & shownSections) { | 333 if (section & shownSections) { |
| 287 shownSections &= ~section; | 334 shownSections &= ~section; |
| 288 | 335 |
| 289 if (section & Section.THUMB || section & Section.LIST) { | 336 if (section & Section.THUMB || section & Section.LIST) { |
| 290 mostVisited.invalidate(); | 337 mostVisited.invalidate(); |
| 291 } | 338 } |
| 292 | 339 |
| 293 if (section & Section.RECENT|| section & Section.TIPS) { | 340 if (section & Section.RECENT || section & Section.TIPS) { |
| 294 notifyLowerSectionForChange(section, true); | 341 notifyLowerSectionForChange(section); |
| 295 layoutLowerSections(); | 342 layoutLowerSections(); |
| 296 } | 343 } |
| 297 | 344 |
| 298 updateOptionMenu(); | 345 updateOptionMenu(); |
| 299 mostVisited.updateDisplayMode(); | 346 mostVisited.updateDisplayMode(); |
| 300 mostVisited.layout(); | 347 mostVisited.layout(); |
| 301 } | 348 } |
| 302 } | 349 } |
| 303 | 350 |
| 304 function notifyLowerSectionForChange(section, large) { | 351 function notifyLowerSectionForChange(section) { |
| 305 // Notify recent and tips if they need to display more data. | 352 // Notify recent and tips if they need to display more data. |
| 306 if (section == Section.RECENT || section == Section.TIPS) { | 353 if (section == Section.RECENT || section == Section.TIPS) { |
| 307 // we are hiding one of them so if the other one is visible it is now | |
| 308 // {@code large}. | |
| 309 if (shownSections & Section.RECENT) { | 354 if (shownSections & Section.RECENT) { |
| 310 recentChangedSize(large); | 355 recentChangedSize(!(shownSections & Section.TIPS)); |
| 311 } else if (shownSections & Section.TIPS) { | 356 } |
| 312 tipsChangedSize(large); | 357 if (shownSections & Section.TIPS) { |
| 358 tipsChangedSize(!(shownSections & Section.RECENT)); |
| 313 } | 359 } |
| 314 } | 360 } |
| 315 } | 361 } |
| 316 | 362 |
| 317 var mostVisited = { | 363 var mostVisited = { |
| 318 getItem: function(el) { | 364 getItem: function(el) { |
| 319 return findAncestorByClass(el, 'thumbnail-container'); | 365 return findAncestorByClass(el, 'thumbnail-container'); |
| 320 }, | 366 }, |
| 321 | 367 |
| 322 getHref: function(el) { | 368 getHref: function(el) { |
| (...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 556 } | 602 } |
| 557 }); | 603 }); |
| 558 | 604 |
| 559 this.dirty_ = false; | 605 this.dirty_ = false; |
| 560 | 606 |
| 561 logEvent('mostVisited.layout: ' + (Date.now() - d0)); | 607 logEvent('mostVisited.layout: ' + (Date.now() - d0)); |
| 562 } | 608 } |
| 563 }; | 609 }; |
| 564 | 610 |
| 565 function recentChangedSize(large) { | 611 function recentChangedSize(large) { |
| 566 // TODO(arv): Implement | 612 if (large) { |
| 613 addClass($('recent-activities'), 'large'); |
| 614 } else { |
| 615 removeClass($('recent-activities'), 'large'); |
| 616 } |
| 617 |
| 618 renderRecentItems(); |
| 567 } | 619 } |
| 568 | 620 |
| 569 function tipsChangedSize(large) { | 621 function tipsChangedSize(large) { |
| 570 // TODO(arv): Implement | 622 // TODO(arv): Implement |
| 571 } | 623 } |
| 572 | 624 |
| 573 // Recent activities | 625 // Recent activities |
| 574 | 626 |
| 575 function layoutLowerSections() { | 627 function layoutLowerSections() { |
| 576 // This lower sections are inline blocks so all we need to do is to set the | 628 // This lower sections are inline blocks so all we need to do is to set the |
| (...skipping 340 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 917 }); | 969 }); |
| 918 | 970 |
| 919 function handleIfEnterKey(f) { | 971 function handleIfEnterKey(f) { |
| 920 return function(e) { | 972 return function(e) { |
| 921 if (e.keyIdentifier == 'Enter') { | 973 if (e.keyIdentifier == 'Enter') { |
| 922 f(e); | 974 f(e); |
| 923 } | 975 } |
| 924 }; | 976 }; |
| 925 } | 977 } |
| 926 | 978 |
| 927 $('downloads').addEventListener('click', maybeOpenFile); | |
| 928 $('downloads').addEventListener('keydown', handleIfEnterKey(maybeOpenFile)); | |
| 929 | |
| 930 function maybeOpenFile(e) { | 979 function maybeOpenFile(e) { |
| 931 var el = findAncestor(e.target, function(el) { | 980 var el = findAncestor(e.target, function(el) { |
| 932 return el.fileId !== undefined; | 981 return el.fileId !== undefined; |
| 933 }); | 982 }); |
| 934 if (el) { | 983 if (el) { |
| 935 chrome.send('openFile', [String(el.fileId)]); | 984 chrome.send('openFile', [String(el.fileId)]); |
| 936 e.preventDefault(); | 985 e.preventDefault(); |
| 937 } | 986 } |
| 938 } | 987 } |
| 939 | 988 |
| 940 var recentTabs = $('recent-tabs'); | |
| 941 recentTabs.addEventListener('click', maybeReopenTab); | |
| 942 recentTabs.addEventListener('keydown', handleIfEnterKey(maybeReopenTab)); | |
| 943 | |
| 944 function maybeReopenTab(e) { | 989 function maybeReopenTab(e) { |
| 945 var el = findAncestor(e.target, function(el) { | 990 var el = findAncestor(e.target, function(el) { |
| 946 return el.sessionId !== undefined; | 991 return el.sessionId !== undefined; |
| 947 }); | 992 }); |
| 948 if (el) { | 993 if (el) { |
| 949 chrome.send('reopenTab', [String(el.sessionId)]); | 994 chrome.send('reopenTab', [String(el.sessionId)]); |
| 950 e.preventDefault(); | 995 e.preventDefault(); |
| 951 } | 996 } |
| 952 } | 997 } |
| 953 | 998 |
| 954 recentTabs.addEventListener('mouseover', maybeShowWindowMenu); | |
| 955 recentTabs.addEventListener('focus', maybeShowWindowMenu, true); | |
| 956 | |
| 957 | |
| 958 function maybeShowWindowMenu(e) { | 999 function maybeShowWindowMenu(e) { |
| 959 var el = findAncestor(e.target, function(el) { | 1000 var f = function(el) { |
| 960 return el.tabItems !== undefined; | 1001 return el.tabItems !== undefined; |
| 961 }); | 1002 }; |
| 962 if (el) { | 1003 var el = findAncestor(e.target, f); |
| 1004 var relatedEl = findAncestor(e.relatedTarget, f); |
| 1005 if (el && el != relatedEl) { |
| 963 windowMenu.show(e, el, el.tabItems); | 1006 windowMenu.show(e, el, el.tabItems); |
| 964 } | 1007 } |
| 965 } | 1008 } |
| 966 | 1009 |
| 1010 |
| 1011 var recentActivitiesElement = $('recent-activities'); |
| 1012 recentActivitiesElement.addEventListener('click', maybeOpenFile); |
| 1013 recentActivitiesElement.addEventListener('keydown', |
| 1014 handleIfEnterKey(maybeOpenFile)); |
| 1015 |
| 1016 recentActivitiesElement.addEventListener('click', maybeReopenTab); |
| 1017 recentActivitiesElement.addEventListener('keydown', |
| 1018 handleIfEnterKey(maybeReopenTab)); |
| 1019 |
| 1020 recentActivitiesElement.addEventListener('mouseover', maybeShowWindowMenu); |
| 1021 recentActivitiesElement.addEventListener('focus', maybeShowWindowMenu, true); |
| 1022 |
| 967 /** | 1023 /** |
| 968 * This object represents a window/tooltip representing a closed window. It is | 1024 * This object represents a window/tooltip representing a closed window. It is |
| 969 * shown when hovering over a closed window item or when the item is focused. It | 1025 * shown when hovering over a closed window item or when the item is focused. It |
| 970 * gets hidden when blurred or when mousing out of the menu or the item. | 1026 * gets hidden when blurred or when mousing out of the menu or the item. |
| 971 * @param {Element} menuEl The element to use as the menu. | 1027 * @param {Element} menuEl The element to use as the menu. |
| 972 * @constructor | 1028 * @constructor |
| 973 */ | 1029 */ |
| 974 function WindowMenu(menuEl) { | 1030 function WindowMenu(menuEl) { |
| 1031 // TODO(arv): Rename WindowTooltip and make it behave even more like a |
| 1032 // tooltip. |
| 975 this.menuEl = menuEl; | 1033 this.menuEl = menuEl; |
| 976 var self = this; | |
| 977 this.boundHide_ = bind(this.hide, this); | 1034 this.boundHide_ = bind(this.hide, this); |
| 978 menuEl.onmouseover = function() { | 1035 this.boundHandleMouseOut_ = bind(this.handleMouseOut, this); |
| 979 clearTimeout(self.timer); | |
| 980 }; | |
| 981 menuEl.onmouseout = this.boundHide_; | |
| 982 } | 1036 } |
| 983 | 1037 |
| 984 WindowMenu.prototype = { | 1038 WindowMenu.prototype = { |
| 985 timer: 0, | 1039 timer: 0, |
| 986 show: function(e, linkEl, tabs) { | 1040 show: function(e, linkEl, tabs) { |
| 987 optionMenu.hide(); | 1041 optionMenu.hide(); |
| 988 | 1042 |
| 989 clearTimeout(this.timer); | 1043 clearTimeout(this.timer); |
| 990 processData('#window-menu', tabs); | 1044 processData('#window-menu', tabs); |
| 991 var rect = linkEl.getBoundingClientRect(); | 1045 var rect = linkEl.getBoundingClientRect(); |
| 992 var bodyRect = document.body.getBoundingClientRect() | 1046 var bodyRect = document.body.getBoundingClientRect() |
| 993 var rtl = document.documentElement.dir == 'rtl'; | 1047 var rtl = document.documentElement.dir == 'rtl'; |
| 994 | 1048 |
| 995 this.menuEl.style.display = 'block'; | 1049 this.menuEl.style.display = 'block'; |
| 996 this.menuEl.style.left = (rtl ? | 1050 // When focused show below, like a drop down menu. |
| 997 rect.left + bodyRect.left + rect.width - this.menuEl.offsetWidth : | 1051 if (e.type == 'focus') { |
| 998 rect.left + bodyRect.left) + 'px'; | 1052 this.menuEl.style.left = (rtl ? |
| 999 this.menuEl.style.top = rect.top + bodyRect.top + rect.height + 'px'; | 1053 rect.left + bodyRect.left + rect.width - this.menuEl.offsetWidth : |
| 1054 rect.left + bodyRect.left) + 'px'; |
| 1055 this.menuEl.style.top = rect.top + bodyRect.top + rect.height + 'px'; |
| 1056 } else { |
| 1057 this.menuEl.style.left = bodyRect.left + (rtl ? |
| 1058 e.clientX - this.menuEl.offsetWidth : |
| 1059 e.clientX) + 'px'; |
| 1060 this.menuEl.style.top = e.clientY + bodyRect.top + 'px'; |
| 1061 } |
| 1000 | 1062 |
| 1001 if (e.type == 'focus') { | 1063 if (e.type == 'focus') { |
| 1002 linkEl.onblur = this.boundHide_; | 1064 linkEl.onblur = this.boundHide_; |
| 1003 } else { // mouseover | 1065 } else { // mouseover |
| 1004 linkEl.onmouseout = this.boundHide_; | 1066 linkEl.onmouseout = this.boundHandleMouseOut_; |
| 1005 } | 1067 } |
| 1006 }, | 1068 }, |
| 1007 hide: function() { | 1069 handleMouseOut: function(e) { |
| 1070 // Don't hide when move to another item in the link. |
| 1071 var f = function(el) { |
| 1072 return el.tabItems !== undefined; |
| 1073 }; |
| 1074 var el = findAncestor(e.target, f); |
| 1075 var relatedEl = findAncestor(e.relatedTarget, f); |
| 1076 if (el && el != relatedEl) { |
| 1077 this.hide(); |
| 1078 } |
| 1079 }, |
| 1080 hide: function(e) { |
| 1008 // Delay before hiding. | 1081 // Delay before hiding. |
| 1009 var self = this; | 1082 var self = this; |
| 1010 this.timer = setTimeout(function() { | 1083 this.timer = setTimeout(function() { |
| 1011 self.menuEl.style.display = 'none'; | 1084 self.menuEl.style.display = 'none'; |
| 1012 }, 100); | 1085 }, 100); |
| 1013 } | 1086 } |
| 1014 }; | 1087 }; |
| 1015 | 1088 |
| 1016 var windowMenu = new WindowMenu($('window-menu')); | 1089 var windowMenu = new WindowMenu($('window-menu')); |
| 1017 | 1090 |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1203 el.addEventListener('dragover', bind(this.handleDragOver, this)); | 1276 el.addEventListener('dragover', bind(this.handleDragOver, this)); |
| 1204 el.addEventListener('dragleave', bind(this.handleDragLeave, this)); | 1277 el.addEventListener('dragleave', bind(this.handleDragLeave, this)); |
| 1205 el.addEventListener('drop', bind(this.handleDrop, this)); | 1278 el.addEventListener('drop', bind(this.handleDrop, this)); |
| 1206 el.addEventListener('dragend', bind(this.handleDragEnd, this)); | 1279 el.addEventListener('dragend', bind(this.handleDragEnd, this)); |
| 1207 el.addEventListener('drag', bind(this.handleDrag, this)); | 1280 el.addEventListener('drag', bind(this.handleDrag, this)); |
| 1208 el.addEventListener('mousedown', bind(this.handleMouseDown, this)); | 1281 el.addEventListener('mousedown', bind(this.handleMouseDown, this)); |
| 1209 } | 1282 } |
| 1210 }; | 1283 }; |
| 1211 | 1284 |
| 1212 dnd.init(); | 1285 dnd.init(); |
| OLD | NEW |