172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvar MAX_APPS_PER_ROW = [];
6731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickMAX_APPS_PER_ROW[LayoutMode.SMALL] = 4;
7731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickMAX_APPS_PER_ROW[LayoutMode.NORMAL] = 6;
8731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochfunction getAppsCallback(data) {
103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  logEvent('received apps');
11ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch
12ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch  // In the case of prefchange-triggered updates, we don't receive this flag.
13ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch  // Just leave it set as it was before in that case.
14ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch  if ('showPromo' in data)
15ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch    apps.showPromo = data.showPromo;
16ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch
173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  var appsSection = $('apps');
18731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  var appsSectionContent = $('apps-content');
193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  var appsMiniview = appsSection.getElementsByClassName('miniview')[0];
20731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  var appsPromo = $('apps-promo');
21ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  var appsPromoLink = $('apps-promo-link');
22dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  var appsPromoPing = APP_LAUNCH_URL.PING_WEBSTORE + '+' + apps.showPromo;
23ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch  var webStoreEntry, webStoreMiniEntry;
24731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
25201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  // Hide menu options that are not supported on the OS or windowing system.
26201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
27201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  // The "Launch as Window" menu option.
28dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  $('apps-launch-type-window-menu-item').hidden = data.disableAppWindowLaunch;
29201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
30201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  // The "Create App Shortcut" menu option.
31dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  $('apps-create-shortcut-command-menu-item').hidden =
32dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      $('apps-create-shortcut-command-separator').hidden =
33dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen          data.disableCreateAppShortcut;
34201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
35ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Hide the context menu, if there is any open.
36ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  cr.ui.contextMenuHandler.hideMenu();
37ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  appsMiniview.textContent = '';
39731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  appsSectionContent.textContent = '';
40731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
41731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  data.apps.sort(function(a,b) {
424a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return a.app_launch_index - b.app_launch_index;
43731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  });
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
4572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Determines if the web store link should be detached and place in the
4672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // top right of the screen.
4772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  apps.detachWebstoreEntry =
4872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      !apps.showPromo && data.apps.length >= MAX_APPS_PER_ROW[layoutMode];
4972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
50dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  markNewApps(data.apps);
5172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  apps.data = data.apps;
5272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
53731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  clearClosedMenu(apps.menu);
54dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
55dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // We wait for the app icons to load before displaying them, but never wait
56dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // longer than 200ms.
57dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  apps.loadedImages = 0;
58dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  apps.imageTimer = setTimeout(apps.showImages.bind(apps), 200);
59dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
60ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch  data.apps.forEach(function(app) {
61ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch    appsSectionContent.appendChild(apps.createElement(app));
62ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch  });
633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
64ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (data.showPromo) {
65ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // Add the promo content...
66ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    $('apps-promo-heading').textContent = data.promoHeader;
67ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    appsPromoLink.href = data.promoLink;
68ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    appsPromoLink.textContent = data.promoButton;
69ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    appsPromoLink.ping = appsPromoPing;
70ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    $('apps-promo-hide').textContent = data.promoExpire;
71ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
72ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // ... then display the promo.
73ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    document.documentElement.classList.add('apps-promo-visible');
74ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  } else {
75ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    document.documentElement.classList.remove('apps-promo-visible');
76ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
77ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
78ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Only show the web store entry if there are apps installed, since the promo
79ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // is sufficient otherwise.
80ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (data.apps.length > 0) {
81ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    webStoreEntry = apps.createWebStoreElement();
82ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    webStoreEntry.querySelector('a').ping = appsPromoPing;
83ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    appsSectionContent.appendChild(webStoreEntry);
84ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (apps.detachWebstoreEntry) {
85ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      webStoreEntry.classList.add('loner');
86ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    } else {
87ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      webStoreEntry.classList.remove('loner');
88ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      apps.data.push('web-store-entry');
89ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
90ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
92ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch  data.apps.slice(0, MAX_MINIVIEW_ITEMS).forEach(function(app) {
93ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch    appsMiniview.appendChild(apps.createMiniviewElement(app));
94ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch    addClosedMenuEntryWithLink(apps.menu, apps.createClosedMenuElement(app));
95ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch  });
96ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch  if (data.apps.length < MAX_MINIVIEW_ITEMS) {
97ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch    webStoreMiniEntry = apps.createWebStoreMiniElement();
98ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    webStoreMiniEntry.querySelector('a').ping = appsPromoPing;
99ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch    appsMiniview.appendChild(webStoreMiniEntry);
100ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch    addClosedMenuEntryWithLink(apps.menu,
101ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch                               apps.createWebStoreClosedMenuElement());
102ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch  }
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
10472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (!data.showLauncher)
105dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    hideSection(Section.APPS);
10672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  else
107ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch    appsSection.classList.remove('disabled');
10872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
10972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  addClosedMenuFooter(apps.menu, 'apps', MENU_APPS, Section.APPS);
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  apps.loaded = true;
11272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
11372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (appsPromoLink)
114ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    appsPromoLink.ping = appsPromoPing;
1153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  maybeDoneLoading();
1163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
11772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Disable the animations when the app launcher is being (re)initailized.
11872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  apps.layout({disableAnimations:true});
119731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
12072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (isDoneLoading()) {
1213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    updateMiniviewClipping(appsMiniview);
1223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    layoutSections();
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
126dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenfunction markNewApps(data) {
127dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  var oldData = apps.data;
128dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  data.forEach(function(app) {
129dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    if (hashParams['app-id'] == app['id']) {
130dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      delete hashParams['app-id'];
131dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      app.isNew = true;
132dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    } else if (oldData &&
133dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        !oldData.some(function(id) { return id == app.id; })) {
134dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      app.isNew = true;
135dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    } else {
136dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      app.isNew = false;
137dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    }
138dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  });
139dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}
140dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
1413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickfunction appsPrefChangeCallback(data) {
1423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Currently the only pref that is watched is the launch type.
1433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  data.apps.forEach(function(app) {
1443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    var appLink = document.querySelector('.app a[app-id=' + app['id'] + ']');
1453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if (appLink)
1463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      appLink.setAttribute('launch-type', app['launch_type']);
1473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  });
1483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
1493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
150dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// Launches the specified app using the APP_LAUNCH_NTP_APP_RE_ENABLE histogram.
151dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// This should only be invoked from the AppLauncherHandler.
152dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenfunction launchAppAfterEnable(appId) {
153dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  chrome.send('launchApp', [appId, APP_LAUNCH.NTP_APP_RE_ENABLE]);
154dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}
155dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
1563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvar apps = (function() {
1573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  function createElement(app) {
1593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    var div = document.createElement('div');
1603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    div.className = 'app';
1613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    var a = div.appendChild(document.createElement('a'));
1633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    a.setAttribute('app-id', app['id']);
1643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    a.setAttribute('launch-type', app['launch_type']);
16572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    a.draggable = false;
1663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    a.xtitle = a.textContent = app['name'];
1673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    a.href = app['launch_url'];
1683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return div;
1703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
17272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  /**
17372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen   * Launches an application.
17472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen   * @param {string} appId Application to launch.
17572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen   * @param {MouseEvent} opt_mouseEvent Mouse event from the click that
17672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen   *     triggered the launch, used to detect modifier keys that change
17772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen   *     the tab's disposition.
17872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen   */
17972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  function launchApp(appId, opt_mouseEvent) {
180dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    var args = [appId, getAppLaunchType()];
18172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (opt_mouseEvent) {
182dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      // Launch came from a click - add details of the click
183dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      // Otherwise it came from a 'command' event from elsewhere in the UI.
184dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      args.push(opt_mouseEvent.altKey, opt_mouseEvent.ctrlKey,
185dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                opt_mouseEvent.metaKey, opt_mouseEvent.shiftKey,
186dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                opt_mouseEvent.button);
18772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    }
188dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    chrome.send('launchApp', args);
189dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
190dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
191ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  function isAppSectionMaximized() {
192ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return getAppLaunchType() == APP_LAUNCH.NTP_APPS_MAXIMIZED &&
193ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      !$('apps').classList.contains('disabled');
194ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
195ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
196dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  function isAppsMenu(node) {
197dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    return node.id == 'apps-menu';
198dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
199dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
200dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  function getAppLaunchType() {
201dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    // We determine if the apps section is maximized, collapsed or in menu mode
202dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    // based on the class of the apps section.
203dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    if ($('apps').classList.contains('menu'))
204dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      return APP_LAUNCH.NTP_APPS_MENU;
205dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    else if ($('apps').classList.contains('collapsed'))
206dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      return APP_LAUNCH.NTP_APPS_COLLAPSED;
207dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    else
208dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      return APP_LAUNCH.NTP_APPS_MAXIMIZED;
2093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
2103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  /**
2123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick   * @this {!HTMLAnchorElement}
2133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick   */
2143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  function handleClick(e) {
2153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    var appId = e.currentTarget.getAttribute('app-id');
21672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (!appDragAndDrop.isDragging())
21772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      launchApp(appId, e);
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
2193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
2213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Keep in sync with LaunchType in extension_prefs.h
2223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  var LaunchType = {
2233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    LAUNCH_PINNED: 0,
2243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    LAUNCH_REGULAR: 1,
225201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    LAUNCH_FULLSCREEN: 2,
226201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    LAUNCH_WINDOW: 3
2273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  };
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
229201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  // Keep in sync with LaunchContainer in extension_constants.h
2303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  var LaunchContainer = {
2313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    LAUNCH_WINDOW: 0,
2323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    LAUNCH_PANEL: 1,
2333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    LAUNCH_TAB: 2
2343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  };
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
2363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  var currentApp;
237ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  var promoHasBeenSeen = false;
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
2393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  function addContextMenu(el, app) {
2403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    el.addEventListener('contextmenu', cr.ui.contextMenuHandler);
2413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    el.addEventListener('keydown', cr.ui.contextMenuHandler);
2423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    el.addEventListener('keyup', cr.ui.contextMenuHandler);
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
2443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    Object.defineProperty(el, 'contextMenu', {
2453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      get: function() {
2463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        currentApp = app;
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
2483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        $('apps-launch-command').label = app['name'];
2493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        $('apps-options-command').canExecuteChange();
2503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
251201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        var launchTypeEl;
252201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        if (el.getAttribute('app-id') === app['id']) {
253201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch          launchTypeEl = el;
254201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        } else {
255201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch          appLinkSel = 'a[app-id=' + app['id'] + ']';
256201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch          launchTypeEl = el.querySelector(appLinkSel);
257201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        }
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
259201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        var launchType = launchTypeEl.getAttribute('launch-type');
2603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        var launchContainer = app['launch_container'];
2613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        var isPanel = launchContainer == LaunchContainer.LAUNCH_PANEL;
2623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        // Update the commands related to the launch type.
2643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        var launchTypeIds = ['apps-launch-type-pinned',
2653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                             'apps-launch-type-regular',
266201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch                             'apps-launch-type-fullscreen',
267201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch                             'apps-launch-type-window'];
2683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        launchTypeIds.forEach(function(id) {
2693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick          var command = $(id);
2703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick          command.disabled = isPanel;
2713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick          command.checked = !isPanel &&
2723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick              launchType == command.getAttribute('launch-type');
2733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        });
2743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        return $('app-context-menu');
2763345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      }
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    });
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
2793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  document.addEventListener('command', function(e) {
2813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if (!currentApp)
2823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      return;
2833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    var commandId = e.command.id;
2853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    switch (commandId) {
2863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      case 'apps-options-command':
2873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        window.location = currentApp['options_url'];
2883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        break;
2893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      case 'apps-launch-command':
2903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        launchApp(currentApp['id']);
2913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        break;
2923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      case 'apps-uninstall-command':
2933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        chrome.send('uninstallApp', [currentApp['id']]);
2943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        break;
295201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      case 'apps-create-shortcut-command':
296201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        chrome.send('createAppShortcut', [currentApp['id']]);
297201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        break;
2983345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      case 'apps-launch-type-pinned':
2993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      case 'apps-launch-type-regular':
3003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      case 'apps-launch-type-fullscreen':
301201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      case 'apps-launch-type-window':
3023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        chrome.send('setLaunchType',
30372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen            [currentApp['id'],
30472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen             Number(e.command.getAttribute('launch-type'))]);
3053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        break;
3063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    }
3073345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  });
3083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
3093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  document.addEventListener('canExecute', function(e) {
3103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    switch (e.command.id) {
3113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      case 'apps-options-command':
3123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        e.canExecute = currentApp && currentApp['options_url'];
3133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        break;
3143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      case 'apps-launch-command':
3153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        e.canExecute = true;
3163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        break;
317ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      case 'apps-uninstall-command':
318ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        e.canExecute = !currentApp['can_uninstall'];
319ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        break;
3203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    }
3213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  });
3223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
32372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Moves the element at position |from| in array |arr| to position |to|.
32472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  function arrayMove(arr, from, to) {
32572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    var element = arr.splice(from, 1);
32672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    arr.splice(to, 0, element[0]);
32772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
32872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
32972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // The autoscroll rate during drag and drop, in px per second.
33072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  var APP_AUTOSCROLL_RATE = 400;
33172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
3323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return {
3333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    loaded: false,
3343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
335731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    menu: $('apps-menu'),
336731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
3374a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    showPromo: false,
3384a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
33972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    detachWebstoreEntry: false,
34072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
34172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    scrollMouseXY_: null,
34272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
34372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    scrollListener_: null,
34472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
34572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // The list of app ids, in order, of each app in the launcher.
34672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    data_: null,
34772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    get data() { return this.data_; },
34872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    set data(data) {
34972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      this.data_ = data.map(function(app) {
35072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        return app.id;
35172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      });
35272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      this.invalidate_();
35372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
35472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
35572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    dirty_: true,
35672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    invalidate_: function() {
35772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      this.dirty_ = true;
35872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
35972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
36072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    visible_: true,
36172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    get visible() {
36272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return this.visible_;
36372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
36472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    set visible(visible) {
36572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      this.visible_ = visible;
36672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      this.invalidate_();
36772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
36872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
369ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    maybePingPromoSeen_: function() {
370ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (promoHasBeenSeen || !this.showPromo || !isAppSectionMaximized())
371ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        return;
372ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
373ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      promoHasBeenSeen = true;
374ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      chrome.send('promoSeen', []);
375ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    },
376ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
37772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // DragAndDropDelegate
37872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
37972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    dragContainer: $('apps-content'),
38072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    transitionsDuration: 200,
38172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
38272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    get dragItem() { return this.dragItem_; },
38372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    set dragItem(dragItem) {
38472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if (this.dragItem_ != dragItem) {
38572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        this.dragItem_ = dragItem;
38672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        this.invalidate_();
38772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      }
38872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
38972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
39072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // The dimensions of each item in the app launcher.
39172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    dimensions_: null,
39272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    get dimensions() {
39372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if (this.dimensions_)
39472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        return this.dimensions_;
39572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
39672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var width = 124;
39772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var height = 136;
39872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
39972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var marginWidth = 6;
40072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var marginHeight = 10;
40172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
40272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var borderWidth = 0;
40372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var borderHeight = 0;
40472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
40572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      this.dimensions_ = {
40672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        width: width + marginWidth + borderWidth,
40772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        height: height + marginHeight + borderHeight
40872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      };
40972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
41072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return this.dimensions_;
41172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
41272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
41372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // Gets the item under the mouse event |e|. Returns null if there is no
41472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // item or if the item is not draggable.
41572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    getItem: function(e) {
41672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var item = findAncestorByClass(e.target, 'app');
41772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
41872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // You can't drag the web store launcher.
41972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if (item && item.classList.contains('web-store-entry'))
42072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        return null;
42172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
42272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return item;
42372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
42472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
42572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // Returns true if |coordinates| point to a valid drop location. The
42672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // coordinates are relative to the drag container and the object should
42772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // have the 'x' and 'y' properties set.
42872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    canDropOn: function(coordinates) {
42972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var cols = MAX_APPS_PER_ROW[layoutMode];
43072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var rows = Math.ceil(this.data.length / cols);
43172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
43272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var bottom = rows * this.dimensions.height;
43372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var right = cols * this.dimensions.width;
43472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
43572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if (coordinates.x >= right || coordinates.x < 0 ||
43672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          coordinates.y >= bottom || coordinates.y < 0)
43772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        return false;
43872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
43972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var position = this.getIndexAt_(coordinates);
44072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var appCount = this.data.length;
44172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
44272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if (!this.detachWebstoreEntry)
44372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        appCount--;
44472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
44572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return position >= 0 && position < appCount;
44672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
44772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
44872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    setDragPlaceholder: function(coordinates) {
44972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var position = this.getIndexAt_(coordinates);
45072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var appId = this.dragItem.querySelector('a').getAttribute('app-id');
45172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var current = this.data.indexOf(appId);
45272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
45372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if (current == position || current < 0)
45472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        return;
45572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
45672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      arrayMove(this.data, current, position);
45772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      this.invalidate_();
45872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      this.layout();
45972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
46072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
46172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    getIndexAt_: function(coordinates) {
46272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var w = this.dimensions.width;
46372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var h = this.dimensions.height;
46472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
46572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var appsPerRow = MAX_APPS_PER_ROW[layoutMode];
46672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
46772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var row = Math.floor(coordinates.y / h);
46872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var col = Math.floor(coordinates.x / w);
46972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var index = appsPerRow * row + col;
47072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
47172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var appCount = this.data.length;
47272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var rows = Math.ceil(appCount / appsPerRow);
47372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
47472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // Rather than making the free space on the last row invalid, we
47572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // map it to the last valid position.
47672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if (index >= appCount && index < appsPerRow * rows)
47772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        return appCount-1;
47872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
47972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return index;
48072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
48172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
48272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    scrollPage: function(xy) {
48372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var rect = this.dragContainer.getBoundingClientRect();
48472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
48572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // Here, we calculate the visible boundaries of the app launcher, which
48672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // are then used to determine when we should auto-scroll.
48772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var top = $('apps').getBoundingClientRect().bottom;
48872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var bottomFudge = 15; // Fudge factor due to a gradient mask.
48972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var bottom = top + maxiviewVisibleHeight - bottomFudge;
49072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var left = rect.left + window.scrollX;
49172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var right = Math.min(window.innerWidth, rect.left + rect.width);
49272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
49372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var dy = Math.min(0, xy.y - top) + Math.max(0, xy.y - bottom);
49472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var dx = Math.min(0, xy.x - left) + Math.max(0, xy.x - right);
49572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
49672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if (dx == 0 && dy == 0) {
49772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        this.stopScroll_();
49872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        return;
49972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      }
50072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
50172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // If we scroll the page directly from this method, it may be choppy and
50272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // inconsistent. Instead, we loop using animation frames, and scroll at a
50372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // speed that's independent of how many times this method is called.
50472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      this.scrollMouseXY_ = {dx: dx, dy: dy};
50572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
50672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if (!this.scrollListener_) {
50772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        this.scrollListener_ = this.scrollImpl_.bind(this);
50872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        this.scrollStep_();
50972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      }
51072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
51172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
51272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    scrollStep_: function() {
51372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      this.scrollStart_ = Date.now();
51472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      window.webkitRequestAnimationFrame(this.scrollListener_);
51572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
51672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
51772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    scrollImpl_: function(time) {
51872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if (!appDragAndDrop.isDragging()) {
51972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        this.stopScroll_();
52072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        return;
52172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      }
52272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
52372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if (!this.scrollMouseXY_)
52472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        return;
52572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
52672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var step = time - this.scrollStart_;
52772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
52872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      window.scrollBy(
52972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          this.calcScroll_(this.scrollMouseXY_.dx, step),
53072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          this.calcScroll_(this.scrollMouseXY_.dy, step));
53172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
53272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      this.scrollStep_();
53372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
53472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
53572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    calcScroll_: function(delta, step) {
53672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if (delta == 0)
53772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        return 0;
53872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
53972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // Increase the multiplier for every 50px the mouse is beyond the edge.
54072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var sign = delta > 0 ? 1 : -1;
54172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var scalar = APP_AUTOSCROLL_RATE * step / 1000;
54272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var multiplier = Math.floor(Math.abs(delta) / 50) + 1;
54372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
54472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return sign * scalar * multiplier;
54572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
54672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
54772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    stopScroll_: function() {
54872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      this.scrollListener_ = null;
54972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      this.scrollMouseXY_ = null;
55072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
55172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
552dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    saveDrag: function(draggedItem) {
55372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      this.invalidate_();
55472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      this.layout();
55572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
556dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      var draggedAppId = draggedItem.querySelector('a').getAttribute('app-id');
55772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var appIds = this.data.filter(function(id) {
55872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        return id != 'web-store-entry';
55972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      });
56072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
56172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // Wait until the transitions are complete before notifying the browser.
56272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // Otherwise, the apps will be re-rendered while still transitioning.
56372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      setTimeout(function() {
564dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        chrome.send('reorderApps', [draggedAppId, appIds]);
56572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      }, this.transitionsDuration + 10);
56672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
56772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
56872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    layout: function(options) {
56972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      options = options || {};
57072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if (!this.dirty_ && options.force != true)
57172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        return;
57272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
57372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      try {
57472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        var container = this.dragContainer;
57572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        if (options.disableAnimations)
57672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          container.setAttribute('launcher-animations', false);
57772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        var d0 = Date.now();
57872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        this.layoutImpl_();
57972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        this.dirty_ = false;
58072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        logEvent('apps.layout: ' + (Date.now() - d0));
58172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
58272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      } finally {
58372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        if (options.disableAnimations) {
58472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          // We need to re-enable animations asynchronously, so that the
58572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          // animations are still disabled for this layout update.
58672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          setTimeout(function() {
58772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen            container.setAttribute('launcher-animations', true);
58872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          }, 0);
58972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        }
59072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      }
59172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
59272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
59372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    layoutImpl_: function() {
594ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      var apps = this.data || [];
59572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var rects = this.getLayoutRects_(apps.length);
59672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var appsContent = this.dragContainer;
59772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
598ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // Ping the PROMO_SEEN histogram only when the promo is maximized, and
599ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // maximum once per NTP load.
600ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.maybePingPromoSeen_();
601ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
60272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if (!this.visible)
60372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        return;
60472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
60572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      for (var i = 0; i < apps.length; i++) {
60672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        var app = appsContent.querySelector('[app-id='+apps[i]+']').parentNode;
60772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
60872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        // If the node is being dragged, don't try to place it in the grid.
60972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        if (app == this.dragItem)
61072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          continue;
61172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
61272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        app.style.left = rects[i].left + 'px';
61372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        app.style.top = rects[i].top + 'px';
61472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      }
61572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
61672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // We need to set the container's height manually because the apps use
61772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // absolute positioning.
61872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var rows = Math.ceil(apps.length / MAX_APPS_PER_ROW[layoutMode]);
61972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      appsContent.style.height = (rows * this.dimensions.height) + 'px';
62072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
62172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
62272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    getLayoutRects_: function(appCount) {
62372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var availableWidth = this.dragContainer.offsetWidth;
62472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var rtl = isRtl();
62572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var rects = [];
62672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var w = this.dimensions.width;
62772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var h = this.dimensions.height;
62872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      var appsPerRow = MAX_APPS_PER_ROW[layoutMode];
62972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
63072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      for (var i = 0; i < appCount; i++) {
63172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        var top = Math.floor(i / appsPerRow) * h;
63272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        var left = (i % appsPerRow) * w;
63372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
63472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        // Reflect the X axis if an RTL language is active.
63572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        if (rtl)
63672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          left = availableWidth - left - w;
63772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        rects[i] = {left: left, top: top};
63872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      }
63972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return rects;
64072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    },
64172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
642dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    get loadedImages() {
643dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      return this.loadedImages_;
644dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    },
645dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
646dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    set loadedImages(value) {
647dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      this.loadedImages_ = value;
648dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      if (this.loadedImages_ == 0)
649dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        return;
650dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
651dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      // Each application icon is loaded asynchronously. Here, we display
652dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      // the icons once they've all been loaded to make it look nicer.
653dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      if (this.loadedImages_ == this.data.length) {
654dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        this.showImages();
655dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        return;
656dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      }
657dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
658dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      // We won't actually have the visible height until the sections have
659dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      // been layed out.
660dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      if (!maxiviewVisibleHeight)
661dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        return;
662dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
663dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      // If we know the visible height of the maxiview, then we can don't need
664dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      // to wait for all the icons. Instead, we wait until the visible portion
665dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      // have been loaded.
666dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      var appsPerRow = MAX_APPS_PER_ROW[layoutMode];
667dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      var rows = Math.ceil(maxiviewVisibleHeight / this.dimensions.height);
668dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      var count = Math.min(appsPerRow * rows, this.data.length);
669dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      if (this.loadedImages_ == count) {
670dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        this.showImages();
671dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        return;
672dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      }
673dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    },
674dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
675dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    showImages: function() {
676dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      $('apps-content').classList.add('visible');
677dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      clearTimeout(this.imageTimer);
678dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    },
679dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
6803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    createElement: function(app) {
6813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      var div = createElement(app);
6823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      var a = div.firstChild;
6833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
6843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      a.onclick = handleClick;
685ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      a.ping = getAppPingUrl(
686ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          'PING_BY_ID', this.showPromo, 'NTP_APPS_MAXIMIZED');
6873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      a.style.backgroundImage = url(app['icon_big']);
688dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      if (app.isNew) {
6893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        div.setAttribute('new', 'new');
6903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        // Delay changing the attribute a bit to let the page settle down a bit.
6913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        setTimeout(function() {
692731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick          // Make sure the new icon is scrolled into view.
693731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick          document.body.scrollTop = document.body.scrollHeight;
694731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
6953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick          // This will trigger the 'bounce' animation defined in apps.css.
6963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick          div.setAttribute('new', 'installed');
6973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        }, 500);
6983345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        div.addEventListener('webkitAnimationEnd', function(e) {
6993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick          div.removeAttribute('new');
7003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        });
7013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      }
7023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
703dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      // CSS background images don't fire 'load' events, so we use an Image.
704dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      var img = new Image();
705dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      img.onload = function() { this.loadedImages++; }.bind(this);
706dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      img.src = app['icon_big'];
707dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
7083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      var settingsButton = div.appendChild(new cr.ui.ContextMenuButton);
7093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      settingsButton.className = 'app-settings';
7103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      settingsButton.title = localStrings.getString('appsettings');
7113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
7123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      addContextMenu(div, app);
7133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
7143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      return div;
7153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    },
7163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
7173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    createMiniviewElement: function(app) {
7183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      var span = document.createElement('span');
7193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      var a = span.appendChild(document.createElement('a'));
7203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
7213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      a.setAttribute('app-id', app['id']);
7223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      a.textContent = app['name'];
7233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      a.href = app['launch_url'];
7243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      a.onclick = handleClick;
725ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      a.ping = getAppPingUrl(
726ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          'PING_BY_ID', this.showPromo, 'NTP_APPS_COLLAPSED');
7273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      a.style.backgroundImage = url(app['icon_small']);
7283345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      a.className = 'item';
7293345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      span.appendChild(a);
7303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
7313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      addContextMenu(span, app);
7323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
7333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      return span;
7343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    },
7353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
736731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    createClosedMenuElement: function(app) {
737731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      var a = document.createElement('a');
738731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      a.setAttribute('app-id', app['id']);
739731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      a.textContent = app['name'];
740731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      a.href = app['launch_url'];
741731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      a.onclick = handleClick;
742ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      a.ping = getAppPingUrl(
743ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          'PING_BY_ID', this.showPromo, 'NTP_APPS_MENU');
744731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      a.style.backgroundImage = url(app['icon_small']);
745731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      a.className = 'item';
746201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
747201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      addContextMenu(a, app);
748201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
749731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      return a;
750731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    },
751731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
7523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    createWebStoreElement: function() {
753731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      var elm = createElement({
7543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        'id': 'web-store-entry',
7553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        'name': localStrings.getString('web_store_title'),
7563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        'launch_url': localStrings.getString('web_store_url')
7573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      });
75872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      elm.classList.add('web-store-entry');
759731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      return elm;
760ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch    },
761ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch
762ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch    createWebStoreMiniElement: function() {
763ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch      var span = document.createElement('span');
764ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch      span.appendChild(this.createWebStoreClosedMenuElement());
765ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch      return span;
766ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch    },
767ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch
768ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch    createWebStoreClosedMenuElement: function() {
769ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch      var a = document.createElement('a');
770ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch      a.textContent = localStrings.getString('web_store_title');
771ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch      a.href = localStrings.getString('web_store_url');
772ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch      a.style.backgroundImage = url('chrome://theme/IDR_PRODUCT_LOGO_16');
773ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch      a.className = 'item';
774ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch      return a;
7753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    }
7763345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  };
7773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick})();
77872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
77972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Enable drag and drop reordering of the app launcher.
78072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvar appDragAndDrop = new DragAndDropController(apps);
781