1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Use of this source code is governed by a BSD-style license that can be 3ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// found in the LICENSE file. 4ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 5ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/** 6ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @fileoverview New tab page 7ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * This is the main code for the new tab page used by touch-enabled Chrome 8ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * browsers. For now this is still a prototype. 9ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 10ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 11ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Use an anonymous function to enable strict mode just for this file (which 12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// will be concatenated with other files when embedded in Chrome 13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsencr.define('ntp4', function() { 14ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 'use strict'; 15ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 16ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 17ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The CardSlider object to use for changing app pages. 18ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {CardSlider|undefined} 19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var cardSlider; 21ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 23ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Template to use for creating new 'dot' elements 24ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!Element|undefined} 25ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 26ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var dotTemplate; 27ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 28ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 29ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The 'page-list' element. 30ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!Element|undefined} 31ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 32ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var pageList; 33ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 34ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 35ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * A list of all 'tile-page' elements. 36ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!NodeList|undefined} 37ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 38ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var tilePages; 39ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 40ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 41ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The Most Visited page. 42ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!Element|undefined} 43ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 44ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var mostVisitedPage; 45ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 46ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 47ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * A list of all 'apps-page' elements. 48ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!NodeList|undefined} 49ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 50ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var appsPages; 51ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 52ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 53ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The 'dots-list' element. 54ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!Element|undefined} 55ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 56ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var dotList; 57ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 58ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 59ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * A list of all 'dots' elements. 60ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!NodeList|undefined} 61ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 62ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var dots; 63ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 64ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 65ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The 'trash' element. Note that technically this is unnecessary, 66ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * JavaScript creates the object for us based on the id. But I don't want 67ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * to rely on the ID being the same, and JSCompiler doesn't know about it. 68ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!Element|undefined} 69ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 70ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var trash; 71ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 72ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 73ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The time in milliseconds for most transitions. This should match what's 74ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * in new_tab.css. Unfortunately there's no better way to try to time 75ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * something to occur until after a transition has completed. 76ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {number} 77ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @const 78ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 79ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var DEFAULT_TRANSITION_TIME = 500; 80ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 81ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 82ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * All the Grabber objects currently in use on the page 83ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {Array.<Grabber>} 84ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 85ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var grabbers = []; 86ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 87ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 88ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Invoked at startup once the DOM is available to initialize the app. 89ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 90ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function initialize() { 91ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Load the current theme colors. 92ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen themeChanged(false); 93ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 94ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen dotList = getRequiredElement('dot-list'); 95ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen pageList = getRequiredElement('page-list'); 96ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen trash = getRequiredElement('trash'); 97ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen trash.hidden = true; 98ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 99ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Request data on the apps so we can fill them in. 100ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Note that this is kicked off asynchronously. 'getAppsCallback' will be 101ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // invoked at some point after this function returns. 102ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen chrome.send('getApps'); 103ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 104ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Prevent touch events from triggering any sort of native scrolling 105ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen document.addEventListener('touchmove', function(e) { 106ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen e.preventDefault(); 107ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, true); 108ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 109ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Get the template elements and remove them from the DOM. Things are 110ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // simpler if we start with 0 pages and 0 apps and don't leave hidden 111ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // template elements behind in the DOM. 112ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen dots = dotList.getElementsByClassName('dot'); 113ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(dots.length == 1, 114ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 'Expected exactly one dot in the dots-list.'); 115ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen dotTemplate = dots[0]; 116ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen dotList.removeChild(dots[0]); 117ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 118ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen tilePages = pageList.getElementsByClassName('tile-page'); 119ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen appsPages = pageList.getElementsByClassName('apps-page'); 120ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 121ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Initialize the cardSlider without any cards at the moment 122ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var sliderFrame = getRequiredElement('card-slider-frame'); 123ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen cardSlider = new CardSlider(sliderFrame, pageList, [], 0, 124ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen sliderFrame.offsetWidth); 125ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen cardSlider.initialize(); 126ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 127ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Ensure the slider is resized appropriately with the window 128ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen window.addEventListener('resize', function() { 129ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen cardSlider.resize(sliderFrame.offsetWidth); 130ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }); 131ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 132ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Handle the page being changed 133ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen pageList.addEventListener( 134ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen CardSlider.EventType.CARD_CHANGED, 135ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function(e) { 136ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Update the active dot 137ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var curDot = dotList.getElementsByClassName('selected')[0]; 138ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (curDot) 139ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen curDot.classList.remove('selected'); 140ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var newPageIndex = e.cardSlider.currentCard; 141ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen dots[newPageIndex].classList.add('selected'); 142ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // If an app was being dragged, move it to the end of the new page 143ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (draggingAppContainer) 144ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen appsPages[newPageIndex].appendChild(draggingAppContainer); 145ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }); 146ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 147ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Add a drag handler to the body (for drags that don't land on an existing 148ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // app) 149ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen document.addEventListener(Grabber.EventType.DRAG_ENTER, appDragEnter); 150ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 151ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Handle dropping an app anywhere other than on the trash 152ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen document.addEventListener(Grabber.EventType.DROP, appDrop); 153ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 154ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Add handles to manage the transition into/out-of rearrange mode 155ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Note that we assume here that we only use a Grabber for moving apps, 156ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // so ANY GRAB event means we're enterring rearrange mode. 157ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen sliderFrame.addEventListener(Grabber.EventType.GRAB, enterRearrangeMode); 158ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen sliderFrame.addEventListener(Grabber.EventType.RELEASE, leaveRearrangeMode); 159ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 160ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Add handlers for the tash can 161ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen trash.addEventListener(Grabber.EventType.DRAG_ENTER, function(e) { 162ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen trash.classList.add('hover'); 163ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen e.grabbedElement.classList.add('trashing'); 164ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen e.stopPropagation(); 165ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }); 166ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen trash.addEventListener(Grabber.EventType.DRAG_LEAVE, function(e) { 167ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen e.grabbedElement.classList.remove('trashing'); 168ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen trash.classList.remove('hover'); 169ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }); 170ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen trash.addEventListener(Grabber.EventType.DROP, appTrash); 171ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 172ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen cr.ui.decorate($('recently-closed-menu-button'), ntp4.RecentMenuButton); 173ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen chrome.send('getRecentlyClosedTabs'); 174ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 175ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen mostVisitedPage = new ntp4.MostVisitedPage('Most Visited'); 176ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen appendTilePage(mostVisitedPage); 177ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen chrome.send('getMostVisited'); 178ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 179ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 180ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 181ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Simple common assertion API 182ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {*} condition The condition to test. Note that this may be used to 183ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * test whether a value is defined or not, and we don't want to force a 184ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * cast to Boolean. 185ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {string=} opt_message A message to use in any error. 186ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 187ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function assert(condition, opt_message) { 188ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 'use strict'; 189ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!condition) { 190ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var msg = 'Assertion failed'; 191ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (opt_message) 192ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen msg = msg + ': ' + opt_message; 193ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen throw new Error(msg); 194ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 195ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 196ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 197ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 198ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Get an element that's known to exist by its ID. We use this instead of just 199ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * calling getElementById and not checking the result because this lets us 200ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * satisfy the JSCompiler type system. 201ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {string} id The identifier name. 202ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @return {!Element} the Element. 203ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 204ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function getRequiredElement(id) { 205ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var element = document.getElementById(id); 206ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(element, 'Missing required element: ' + id); 207ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return element; 208ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 209ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 210ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 211ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Callback invoked by chrome with the apps available. 212ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * 213ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Note that calls to this function can occur at any time, not just in 214ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * response to a getApps request. For example, when a user installs/uninstalls 215ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * an app on another synchronized devices. 216ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {Object} data An object with all the data on available 217ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * applications. 218ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 219ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function getAppsCallback(data) { 220ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Clean up any existing grabber objects - cancelling any outstanding drag. 221ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Ideally an async app update wouldn't disrupt an active drag but 222ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // that would require us to re-use existing elements and detect how the apps 223ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // have changed, which would be a lot of work. 224ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Note that we have to explicitly clean up the grabber objects so they stop 225ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // listening to events and break the DOM<->JS cycles necessary to enable 226ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // collection of all these objects. 227ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen grabbers.forEach(function(g) { 228ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Note that this may raise DRAG_END/RELEASE events to clean up an 229ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // oustanding drag. 230ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen g.dispose(); 231ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }); 232ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(!draggingAppContainer && !draggingAppOriginalPosition && 233ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen !draggingAppOriginalPage); 234ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen grabbers = []; 235ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 236ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Clear any existing apps pages and dots. 237ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // TODO(rbyers): It might be nice to preserve animation of dots after an 238ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // uninstall. Could we re-use the existing page and dot elements? It seems 239ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // unfortunate to have Chrome send us the entire apps list after an 240ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // uninstall. 241ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (var i = 0; i < appsPages.length; i++) { 242ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var page = appsPages[i]; 243ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var dot = page.navigationDot; 244ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 245ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen page.tearDown(); 246ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen page.parentNode.removeChild(page); 247ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen dot.parentNode.removeChild(dot); 248ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 249ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 250ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Get the array of apps and add any special synthesized entries 251ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var apps = data.apps; 252ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 253ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Sort by launch index 254ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen apps.sort(function(a, b) { 255ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return a.app_launch_index - b.app_launch_index; 256ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }); 257ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 258ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Add the apps, creating pages as necessary 259ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (var i = 0; i < apps.length; i++) { 260ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var app = apps[i]; 261ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var pageIndex = (app.page_index || 0); 262ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen while (pageIndex >= appsPages.length) { 263ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var origPageCount = appsPages.length; 264ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen appendTilePage(new ntp4.AppsPage('Apps')); 265ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Confirm that appsPages is a live object, updated when a new page is 266ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // added (otherwise we'd have an infinite loop) 267ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(appsPages.length == origPageCount + 1, 'expected new page'); 268ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 269ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 270ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen appsPages[pageIndex].appendApp(app); 271ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 272ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 273ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Add a couple blank apps pages for testing. TODO(estade): remove this. 274ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen appendTilePage(new ntp4.AppsPage('Foo')); 275ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen appendTilePage(new ntp4.AppsPage('Bar')); 276ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 277ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Tell the slider about the pages 278ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen updateSliderCards(); 279ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 280ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Mark the current page 281ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen dots[cardSlider.currentCard].classList.add('selected'); 282ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 283ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 284ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 285ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Make a synthesized app object representing the chrome web store. It seems 286ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * like this could just as easily come from the back-end, and then would 287ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * support being rearranged, etc. 288ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @return {Object} The app object as would be sent from the webui back-end. 289ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 290ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function makeWebstoreApp() { 291ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return { 292ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen id: '', // Empty ID signifies this is a special synthesized app 293ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen page_index: 0, 294ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen app_launch_index: -1, // always first 295ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen name: templateData.web_store_title, 296ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen launch_url: templateData.web_store_url, 297ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen icon_big: getThemeUrl('IDR_WEBSTORE_ICON') 298ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }; 299ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 300ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 301ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 302ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Given a theme resource name, construct a URL for it. 303ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {string} resourceName The name of the resource. 304ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @return {string} A url which can be used to load the resource. 305ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 306ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function getThemeUrl(resourceName) { 307ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return 'chrome://theme/' + resourceName; 308ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 309ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 310ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 311ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Callback invoked by chrome whenever an app preference changes. 312ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The normal NTP uses this to keep track of the current launch-type of an 313ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * app, updating the choices in the context menu. We don't have such a menu 314ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * so don't use this at all (but it still needs to be here for chrome to 315ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * call). 316ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {Object} data An object with all the data on available 317ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * applications. 318ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 319ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function appsPrefChangeCallback(data) { 320ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 321ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 322ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 323ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Invoked whenever the pages in apps-page-list have changed so that 324ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * the Slider knows about the new elements. 325ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 326ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function updateSliderCards() { 327ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var pageNo = cardSlider.currentCard; 328ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (pageNo >= tilePages.length) 329ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen pageNo = tilePages.length - 1; 330ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var pageArray = []; 331ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (var i = 0; i < tilePages.length; i++) 332ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen pageArray[i] = tilePages[i]; 333ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen cardSlider.setCards(pageArray, pageNo); 334ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 335ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 336ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 337ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Appends a tile page (for apps or most visited). 338ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * 339ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {TilePage} page The page element. 340ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {boolean=} opt_animate If true, add the class 'new' to the created 341ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * dot. 342ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 343ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function appendTilePage(page, opt_animate) { 344ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen pageList.appendChild(page); 345ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 346ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Make a deep copy of the dot template to add a new one. 347ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var dotCount = dots.length; 348ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var newDot = dotTemplate.cloneNode(true); 349ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen newDot.querySelector('span').textContent = page.pageName; 350ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (opt_animate) 351ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen newDot.classList.add('new'); 352ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen dotList.appendChild(newDot); 353ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen page.navigationDot = newDot; 354ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 355ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Add click handler to the dot to change the page. 356ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // TODO(rbyers): Perhaps this should be TouchHandler.START_EVENT_ (so we 357ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // don't rely on synthesized click events, and the change takes effect 358ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // before releasing). However, click events seems to be synthesized for a 359ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // region outside the border, and a 10px box is too small to require touch 360ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // events to fall inside of. We could get around this by adding a box around 361ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // the dot for accepting the touch events. 362ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function switchPage(e) { 363ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen cardSlider.selectCard(dotCount, true); 364ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen e.stopPropagation(); 365ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 366ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen newDot.addEventListener('click', switchPage); 367ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 368ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Change pages whenever an app is dragged over a dot. 369ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen newDot.addEventListener(Grabber.EventType.DRAG_ENTER, switchPage); 370ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 371ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 372ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Search an elements ancestor chain for the nearest element that is a member 373ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * of the specified class. 374ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {!Element} element The element to start searching from. 375ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {string} className The name of the class to locate. 376ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @return {Element} The first ancestor of the specified class or null. 377ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 378ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function getParentByClassName(element, className) { 379ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (var e = element; e; e = e.parentElement) { 380ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (e.classList.contains(className)) 381ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return e; 382ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 383ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return null; 384ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 385ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 386ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 387ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The container where the app currently being dragged came from. 388ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!Element|undefined} 389ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 390ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var draggingAppContainer; 391ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 392ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 393ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The apps-page that the app currently being dragged camed from. 394ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!Element|undefined} 395ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 396ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var draggingAppOriginalPage; 397ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 398ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 399ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The element that was originally after the app currently being dragged (or 400ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * null if it was the last on the page). 401ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!Element|undefined} 402ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 403ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var draggingAppOriginalPosition; 404ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 405ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 406ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Invoked when app dragging begins. 407ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {Grabber.Event} e The event from the Grabber indicating the drag. 408ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 409ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function appDragStart(e) { 410ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Pull the element out to the sliderFrame using fixed positioning. This 411ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // ensures that the app is not affected (remains under the finger) if the 412ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // slider changes cards and is translated. An alternate approach would be 413ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // to use fixed positioning for the slider (so that changes to its position 414ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // don't affect children that aren't positioned relative to it), but we 415ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // don't yet have GPU acceleration for this. 416ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var element = e.grabbedElement; 417ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 418ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var pos = element.getBoundingClientRect(); 419ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen element.style.webkitTransform = ''; 420ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 421ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen element.style.position = 'fixed'; 422ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Don't want to zoom around the middle since the left/top co-ordinates 423ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // are post-transform values. 424ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen element.style.webkitTransformOrigin = 'left top'; 425ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen element.style.left = pos.left + 'px'; 426ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen element.style.top = pos.top + 'px'; 427ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 428ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Keep track of what app is being dragged and where it came from 429ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(!draggingAppContainer, 'got DRAG_START without DRAG_END'); 430ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen draggingAppContainer = element.parentNode; 431ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(draggingAppContainer.classList.contains('app-container')); 432ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen draggingAppOriginalPosition = draggingAppContainer.nextSibling; 433ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen draggingAppOriginalPage = draggingAppContainer.parentNode; 434ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 435ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Move the app out of the container 436ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Note that appendChild also removes the element from its current parent. 437ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen sliderFrame.appendChild(element); 438ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 439ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 440ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 441ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Invoked when app dragging terminates (either successfully or not) 442ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {Grabber.Event} e The event from the Grabber. 443ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 444ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function appDragEnd(e) { 445ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Stop floating the app 446ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var appBeingDragged = e.grabbedElement; 447ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(appBeingDragged.classList.contains('app')); 448ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen appBeingDragged.style.position = ''; 449ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen appBeingDragged.style.webkitTransformOrigin = ''; 450ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen appBeingDragged.style.left = ''; 451ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen appBeingDragged.style.top = ''; 452ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 453ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Ensure the trash can is not active (we won't necessarily get a DRAG_LEAVE 454ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // for it - eg. if we drop on it, or the drag is cancelled) 455ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen trash.classList.remove('hover'); 456ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen appBeingDragged.classList.remove('trashing'); 457ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 458ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // If we have an active drag (i.e. it wasn't aborted by an app update) 459ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (draggingAppContainer) { 460ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Put the app back into it's container 461ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (appBeingDragged.parentNode != draggingAppContainer) 462ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen draggingAppContainer.appendChild(appBeingDragged); 463ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 464ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // If we care about the container's original position 465ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (draggingAppOriginalPage) 466ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen { 467ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Then put the container back where it came from 468ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (draggingAppOriginalPosition) { 469ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen draggingAppOriginalPage.insertBefore(draggingAppContainer, 470ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen draggingAppOriginalPosition); 471ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } else { 472ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen draggingAppOriginalPage.appendChild(draggingAppContainer); 473ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 474ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 475ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 476ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 477ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen draggingAppContainer = undefined; 478ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen draggingAppOriginalPage = undefined; 479ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen draggingAppOriginalPosition = undefined; 480ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 481ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 482ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 483ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Invoked when an app is dragged over another app. Updates the DOM to affect 484ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * the rearrangement (but doesn't commit the change until the app is dropped). 485ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {Grabber.Event} e The event from the Grabber indicating the drag. 486ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 487ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function appDragEnter(e) 488ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen { 489ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(draggingAppContainer, 'expected stored container'); 490ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var sourceContainer = draggingAppContainer; 491ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 492ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Ensure enter events delivered to an app-container don't also get 493ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // delivered to the document. 494ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen e.stopPropagation(); 495ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 496ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var curPage = appsPages[cardSlider.currentCard]; 497ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var followingContainer = null; 498ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 499ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // If we dragged over a specific app, determine which one to insert before 500ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (e.currentTarget != document) { 501ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 502ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Start by assuming we'll insert the app before the one dragged over 503ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen followingContainer = e.currentTarget; 504ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(followingContainer.classList.contains('app-container'), 505ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 'expected drag over container'); 506ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(followingContainer.parentNode == curPage); 507ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (followingContainer == draggingAppContainer) 508ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return; 509ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 510ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // But if it's after the current container position then we'll need to 511ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // move ahead by one to account for the container being removed. 512ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (curPage == draggingAppContainer.parentNode) { 513ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (var c = draggingAppContainer; c; c = c.nextElementSibling) { 514ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (c == followingContainer) { 515ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen followingContainer = followingContainer.nextElementSibling; 516ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen break; 517ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 518ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 519ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 520ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 521ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 522ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Move the container to the appropriate place on the page 523ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen curPage.insertBefore(draggingAppContainer, followingContainer); 524ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 525ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 526ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 527ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Invoked when an app is dropped on the trash 528ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {Grabber.Event} e The event from the Grabber indicating the drop. 529ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 530ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function appTrash(e) { 531ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var appElement = e.grabbedElement; 532ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(appElement.classList.contains('app')); 533ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var appId = appElement.getAttribute('app-id'); 534ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(appId); 535ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 536ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Mark this drop as handled so that the catch-all drop handler 537ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // on the document doesn't see this event. 538ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen e.stopPropagation(); 539ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 540ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Tell chrome to uninstall the app (prompting the user) 541ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen chrome.send('uninstallApp', [appId]); 542ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 543ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 544ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 545ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Called when an app is dropped anywhere other than the trash can. Commits 546ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * any movement that has occurred. 547ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {Grabber.Event} e The event from the Grabber indicating the drop. 548ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 549ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function appDrop(e) { 550ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!draggingAppContainer) 551ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Drag was aborted (eg. due to an app update) - do nothing 552ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return; 553ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 554ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // If the app is dropped back into it's original position then do nothing 555ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(draggingAppOriginalPage); 556ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (draggingAppContainer.parentNode == draggingAppOriginalPage && 557ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen draggingAppContainer.nextSibling == draggingAppOriginalPosition) 558ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return; 559ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 560ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Determine which app was being dragged 561ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var appElement = e.grabbedElement; 562ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(appElement.classList.contains('app')); 563ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var appId = appElement.getAttribute('app-id'); 564ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(appId); 565ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 566ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Update the page index for the app if it's changed. This doesn't trigger 567ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // a call to getAppsCallback so we want to do it before reorderApps 568ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var pageIndex = cardSlider.currentCard; 569ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(pageIndex >= 0 && pageIndex < appsPages.length, 570ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 'page number out of range'); 571ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (appsPages[pageIndex] != draggingAppOriginalPage) 572ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen chrome.send('setPageIndex', [appId, pageIndex]); 573ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 574ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Put the app being dragged back into it's container 575ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen draggingAppContainer.appendChild(appElement); 576ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 577ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Create a list of all appIds in the order now present in the DOM 578ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var appIds = []; 579ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (var page = 0; page < appsPages.length; page++) { 580ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var appsOnPage = appsPages[page].getElementsByClassName('app'); 581ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (var i = 0; i < appsOnPage.length; i++) { 582ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var id = appsOnPage[i].getAttribute('app-id'); 583ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (id) 584ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen appIds.push(id); 585ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 586ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 587ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 588ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // We are going to commit this repositioning - clear the original position 589ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen draggingAppOriginalPage = undefined; 590ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen draggingAppOriginalPosition = undefined; 591ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 592ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Tell chrome to update its database to persist this new order of apps This 593ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // will cause getAppsCallback to be invoked and the apps to be redrawn. 594ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen chrome.send('reorderApps', [appId, appIds]); 595ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen appMoved = true; 596ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 597ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 598ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 599ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Set to true if we're currently in rearrange mode and an app has 600ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * been successfully dropped to a new location. This indicates that 601ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * a getAppsCallback call is pending and we can rely on the DOM being 602ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * updated by that. 603ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {boolean} 604ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 605ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var appMoved = false; 606ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 607ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 608ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Invoked whenever some app is grabbed 609ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {Grabber.Event} e The Grabber Grab event. 610ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 611ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function enterRearrangeMode(e) 612ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen { 613ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Stop the slider from sliding for this touch 614ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen cardSlider.cancelTouch(); 615ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 616ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Add an extra blank page in case the user wants to create a new page 617ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen appendTilePage(new ntp4.AppsPage(''), true); 618ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var pageAdded = appsPages.length - 1; 619ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen window.setTimeout(function() { 620ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen dots[pageAdded].classList.remove('new'); 621ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 0); 622ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 623ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen updateSliderCards(); 624ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 625ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Cause the dot-list to grow 626ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen getRequiredElement('footer').classList.add('rearrange-mode'); 627ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 628ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(!appMoved, 'appMoved should not be set yet'); 629ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 630ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 631ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 632ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Invoked whenever some app is released 633ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {Grabber.Event} e The Grabber RELEASE event. 634ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 635ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function leaveRearrangeMode(e) 636ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen { 637ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Return the dot-list to normal 638ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen getRequiredElement('footer').classList.remove('rearrange-mode'); 639ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 640ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // If we didn't successfully re-arrange an app, then we won't be 641ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // refreshing the app view in getAppCallback and need to explicitly remove 642ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // the extra empty page we added. We don't want to do this in the normal 643ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // case because if we did actually drop an app there, we want to retain that 644ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // page as our current page number. 645ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!appMoved) { 646ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(appsPages[appsPages.length - 1]. 647ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen getElementsByClassName('app-container').length == 0, 648ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 'Last app page should be empty'); 649ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen removePage(appsPages.length - 1); 650ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 651ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen appMoved = false; 652ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 653ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 654ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 655ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Remove the page with the specified index and update the slider. 656ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {number} pageNo The index of the page to remove. 657ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 658ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function removePage(pageNo) { 659ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen pageList.removeChild(tilePages[pageNo]); 660ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 661ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Remove the corresponding dot 662ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Need to give it a chance to animate though 663ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var dot = dots[pageNo]; 664ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen dot.classList.add('new'); 665ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen window.setTimeout(function() { 666ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // If we've re-created the apps (eg. because an app was uninstalled) then 667ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // we will have removed the old dots from the document already, so skip. 668ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (dot.parentNode) 669ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen dot.parentNode.removeChild(dot); 670ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, DEFAULT_TRANSITION_TIME); 671ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 672ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen updateSliderCards(); 673ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 674ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 675ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // TODO(estade): remove |hasAttribution|. 676ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // TODO(estade): rename newtab.css to new_tab_theme.css 677ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function themeChanged(hasAttribution) { 678ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen $('themecss').href = 'chrome://theme/css/newtab.css?' + Date.now(); 679ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 680ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 681ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function setRecentlyClosedTabs(dataItems) { 682ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen $('recently-closed-menu-button').dataItems = dataItems; 683ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 684ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 685ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function setMostVisitedPages(data, firstRun, hasBlacklistedUrls) { 686ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen mostVisitedPage.data = data; 687ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 688ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 689ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Return an object with all the exports 690ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return { 691ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert: assert, 692ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen appsPrefChangeCallback: appsPrefChangeCallback, 693ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen getAppsCallback: getAppsCallback, 694ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen initialize: initialize, 695ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen themeChanged: themeChanged, 696ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen setRecentlyClosedTabs: setRecentlyClosedTabs, 697ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen setMostVisitedPages: setMostVisitedPages, 698ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }; 699ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}); 700ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 701ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// publish ntp globals 702ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// TODO(estade): update the content handlers to use ntp namespace instead of 703ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// making these global. 704ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar assert = ntp4.assert; 705ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar getAppsCallback = ntp4.getAppsCallback; 706ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar appsPrefChangeCallback = ntp4.appsPrefChangeCallback; 707ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar themeChanged = ntp4.themeChanged; 708ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar recentlyClosedTabs = ntp4.setRecentlyClosedTabs; 709ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar mostVisitedPages = ntp4.setMostVisitedPages; 710ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 711ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsendocument.addEventListener('DOMContentLoaded', ntp4.initialize); 712