| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 var MAX_APPS_PER_ROW = []; | 5 var MAX_APPS_PER_ROW = []; |
| 6 MAX_APPS_PER_ROW[LayoutMode.SMALL] = 4; | 6 MAX_APPS_PER_ROW[LayoutMode.SMALL] = 4; |
| 7 MAX_APPS_PER_ROW[LayoutMode.NORMAL] = 6; | 7 MAX_APPS_PER_ROW[LayoutMode.NORMAL] = 6; |
| 8 | 8 |
| 9 function getAppsCallback(data) { | 9 function getAppsCallback(data) { |
| 10 logEvent('received apps'); | 10 logEvent('received apps'); |
| 11 | 11 |
| 12 // In the case of prefchange-triggered updates, we don't receive this flag. | 12 // In the case of prefchange-triggered updates, we don't receive this flag. |
| 13 // Just leave it set as it was before in that case. | 13 // Just leave it set as it was before in that case. |
| 14 if ('showPromo' in data) | 14 if ('showPromo' in data) |
| 15 apps.showPromo = data.showPromo; | 15 apps.showPromo = data.showPromo; |
| 16 | 16 |
| 17 var appsSection = $('apps'); | 17 var appsSection = $('apps'); |
| 18 var appsSectionContent = $('apps-content'); | 18 var appsSectionContent = $('apps-content'); |
| 19 var appsMiniview = appsSection.getElementsByClassName('miniview')[0]; | 19 var appsMiniview = appsSection.getElementsByClassName('miniview')[0]; |
| 20 var appsPromo = $('apps-promo'); | 20 var appsPromo = $('apps-promo'); |
| 21 var appsPromoLink = $('apps-promo-link'); |
| 21 var appsPromoPing = APP_LAUNCH_URL.PING_WEBSTORE + '+' + apps.showPromo; | 22 var appsPromoPing = APP_LAUNCH_URL.PING_WEBSTORE + '+' + apps.showPromo; |
| 22 var webStoreEntry, webStoreMiniEntry; | 23 var webStoreEntry, webStoreMiniEntry; |
| 23 | 24 |
| 24 // Hide menu options that are not supported on the OS or windowing system. | 25 // Hide menu options that are not supported on the OS or windowing system. |
| 25 | 26 |
| 26 // The "Launch as Window" menu option. | 27 // The "Launch as Window" menu option. |
| 27 $('apps-launch-type-window-menu-item').hidden = data.disableAppWindowLaunch; | 28 $('apps-launch-type-window-menu-item').hidden = data.disableAppWindowLaunch; |
| 28 | 29 |
| 29 // The "Create App Shortcut" menu option. | 30 // The "Create App Shortcut" menu option. |
| 30 $('apps-create-shortcut-command-menu-item').hidden = | 31 $('apps-create-shortcut-command-menu-item').hidden = |
| (...skipping 10 matching lines...) Expand all Loading... |
| 41 return a.app_launch_index - b.app_launch_index; | 42 return a.app_launch_index - b.app_launch_index; |
| 42 }); | 43 }); |
| 43 | 44 |
| 44 // Determines if the web store link should be detached and place in the | 45 // Determines if the web store link should be detached and place in the |
| 45 // top right of the screen. | 46 // top right of the screen. |
| 46 apps.detachWebstoreEntry = | 47 apps.detachWebstoreEntry = |
| 47 !apps.showPromo && data.apps.length >= MAX_APPS_PER_ROW[layoutMode]; | 48 !apps.showPromo && data.apps.length >= MAX_APPS_PER_ROW[layoutMode]; |
| 48 | 49 |
| 49 markNewApps(data.apps); | 50 markNewApps(data.apps); |
| 50 apps.data = data.apps; | 51 apps.data = data.apps; |
| 51 if (!apps.detachWebstoreEntry) | |
| 52 apps.data.push('web-store-entry'); | |
| 53 | 52 |
| 54 clearClosedMenu(apps.menu); | 53 clearClosedMenu(apps.menu); |
| 55 | 54 |
| 56 // We wait for the app icons to load before displaying them, but never wait | 55 // We wait for the app icons to load before displaying them, but never wait |
| 57 // longer than 200ms. | 56 // longer than 200ms. |
| 58 apps.loadedImages = 0; | 57 apps.loadedImages = 0; |
| 59 apps.imageTimer = setTimeout(apps.showImages.bind(apps), 200); | 58 apps.imageTimer = setTimeout(apps.showImages.bind(apps), 200); |
| 60 | 59 |
| 61 data.apps.forEach(function(app) { | 60 data.apps.forEach(function(app) { |
| 62 appsSectionContent.appendChild(apps.createElement(app)); | 61 appsSectionContent.appendChild(apps.createElement(app)); |
| 63 }); | 62 }); |
| 64 | 63 |
| 65 webStoreEntry = apps.createWebStoreElement(); | 64 if (data.showPromo) { |
| 66 webStoreEntry.querySelector('a').setAttribute('ping', appsPromoPing); | 65 // Add the promo content... |
| 67 appsSectionContent.appendChild(webStoreEntry); | 66 $('apps-promo-heading').textContent = data.promoHeader; |
| 67 appsPromoLink.href = data.promoLink; |
| 68 appsPromoLink.textContent = data.promoButton; |
| 69 appsPromoLink.ping = appsPromoPing; |
| 70 $('apps-promo-hide').textContent = data.promoExpire; |
| 71 |
| 72 // ... then display the promo. |
| 73 document.documentElement.classList.add('apps-promo-visible'); |
| 74 } else { |
| 75 document.documentElement.classList.remove('apps-promo-visible'); |
| 76 } |
| 77 |
| 78 // Only show the web store entry if there are apps installed, since the promo |
| 79 // is sufficient otherwise. |
| 80 if (data.apps.length > 0) { |
| 81 webStoreEntry = apps.createWebStoreElement(); |
| 82 webStoreEntry.querySelector('a').ping = appsPromoPing; |
| 83 appsSectionContent.appendChild(webStoreEntry); |
| 84 if (apps.detachWebstoreEntry) { |
| 85 webStoreEntry.classList.add('loner'); |
| 86 } else { |
| 87 webStoreEntry.classList.remove('loner'); |
| 88 apps.data.push('web-store-entry'); |
| 89 } |
| 90 } |
| 68 | 91 |
| 69 data.apps.slice(0, MAX_MINIVIEW_ITEMS).forEach(function(app) { | 92 data.apps.slice(0, MAX_MINIVIEW_ITEMS).forEach(function(app) { |
| 70 appsMiniview.appendChild(apps.createMiniviewElement(app)); | 93 appsMiniview.appendChild(apps.createMiniviewElement(app)); |
| 71 addClosedMenuEntryWithLink(apps.menu, apps.createClosedMenuElement(app)); | 94 addClosedMenuEntryWithLink(apps.menu, apps.createClosedMenuElement(app)); |
| 72 }); | 95 }); |
| 73 if (data.apps.length < MAX_MINIVIEW_ITEMS) { | 96 if (data.apps.length < MAX_MINIVIEW_ITEMS) { |
| 74 webStoreMiniEntry = apps.createWebStoreMiniElement(); | 97 webStoreMiniEntry = apps.createWebStoreMiniElement(); |
| 75 webStoreEntry.querySelector('a').setAttribute('ping', appsPromoPing); | 98 webStoreMiniEntry.querySelector('a').ping = appsPromoPing; |
| 76 appsMiniview.appendChild(webStoreMiniEntry); | 99 appsMiniview.appendChild(webStoreMiniEntry); |
| 77 addClosedMenuEntryWithLink(apps.menu, | 100 addClosedMenuEntryWithLink(apps.menu, |
| 78 apps.createWebStoreClosedMenuElement()); | 101 apps.createWebStoreClosedMenuElement()); |
| 79 } | 102 } |
| 80 | 103 |
| 81 if (!data.showLauncher) | 104 if (!data.showLauncher) |
| 82 hideSection(Section.APPS); | 105 hideSection(Section.APPS); |
| 83 else | 106 else |
| 84 appsSection.classList.remove('disabled'); | 107 appsSection.classList.remove('disabled'); |
| 85 | 108 |
| 86 addClosedMenuFooter(apps.menu, 'apps', MENU_APPS, Section.APPS); | 109 addClosedMenuFooter(apps.menu, 'apps', MENU_APPS, Section.APPS); |
| 87 | 110 |
| 88 apps.loaded = true; | 111 apps.loaded = true; |
| 89 if (apps.showPromo) | |
| 90 document.documentElement.classList.add('apps-promo-visible'); | |
| 91 else | |
| 92 document.documentElement.classList.remove('apps-promo-visible'); | |
| 93 | 112 |
| 94 var appsPromoLink = $('apps-promo-link'); | |
| 95 if (appsPromoLink) | 113 if (appsPromoLink) |
| 96 appsPromoLink.setAttribute('ping', appsPromoPing); | 114 appsPromoLink.ping = appsPromoPing; |
| 97 maybeDoneLoading(); | 115 maybeDoneLoading(); |
| 98 | 116 |
| 99 // Disable the animations when the app launcher is being (re)initailized. | 117 // Disable the animations when the app launcher is being (re)initailized. |
| 100 apps.layout({disableAnimations:true}); | 118 apps.layout({disableAnimations:true}); |
| 101 | 119 |
| 102 if (apps.detachWebstoreEntry) | |
| 103 webStoreEntry.classList.add('loner'); | |
| 104 else | |
| 105 webStoreEntry.classList.remove('loner'); | |
| 106 | |
| 107 if (isDoneLoading()) { | 120 if (isDoneLoading()) { |
| 108 updateMiniviewClipping(appsMiniview); | 121 updateMiniviewClipping(appsMiniview); |
| 109 layoutSections(); | 122 layoutSections(); |
| 110 } | 123 } |
| 111 } | 124 } |
| 112 | 125 |
| 113 function markNewApps(data) { | 126 function markNewApps(data) { |
| 114 var oldData = apps.data; | 127 var oldData = apps.data; |
| 115 data.forEach(function(app) { | 128 data.forEach(function(app) { |
| 116 if (hashParams['app-id'] == app['id']) { | 129 if (hashParams['app-id'] == app['id']) { |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 168 if (opt_mouseEvent) { | 181 if (opt_mouseEvent) { |
| 169 // Launch came from a click - add details of the click | 182 // Launch came from a click - add details of the click |
| 170 // Otherwise it came from a 'command' event from elsewhere in the UI. | 183 // Otherwise it came from a 'command' event from elsewhere in the UI. |
| 171 args.push(opt_mouseEvent.altKey, opt_mouseEvent.ctrlKey, | 184 args.push(opt_mouseEvent.altKey, opt_mouseEvent.ctrlKey, |
| 172 opt_mouseEvent.metaKey, opt_mouseEvent.shiftKey, | 185 opt_mouseEvent.metaKey, opt_mouseEvent.shiftKey, |
| 173 opt_mouseEvent.button); | 186 opt_mouseEvent.button); |
| 174 } | 187 } |
| 175 chrome.send('launchApp', args); | 188 chrome.send('launchApp', args); |
| 176 } | 189 } |
| 177 | 190 |
| 191 function isAppSectionMaximized() { |
| 192 return getAppLaunchType() == APP_LAUNCH.NTP_APPS_MAXIMIZED && |
| 193 !$('apps').classList.contains('disabled'); |
| 194 } |
| 195 |
| 178 function isAppsMenu(node) { | 196 function isAppsMenu(node) { |
| 179 return node.id == 'apps-menu'; | 197 return node.id == 'apps-menu'; |
| 180 } | 198 } |
| 181 | 199 |
| 182 function getAppLaunchType() { | 200 function getAppLaunchType() { |
| 183 // We determine if the apps section is maximized, collapsed or in menu mode | 201 // We determine if the apps section is maximized, collapsed or in menu mode |
| 184 // based on the class of the apps section. | 202 // based on the class of the apps section. |
| 185 if ($('apps').classList.contains('menu')) | 203 if ($('apps').classList.contains('menu')) |
| 186 return APP_LAUNCH.NTP_APPS_MENU; | 204 return APP_LAUNCH.NTP_APPS_MENU; |
| 187 else if ($('apps').classList.contains('collapsed')) | 205 else if ($('apps').classList.contains('collapsed')) |
| (...skipping 21 matching lines...) Expand all Loading... |
| 209 }; | 227 }; |
| 210 | 228 |
| 211 // Keep in sync with LaunchContainer in extension_constants.h | 229 // Keep in sync with LaunchContainer in extension_constants.h |
| 212 var LaunchContainer = { | 230 var LaunchContainer = { |
| 213 LAUNCH_WINDOW: 0, | 231 LAUNCH_WINDOW: 0, |
| 214 LAUNCH_PANEL: 1, | 232 LAUNCH_PANEL: 1, |
| 215 LAUNCH_TAB: 2 | 233 LAUNCH_TAB: 2 |
| 216 }; | 234 }; |
| 217 | 235 |
| 218 var currentApp; | 236 var currentApp; |
| 237 var promoHasBeenSeen = false; |
| 219 | 238 |
| 220 function addContextMenu(el, app) { | 239 function addContextMenu(el, app) { |
| 221 el.addEventListener('contextmenu', cr.ui.contextMenuHandler); | 240 el.addEventListener('contextmenu', cr.ui.contextMenuHandler); |
| 222 el.addEventListener('keydown', cr.ui.contextMenuHandler); | 241 el.addEventListener('keydown', cr.ui.contextMenuHandler); |
| 223 el.addEventListener('keyup', cr.ui.contextMenuHandler); | 242 el.addEventListener('keyup', cr.ui.contextMenuHandler); |
| 224 | 243 |
| 225 Object.defineProperty(el, 'contextMenu', { | 244 Object.defineProperty(el, 'contextMenu', { |
| 226 get: function() { | 245 get: function() { |
| 227 currentApp = app; | 246 currentApp = app; |
| 228 | 247 |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 340 | 359 |
| 341 visible_: true, | 360 visible_: true, |
| 342 get visible() { | 361 get visible() { |
| 343 return this.visible_; | 362 return this.visible_; |
| 344 }, | 363 }, |
| 345 set visible(visible) { | 364 set visible(visible) { |
| 346 this.visible_ = visible; | 365 this.visible_ = visible; |
| 347 this.invalidate_(); | 366 this.invalidate_(); |
| 348 }, | 367 }, |
| 349 | 368 |
| 369 maybePingPromoSeen_: function() { |
| 370 if (promoHasBeenSeen || !this.showPromo || !isAppSectionMaximized()) |
| 371 return; |
| 372 |
| 373 promoHasBeenSeen = true; |
| 374 chrome.send('promoSeen', []); |
| 375 }, |
| 376 |
| 350 // DragAndDropDelegate | 377 // DragAndDropDelegate |
| 351 | 378 |
| 352 dragContainer: $('apps-content'), | 379 dragContainer: $('apps-content'), |
| 353 transitionsDuration: 200, | 380 transitionsDuration: 200, |
| 354 | 381 |
| 355 get dragItem() { return this.dragItem_; }, | 382 get dragItem() { return this.dragItem_; }, |
| 356 set dragItem(dragItem) { | 383 set dragItem(dragItem) { |
| 357 if (this.dragItem_ != dragItem) { | 384 if (this.dragItem_ != dragItem) { |
| 358 this.dragItem_ = dragItem; | 385 this.dragItem_ = dragItem; |
| 359 this.invalidate_(); | 386 this.invalidate_(); |
| (...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 557 // We need to re-enable animations asynchronously, so that the | 584 // We need to re-enable animations asynchronously, so that the |
| 558 // animations are still disabled for this layout update. | 585 // animations are still disabled for this layout update. |
| 559 setTimeout(function() { | 586 setTimeout(function() { |
| 560 container.setAttribute('launcher-animations', true); | 587 container.setAttribute('launcher-animations', true); |
| 561 }, 0); | 588 }, 0); |
| 562 } | 589 } |
| 563 } | 590 } |
| 564 }, | 591 }, |
| 565 | 592 |
| 566 layoutImpl_: function() { | 593 layoutImpl_: function() { |
| 567 var apps = this.data; | 594 var apps = this.data || []; |
| 568 var rects = this.getLayoutRects_(apps.length); | 595 var rects = this.getLayoutRects_(apps.length); |
| 569 var appsContent = this.dragContainer; | 596 var appsContent = this.dragContainer; |
| 570 | 597 |
| 598 // Ping the PROMO_SEEN histogram only when the promo is maximized, and |
| 599 // maximum once per NTP load. |
| 600 this.maybePingPromoSeen_(); |
| 601 |
| 571 if (!this.visible) | 602 if (!this.visible) |
| 572 return; | 603 return; |
| 573 | 604 |
| 574 for (var i = 0; i < apps.length; i++) { | 605 for (var i = 0; i < apps.length; i++) { |
| 575 var app = appsContent.querySelector('[app-id='+apps[i]+']').parentNode; | 606 var app = appsContent.querySelector('[app-id='+apps[i]+']').parentNode; |
| 576 | 607 |
| 577 // If the node is being dragged, don't try to place it in the grid. | 608 // If the node is being dragged, don't try to place it in the grid. |
| 578 if (app == this.dragItem) | 609 if (app == this.dragItem) |
| 579 continue; | 610 continue; |
| 580 | 611 |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 644 showImages: function() { | 675 showImages: function() { |
| 645 $('apps-content').classList.add('visible'); | 676 $('apps-content').classList.add('visible'); |
| 646 clearTimeout(this.imageTimer); | 677 clearTimeout(this.imageTimer); |
| 647 }, | 678 }, |
| 648 | 679 |
| 649 createElement: function(app) { | 680 createElement: function(app) { |
| 650 var div = createElement(app); | 681 var div = createElement(app); |
| 651 var a = div.firstChild; | 682 var a = div.firstChild; |
| 652 | 683 |
| 653 a.onclick = handleClick; | 684 a.onclick = handleClick; |
| 654 a.setAttribute('ping', | 685 a.ping = getAppPingUrl( |
| 655 getAppPingUrl('PING_BY_ID', this.showPromo, 'NTP_APPS_MAXIMIZED')); | 686 'PING_BY_ID', this.showPromo, 'NTP_APPS_MAXIMIZED'); |
| 656 a.style.backgroundImage = url(app['icon_big']); | 687 a.style.backgroundImage = url(app['icon_big']); |
| 657 if (app.isNew) { | 688 if (app.isNew) { |
| 658 div.setAttribute('new', 'new'); | 689 div.setAttribute('new', 'new'); |
| 659 // Delay changing the attribute a bit to let the page settle down a bit. | 690 // Delay changing the attribute a bit to let the page settle down a bit. |
| 660 setTimeout(function() { | 691 setTimeout(function() { |
| 661 // Make sure the new icon is scrolled into view. | 692 // Make sure the new icon is scrolled into view. |
| 662 document.body.scrollTop = document.body.scrollHeight; | 693 document.body.scrollTop = document.body.scrollHeight; |
| 663 | 694 |
| 664 // This will trigger the 'bounce' animation defined in apps.css. | 695 // This will trigger the 'bounce' animation defined in apps.css. |
| 665 div.setAttribute('new', 'installed'); | 696 div.setAttribute('new', 'installed'); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 684 }, | 715 }, |
| 685 | 716 |
| 686 createMiniviewElement: function(app) { | 717 createMiniviewElement: function(app) { |
| 687 var span = document.createElement('span'); | 718 var span = document.createElement('span'); |
| 688 var a = span.appendChild(document.createElement('a')); | 719 var a = span.appendChild(document.createElement('a')); |
| 689 | 720 |
| 690 a.setAttribute('app-id', app['id']); | 721 a.setAttribute('app-id', app['id']); |
| 691 a.textContent = app['name']; | 722 a.textContent = app['name']; |
| 692 a.href = app['launch_url']; | 723 a.href = app['launch_url']; |
| 693 a.onclick = handleClick; | 724 a.onclick = handleClick; |
| 694 a.setAttribute('ping', | 725 a.ping = getAppPingUrl( |
| 695 getAppPingUrl('PING_BY_ID', this.showPromo, 'NTP_APPS_COLLAPSED')); | 726 'PING_BY_ID', this.showPromo, 'NTP_APPS_COLLAPSED'); |
| 696 a.style.backgroundImage = url(app['icon_small']); | 727 a.style.backgroundImage = url(app['icon_small']); |
| 697 a.className = 'item'; | 728 a.className = 'item'; |
| 698 span.appendChild(a); | 729 span.appendChild(a); |
| 699 | 730 |
| 700 addContextMenu(span, app); | 731 addContextMenu(span, app); |
| 701 | 732 |
| 702 return span; | 733 return span; |
| 703 }, | 734 }, |
| 704 | 735 |
| 705 createClosedMenuElement: function(app) { | 736 createClosedMenuElement: function(app) { |
| 706 var a = document.createElement('a'); | 737 var a = document.createElement('a'); |
| 707 a.setAttribute('app-id', app['id']); | 738 a.setAttribute('app-id', app['id']); |
| 708 a.textContent = app['name']; | 739 a.textContent = app['name']; |
| 709 a.href = app['launch_url']; | 740 a.href = app['launch_url']; |
| 710 a.onclick = handleClick; | 741 a.onclick = handleClick; |
| 711 a.setAttribute('ping', | 742 a.ping = getAppPingUrl( |
| 712 getAppPingUrl('PING_BY_ID', this.showPromo, 'NTP_APPS_MENU')); | 743 'PING_BY_ID', this.showPromo, 'NTP_APPS_MENU'); |
| 713 a.style.backgroundImage = url(app['icon_small']); | 744 a.style.backgroundImage = url(app['icon_small']); |
| 714 a.className = 'item'; | 745 a.className = 'item'; |
| 715 | 746 |
| 716 addContextMenu(a, app); | 747 addContextMenu(a, app); |
| 717 | 748 |
| 718 return a; | 749 return a; |
| 719 }, | 750 }, |
| 720 | 751 |
| 721 createWebStoreElement: function() { | 752 createWebStoreElement: function() { |
| 722 var elm = createElement({ | 753 var elm = createElement({ |
| (...skipping 17 matching lines...) Expand all Loading... |
| 740 a.href = localStrings.getString('web_store_url'); | 771 a.href = localStrings.getString('web_store_url'); |
| 741 a.style.backgroundImage = url('chrome://theme/IDR_PRODUCT_LOGO_16'); | 772 a.style.backgroundImage = url('chrome://theme/IDR_PRODUCT_LOGO_16'); |
| 742 a.className = 'item'; | 773 a.className = 'item'; |
| 743 return a; | 774 return a; |
| 744 } | 775 } |
| 745 }; | 776 }; |
| 746 })(); | 777 })(); |
| 747 | 778 |
| 748 // Enable drag and drop reordering of the app launcher. | 779 // Enable drag and drop reordering of the app launcher. |
| 749 var appDragAndDrop = new DragAndDropController(apps); | 780 var appDragAndDrop = new DragAndDropController(apps); |
| OLD | NEW |