Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(211)

Side by Side Diff: chrome/browser/resources/ntp4/page_list_view.js

Issue 8637001: [NTP4] Auto-deletion of empty apps panes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: removing bits for refactor Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/resources/ntp4/new_tab.js ('k') | chrome/browser/resources/ntp4/tile_page.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @fileoverview PageListView implementation. 6 * @fileoverview PageListView implementation.
7 * PageListView manages page list, dot list, switcher buttons and handles apps 7 * PageListView manages page list, dot list, switcher buttons and handles apps
8 * pages callbacks from backend. 8 * pages callbacks from backend.
9 * 9 *
10 * Note that you need to have AppLauncherHandler in your WebUI to use this code. 10 * Note that you need to have AppLauncherHandler in your WebUI to use this code.
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after
165 this.sliderFrame = cardSliderFrame; 165 this.sliderFrame = cardSliderFrame;
166 this.cardSlider = new cr.ui.CardSlider(this.sliderFrame, this.pageList, 166 this.cardSlider = new cr.ui.CardSlider(this.sliderFrame, this.pageList,
167 this.sliderFrame.offsetWidth); 167 this.sliderFrame.offsetWidth);
168 this.cardSlider.initialize(); 168 this.cardSlider.initialize();
169 169
170 // Handle the page being changed 170 // Handle the page being changed
171 this.pageList.addEventListener( 171 this.pageList.addEventListener(
172 cr.ui.CardSlider.EventType.CARD_CHANGED, 172 cr.ui.CardSlider.EventType.CARD_CHANGED,
173 this.cardChangedHandler_.bind(this)); 173 this.cardChangedHandler_.bind(this));
174 174
175 // Handle the end of cards being changed (useful if animated).
176 this.pageList.addEventListener(
177 cr.ui.CardSlider.EventType.CARD_CHANGE_ENDED,
178 this.cardChangeEndedHandler_.bind(this));
179
180 // Handle cards being added to the card slider.
181 this.pageList.addEventListener(
182 cr.ui.CardSlider.EventType.CARD_ADDED,
183 this.cardAddedHandler_.bind(this));
184
185 // Handle cards being removed from the card slider.
186 this.pageList.addEventListener(
187 cr.ui.CardSlider.EventType.CARD_REMOVED,
188 this.cardRemovedHandler_.bind(this));
189
190 // Handle tiles being removed from tile pages.
191 this.pageList.addEventListener(
192 ntp4.TilePage.EventType.TILE_REMOVED,
193 this.tileRemovedHandler_.bind(this));
194
175 // Ensure the slider is resized appropriately with the window 195 // Ensure the slider is resized appropriately with the window
176 window.addEventListener('resize', this.onWindowResize_.bind(this)); 196 window.addEventListener('resize', this.onWindowResize_.bind(this));
177 197
178 // Update apps when online state changes. 198 // Update apps when online state changes.
179 window.addEventListener('online', 199 window.addEventListener('online',
180 this.updateOfflineEnabledApps_.bind(this)); 200 this.updateOfflineEnabledApps_.bind(this));
181 window.addEventListener('offline', 201 window.addEventListener('offline',
182 this.updateOfflineEnabledApps_.bind(this)); 202 this.updateOfflineEnabledApps_.bind(this));
183 }, 203 },
184 204
185 /** 205 /**
186 * Appends a tile page. 206 * Appends a tile page.
187 * 207 *
188 * @param {TilePage} page The page element. 208 * @param {TilePage} page The page element.
189 * @param {string} title The title of the tile page. 209 * @param {string} title The title of the tile page.
190 * @param {bool} titleIsEditable If true, the title can be changed. 210 * @param {bool} titleIsEditable If true, the title can be changed.
191 * @param {TilePage} opt_refNode Optional reference node to insert in front 211 * @param {TilePage} opt_refNode Optional reference node to insert in front
192 * of. 212 * of.
193 * When opt_refNode is falsey, |page| will just be appended to the end of 213 * When opt_refNode is falsey, |page| will just be appended to the end of
194 * the page list. 214 * the page list.
195 */ 215 */
196 appendTilePage: function(page, title, titleIsEditable, opt_refNode) { 216 appendTilePage: function(page, title, titleIsEditable, opt_refNode) {
197 // When opt_refNode is falsey, insertBefore acts just like appendChild. 217 if (opt_refNode) {
198 this.pageList.insertBefore(page, opt_refNode); 218 var refIndex = this.getTilePageIndex(opt_refNode);
219 this.cardSlider.insertCardAtIndex(page, refIndex);
220 } else {
221 this.cardSlider.appendCard(page);
222 }
199 223
200 // Remember special MostVisitedPage. 224 // Remember special MostVisitedPage.
201 if (typeof ntp4.MostVisitedPage != 'undefined' && 225 if (typeof ntp4.MostVisitedPage != 'undefined' &&
202 page instanceof ntp4.MostVisitedPage) { 226 page instanceof ntp4.MostVisitedPage) {
203 assert(this.tilePages.length == 1, 227 assert(this.tilePages.length == 1,
204 'MostVisitedPage should be added as first tile page'); 228 'MostVisitedPage should be added as first tile page');
205 this.mostVisitedPage = page; 229 this.mostVisitedPage = page;
206 } 230 }
207 231
208 // If we're appending an AppsPage and it's a temporary page, animate it. 232 // If we're appending an AppsPage and it's a temporary page, animate it.
(...skipping 11 matching lines...) Expand all
220 this.eventTracker.add(page, 'pagelayout', this.onPageLayout_.bind(this)); 244 this.eventTracker.add(page, 'pagelayout', this.onPageLayout_.bind(this));
221 }, 245 },
222 246
223 /** 247 /**
224 * Called by chrome when an existing app has been disabled or 248 * Called by chrome when an existing app has been disabled or
225 * removed/uninstalled from chrome. 249 * removed/uninstalled from chrome.
226 * @param {Object} appData A data structure full of relevant information for 250 * @param {Object} appData A data structure full of relevant information for
227 * the app. 251 * the app.
228 * @param {boolean} isUninstall True if the app is being uninstalled; 252 * @param {boolean} isUninstall True if the app is being uninstalled;
229 * false if the app is being disabled. 253 * false if the app is being disabled.
254 * @param {boolean} fromPage If the removal was from the current page.
230 */ 255 */
231 appRemoved: function(appData, isUninstall) { 256 appRemoved: function(appData, isUninstall, fromPage) {
232 var app = $(appData.id); 257 var app = $(appData.id);
233 assert(app, 'trying to remove an app that doesn\'t exist'); 258 assert(app, 'trying to remove an app that doesn\'t exist');
234 259
235 if (!isUninstall) 260 if (!isUninstall) {
236 app.replaceAppData(appData); 261 app.replaceAppData(appData);
237 else 262 } else {
238 app.remove(); 263 // If the uninstall was from this page, run the blipout animation and
264 // the tile will be removed in TilePage#onContentsAnimationEnd_.
265 // Otherwise delete the tile without auto-deleting the page to avoid
266 // re-deleting the same page (or the page that slid in to take its
267 // place).
268 if (fromPage) {
269 // Unset the ID immediately, because the app is already gone. But
270 // leave the tile on the page as it animates out.
271 app.id = '';
272 app.classList.add('removing-tile-contents');
273 } else {
274 var tilePage = app.tile.tilePage;
275 tilePage.removeTile(app.tile, false, true);
276 this.removeAppsPageIfEmpty_(tilePage, false, true);
277 }
278 }
239 }, 279 },
240 280
241 /** 281 /**
282 * @return {boolean} If the page is still starting up.
283 * @private
284 */
285 isStartingUp_: function() {
286 return document.documentElement.classList.contains('starting-up');
287 },
288
289 /**
290 * Returns a hashmap of apps pages keyed by page ordinal.
291 * @return {Object.<string, AppsPage>} Map of apps pages.
292 * @private
293 */
294 getAppsPageOrdinalMap_: function() {
295 var map = {};
296 for (var i = 0; i < this.appsPages.length; ++i)
297 map[this.appsPages[i].ordinal] = this.appsPages[i];
298 return map;
299 },
300
301 /**
302 * Get an unsorted list of apps page ordinals.
303 */
304 getAppsPageOrdinals_: function() {
305 return Array.prototype.map.call(this.appsPages, function(page) {
306 return page.ordinal;
307 });
308 },
309
310 /**
311 * Gets an apps page by string ordinal.
312 * @param {string} ordinal The page ordinal we're searching for.
313 * @return {?AppsPage} The apps page with corresponding ordinal or null.
314 * @private
315 */
316 getAppsPageByOrdinal_: function(ordinal) {
317 assert(typeof ordinal == 'string' && ordinal);
318 return this.getAppsPageOrdinalMap_()[ordinal] || null;
319 },
320
321 /**
322 * Find an apps page index via ordinal.
323 * @return {number} The index of the ordinal or -1 if it doesn't exist.
324 */
325 getPageIndexFromOrdinal_: function(ordinal) {
326 return this.getAppsPageOrdinals_().indexOf(ordinal);
327 },
328
329 /**
330 * Finds a position for a specific ordinal in an ordinal map.
331 * @param {Array} ordinalList An array of ordinals.
332 * @param {string} ordinal A specific ordinal to search for.
333 * @return {number} The position where the ordinal should be positioned.
334 */
335 getPositionForNewOrdinal_: function(ordinalList, ordinal) {
336 assert(Array.isArray(ordinal) && ordinalList.indexOf(ordinal) == -1);
337 var sorted = Array.prototype.concat.call(ordinalList).sort();
338 function split(num) {
339 return Math.floor(num << 1);
340 }
341 var size = split(sorted.length);
342 var pos = size;
343 while (size > 0) {
344 size = split(size);
345 // This just no-ops when size = 0.
346 pos = ordinal > sorted[pos] ? pos + size : pos - size;
347 }
348 return pos;
349 },
350
351 /**
242 * Callback invoked by chrome with the apps available. 352 * Callback invoked by chrome with the apps available.
243 * 353 *
244 * Note that calls to this function can occur at any time, not just in 354 * Note that calls to this function can occur at any time, not just in
245 * response to a getApps request. For example, when a user 355 * response to a getApps request. For example, when a user
246 * installs/uninstalls an app on another synchronized devices. 356 * installs/uninstalls an app on another synchronized devices.
247 * @param {Object} data An object with all the data on available 357 * @param {Object} data An object with all the data on available
248 * applications. 358 * applications.
249 */ 359 */
250 getAppsCallback: function(data) { 360 getAppsCallback: function(data, measureTime) {
251 var startTime = Date.now(); 361 var startTime;
362 if (measureTime)
363 startTime = Date.now();
252 364
253 // Clear any existing apps pages and dots. 365 this.syncAppsPages(data);
254 // TODO(rbyers): It might be nice to preserve animation of dots after an
255 // uninstall. Could we re-use the existing page and dot elements? It
256 // seems unfortunate to have Chrome send us the entire apps list after an
257 // uninstall.
258 while (this.appsPages.length > 0) {
259 var page = this.appsPages[0];
260 var dot = page.navigationDot;
261
262 this.eventTracker.remove(page);
263 page.tearDown();
264 page.parentNode.removeChild(page);
265 dot.parentNode.removeChild(dot);
266 }
267
268 // Get the array of apps and add any special synthesized entries
269 var apps = data.apps;
270
271 // Get a list of page names
272 var pageNames = data.appPageNames;
273
274 function stringListIsEmpty(list) {
275 for (var i = 0; i < list.length; i++) {
276 if (list[i])
277 return false;
278 }
279 return true;
280 }
281
282 // Sort by launch ordinal
283 apps.sort(function(a, b) {
284 return a.app_launch_ordinal > b.app_launch_ordinal ? 1 :
285 a.app_launch_ordinal < b.app_launch_ordinal ? -1 : 0;
286 });
287
288 // An app to animate (in case it was just installed).
289 var highlightApp;
290
291 // Add the apps, creating pages as necessary
292 for (var i = 0; i < apps.length; i++) {
293 var app = apps[i];
294 var pageIndex = app.page_index || 0;
295 while (pageIndex >= this.appsPages.length) {
296 var pageName = localStrings.getString('appDefaultPageName');
297 if (this.appsPages.length < pageNames.length)
298 pageName = pageNames[this.appsPages.length];
299
300 var origPageCount = this.appsPages.length;
301 this.appendTilePage(new ntp4.AppsPage(), pageName, true);
302 // Confirm that appsPages is a live object, updated when a new page is
303 // added (otherwise we'd have an infinite loop)
304 assert(this.appsPages.length == origPageCount + 1,
305 'expected new page');
306 }
307
308 if (app.id == this.highlightAppId)
309 highlightApp = app;
310 else
311 this.appsPages[pageIndex].appendApp(app);
312 }
313
314 ntp4.AppsPage.setPromo(data.showPromo ? data : null); 366 ntp4.AppsPage.setPromo(data.showPromo ? data : null);
315 367
316 // Tell the slider about the pages. 368 // Tell the slider/switchers about the pages.
317 this.updateSliderCards(); 369 this.updateSliderCards();
370 this.updatePageSwitchers();
318 371
319 if (highlightApp) 372 if (highlightApp)
320 this.appAdded(highlightApp, true); 373 this.appAdded(highlightApp, true);
321 374
322 // Mark the current page. 375 // Mark the current page.
323 this.cardSlider.currentCardValue.navigationDot.classList.add('selected'); 376 this.cardSlider.currentCardValue.navigationDot.classList.add('selected');
324 logEvent('apps.layout: ' + (Date.now() - startTime)); 377
378 if (measureTime)
379 logEvent('apps.layout: ' + (Date.now() - startTime));
325 380
326 document.documentElement.classList.remove('starting-up'); 381 document.documentElement.classList.remove('starting-up');
327 }, 382 },
328 383
329 /** 384 /**
330 * Called by chrome when a new app has been added to chrome or has been 385 * @param {Object<>}
331 * enabled if previously disabled.
332 * @param {Object} appData A data structure full of relevant information for
333 * the app.
334 */ 386 */
335 appAdded: function(appData, opt_highlight) { 387 syncAppsPages: function() {
336 if (appData.id == this.highlightAppId) { 388 // Remove pages that aren't in the data we were given.
337 opt_highlight = true; 389 var existingPages = this.getAppsPageOrdinalMap_();
338 this.highlightAppId = null; 390 for (var i in existingPages) {
391 if (existingPages.hasOwnProperty(i) &&
392 !(existingPages[i] in data.appsPages)) {
393 this.removeTilePageAndDot_(existingPages[i]);
394 delete existingPages[i];
395 }
339 } 396 }
340 397 // Create or re-use pages or apps and ensure we're synced.
341 var pageIndex = appData.page_index || 0; 398 var pageOrdinals = Object.keys(data.appsPages).sort();
342 399 for (var i = 0; i < pageOrdinals.length; ++i) {
343 if (pageIndex >= this.appsPages.length) { 400 // Re-use previous pages or create new pages as necessary.
344 while (pageIndex >= this.appsPages.length) { 401 var appsPage = this.getOrCreateAppsPageAtOrdinal_(pageOrdinals[i]);
345 this.appendTilePage(new ntp4.AppsPage(), 402 existingPages[pageOrdinals[i]] = appsPage;
346 localStrings.getString('appDefaultPageName'), 403 var page = data.appsPages[pageOrdinals[i]];
347 true); 404 appsPage.navigationDot.displayTitle = page.name;
405 var appOrdinals = Object.keys(page.apps).sort();
406 for (var j = 0; j < appOrdinals.length; ++j) {
407 var appData = page.apps[appOrdinals[j]];
408 var app = $(appData.id);
409 if (app) {
410 if (appData.page_ordinal != app.data.page_ordinal ||
411 appData.app_launch_ordinal != app.data.app_launch_ordinal) {
412 var originalPage = app.tile.tilePage;
413 var detached = originalPage.removeTile(app);
414 var index = this.getIndexForNewOrdinal_(
415 appOrdinals, appData.app_launch_ordinal);
416 existingPages[appData.page_ordinal].addTileAt(detached, index);
417 } else {
418 app.replaceAppData(appData);
419 }
420 } else {
421 var index = this.getIndexForNewOrdinal_(app.app_launch_ordinal);
422 appsPage.addTileAt(app, app.id == data.highlightId);
423 }
348 } 424 }
349 this.updateSliderCards();
350 } 425 }
351 426 for (var i = 0; i < this.appsPages.length; ++i)
352 var page = this.appsPages[pageIndex]; 427 this.appsPages[i].cleanupDrag();
353 var app = $(appData.id);
354 if (app)
355 app.replaceAppData(appData);
356 else
357 page.appendApp(appData, opt_highlight);
358 }, 428 },
359 429
360 /** 430 /**
361 * Callback invoked by chrome whenever an app preference changes.
362 * @param {Object} data An object with all the data on available
363 * applications.
364 */
365 appsPrefChangedCallback: function(data) {
366 for (var i = 0; i < data.apps.length; ++i) {
367 $(data.apps[i].id).appData = data.apps[i];
368 }
369
370 // Set the App dot names. Skip the first dot (Most Visited).
371 var dots = this.dotList.getElementsByClassName('dot');
372 var start = this.mostVisitedPage ? 1 : 0;
373 for (var i = start; i < dots.length; ++i) {
374 dots[i].displayTitle = data.appPageNames[i - start] || '';
375 }
376 },
377
378 /**
379 * Invoked whenever the pages in apps-page-list have changed so that 431 * Invoked whenever the pages in apps-page-list have changed so that
380 * the Slider knows about the new elements. 432 * the Slider knows about the new elements.
381 */ 433 */
382 updateSliderCards: function() { 434 updateSliderCards: function() {
383 var pageNo = Math.min(this.cardSlider.currentCard, 435 var index = -1;
384 this.tilePages.length - 1); 436 var tiles = Array.prototype.slice.call(this.tilePages);
385 this.cardSlider.setCards(Array.prototype.slice.call(this.tilePages), 437 // Clamping this value each time helps self-heal unexpected input.
386 pageNo); 438 this.shownPageIndex = Math.max(0, Math.min(this.shownPageIndex,
439 this.appsPages.length - 1));
387 switch (this.shownPage) { 440 switch (this.shownPage) {
388 case templateData['apps_page_id']: 441 case templateData.apps_page_id:
389 this.cardSlider.selectCardByValue( 442 index = tiles.indexOf(this.appsPages[this.shownPageIndex]);
390 this.appsPages[Math.min(this.shownPageIndex,
391 this.appsPages.length - 1)]);
392 break; 443 break;
393 case templateData['most_visited_page_id']: 444 case templateData.most_visited_page_id:
394 if (this.mostVisitedPage) 445 index = tiles.indexOf(this.mostVisitedPage);
395 this.cardSlider.selectCardByValue(this.mostVisitedPage);
396 break; 446 break;
397 } 447 }
448 // If shownPage was saved as a page that's now disabled or the shownPage
449 // doesn't exist any more, index will be -1. Change to the preferred
450 // default page (first apps pane) in this case.
451 if (index < 0) {
452 this.shownPage = templateData.apps_page_id;
453 index = tiles.indexOf(this.appsPages[0]);
454 }
455 // Set the new cards and index.
456 this.cardSlider.setCards(tiles, index);
457
458 chrome.send('pageSelected', [this.shownPage, this.shownPageIndex]);
398 }, 459 },
399 460
400 /** 461 /**
401 * Called whenever tiles should be re-arranging themselves out of the way 462 * Called whenever tiles should be re-arranging themselves out of the way
402 * of a moving or insert tile. 463 * of a moving or insert tile.
403 */ 464 */
404 enterRearrangeMode: function() { 465 enterRearrangeMode: function() {
405 var tempPage = new ntp4.AppsPage(); 466 var tempPage = new ntp4.AppsPage();
406 tempPage.classList.add('temporary'); 467 tempPage.classList.add('temporary');
407 this.appendTilePage(tempPage, 468 var pageName = localStrings.getString('appDefaultPageName');
408 localStrings.getString('appDefaultPageName'), 469 this.appendTilePage(tempPage, pageName, true);
409 true);
410 var tempIndex = Array.prototype.indexOf.call(this.tilePages, tempPage);
411 if (this.cardSlider.currentCard >= tempIndex)
412 this.cardSlider.currentCard += 1;
413 this.updateSliderCards();
414 470
415 if (ntp4.getCurrentlyDraggingTile().firstChild.canBeRemoved()) 471 if (ntp4.getCurrentlyDraggingTile().firstChild.canBeRemoved())
416 $('footer').classList.add('showing-trash-mode'); 472 $('footer').classList.add('showing-trash-mode');
417 }, 473 },
418 474
419 /** 475 /**
420 * Invoked whenever some app is released 476 * Invoked whenever some app is released
421 */ 477 */
422 leaveRearrangeMode: function() { 478 leaveRearrangeMode: function() {
423 var tempPage = document.querySelector('.tile-page.temporary'); 479 var tempPage = document.querySelector('.tile-page.temporary');
424 var dot = tempPage.navigationDot; 480 // Either remove a temp page if it's empty or save the page name (as an
425 if (!tempPage.tileCount && tempPage != this.cardSlider.currentCardValue) { 481 // app has just been dropped on it or created somehow).
426 dot.animateRemove(); 482 // TODO(dbeam): Animated removal if currently on temp page.
427 var tempIndex = Array.prototype.indexOf.call(this.tilePages, tempPage); 483 if (tempPage && !this.removeAppsPageIfEmpty_(tempPage, true, true)) {
428 if (this.cardSlider.currentCard > tempIndex) 484 this.saveAppsPageName(tempPage,
429 this.cardSlider.currentCard -= 1; 485 tempPage.navigationDot.displayTitle,
430 tempPage.parentNode.removeChild(tempPage); 486 true);
431 this.updateSliderCards();
432 } else {
433 tempPage.classList.remove('temporary'); 487 tempPage.classList.remove('temporary');
434 this.saveAppPageName(tempPage,
435 localStrings.getString('appDefaultPageName'));
436 } 488 }
437 489
438 $('footer').classList.remove('showing-trash-mode'); 490 $('footer').classList.remove('showing-trash-mode');
439 }, 491 },
440 492
441 /** 493 /**
442 * Callback for the 'pagelayout' event. 494 * Callback for the 'pagelayout' event.
443 * @param {Event} e The event. 495 * @param {Event} e The event.
444 */ 496 */
445 onPageLayout_: function(e) { 497 onPageLayout_: function(e) {
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
500 /** 552 /**
501 * Handler for CARD_CHANGED on cardSlider. 553 * Handler for CARD_CHANGED on cardSlider.
502 * @param {Event} e The CARD_CHANGED event. 554 * @param {Event} e The CARD_CHANGED event.
503 * @private 555 * @private
504 */ 556 */
505 cardChangedHandler_: function(e) { 557 cardChangedHandler_: function(e) {
506 var page = e.cardSlider.currentCardValue; 558 var page = e.cardSlider.currentCardValue;
507 559
508 // Don't change shownPage until startup is done (and page changes actually 560 // Don't change shownPage until startup is done (and page changes actually
509 // reflect user actions). 561 // reflect user actions).
510 if (!document.documentElement.classList.contains('starting-up')) { 562 if (!this.isStartingUp_()) {
511 if (page.classList.contains('apps-page')) { 563 if (page.classList.contains('apps-page')) {
512 this.shownPage = templateData['apps_page_id']; 564 this.shownPage = templateData['apps_page_id'];
513 this.shownPageIndex = this.getAppsPageIndex(page); 565 this.shownPageIndex = this.getAppsPageIndex(page);
514 } else if (page.classList.contains('most-visited-page')) { 566 } else if (page.classList.contains('most-visited-page')) {
515 this.shownPage = templateData['most_visited_page_id']; 567 this.shownPage = templateData['most_visited_page_id'];
516 this.shownPageIndex = 0; 568 this.shownPageIndex = 0;
517 } else { 569 } else {
518 console.error('unknown page selected'); 570 console.error('unknown page selected');
519 } 571 }
520 chrome.send('pageSelected', [this.shownPage, this.shownPageIndex]); 572 chrome.send('pageSelected', [this.shownPage, this.shownPageIndex]);
521 } 573 }
522 574
523 // Update the active dot 575 // Update the active dot
524 var curDot = this.dotList.getElementsByClassName('selected')[0]; 576 var curDot = this.dotList.getElementsByClassName('selected')[0];
525 if (curDot) 577 if (curDot)
526 curDot.classList.remove('selected'); 578 curDot.classList.remove('selected');
527 page.navigationDot.classList.add('selected'); 579 page.navigationDot.classList.add('selected');
528 this.updatePageSwitchers(); 580 this.updatePageSwitchers();
529 }, 581 },
530 582
531 /* 583 /**
532 * Save the name of an app page. 584 * Listen for card additions to update the page switchers or the current
533 * Store the app page name into the preferences store. 585 * card accordingly.
534 * @param {AppsPage} appPage The app page for which we wish to save. 586 * @param {Event} e A card removed or added event.
535 * @param {string} name The name of the page.
536 */ 587 */
537 saveAppPageName: function(appPage, name) { 588 cardAddedHandler_: function(e) {
538 var index = this.getAppsPageIndex(appPage); 589 // When the second arg passed to insertBefore is falsey, it acts just like
539 assert(index != -1); 590 // appendChild.
540 chrome.send('saveAppPageName', [name, index]); 591 this.pageList.insertBefore(e.addedCard, this.tilePages[e.addedIndex]);
592 if (!this.isStartingUp_())
593 this.updatePageSwitchers();
541 }, 594 },
542 595
543 /** 596 /**
597 * Listen for card removals to update the page switchers or the current card
598 * accordingly.
599 * @param {Event} e A card removed or added event.
600 */
601 cardRemovedHandler_: function(e) {
602 if (!this.isStartingUp_())
603 this.updatePageSwitchers();
604 assert(!e.removedCard.classList.contains('selected-card'));
605 e.removedCard.parentNode.removeChild(e.removedCard);
606 },
607
608 /**
609 * Save the name of an apps page.
610 * Store the apps page name into the preferences store.
611 * @param {AppsPage} appsPage The app page for which we wish to save.
612 * @param {string} name The name of the page.
613 * @param {boolean} notify If we should notify of when saving the pref.
614 */
615 saveAppsPageName: function(appsPage, name, notify) {
616 var index = this.getAppsPageIndex(appsPage);
617 assert(index != -1);
618 chrome.send('saveAppsPageName', [name, index, notify]);
619 },
620
621 /**
622 * An Array of callbacks to be called on the next CARD_CHANGE_ENDED event
623 * handled from the cardSlider.
624 * @private
625 */
626 cardChangeEndedCallbacks_: [],
627
628 /**
629 * Handler for CARD_CHANGE_ENDED on cardSlider.
630 * @param {Event} e The CARD_CHANGE_ENDED event.
631 * @private
632 */
633 cardChangeEndedHandler_: function(e) {
634 if (!this.isStartingUp_()) {
635 for (var i = 0; i < this.cardChangeEndedCallbacks_.length; ++i) {
636 if (this.cardChangeEndedCallbacks_[i].call(this, e) !== false)
637 this.cardChangeEndedCallbacks_.splice(i--, 1);
638 }
639 }
640 },
641
642 /**
643 * Happens when a tile is removed from a tile page.
644 * @param {Event} e An event dispatched from a tile when it is removed.
645 */
646 tileRemovedHandler_: function(e) {
647 if (e.tilePage instanceof ntp4.AppsPage)
648 this.removeAppsPageIfEmpty_(e.tilePage, e.wasAnimated);
649 },
650
651 /**
652 * Remove an apps page if it now has no tiles (is empty).
653 * @param {AppsPage} appsPage A page to check for emptiness.
654 * @param {boolean=} opt_animate Whether the prospective removal should be
655 * animated.
656 * @param {boolean=} opt_dontNotify Whether this NTP's AppLauncherHandler
657 * shouldn't be notified of this pages' prospective removal (default is
658 * to notify).
659 * @return {boolean} If |appsPage| was removed or not.
660 */
661 removeAppsPageIfEmpty_: function(appsPage, opt_animate, opt_dontNotify) {
662 assert(appsPage instanceof ntp4.AppsPage,
663 '|appsPage| is not really an AppsPage');
664
665 if (appsPage.tileCount !== 0)
666 return false;
667
668 // If the user is currently on the empty apps page, avoid visual issues
669 // by selecting a different page before deleting. If the user isn't on
670 // the empty/deleting page, just delete it right away.
671 var whenOnCorrectPage = function() {
672 if (!opt_dontNotify)
673 chrome.send('deleteEmptyAppsPage', [appsPage.ordinal]);
674 this.removeTilePageAndDot_(appsPage, opt_animate);
675 };
676
677 if (appsPage == this.cardSlider.currentCardValue) {
678 var index = this.getAppsPageIndex(appsPage);
679 assert(index != -1);
680 var tempPage = document.querySelector('.apps-page.temporary');
681 var tempOffset = tempPage.tileCount == 0 ? 1 : 0;
682 // If the apps page being deleted is the last apps page (excluding soon
683 // to be deleted temp pages), move backward one in the card slider.
684 // Otherwise, move forward one.
685 var change = index == this.appsPages.length - 1 - tempOffset ? -1 : 1;
686 var newIndex = this.cardSlider.currentCard + change;
687 this.cardSlider.selectCard(newIndex, opt_animate);
688 // In the case where |opt_animate| is truthy, the card selection is
689 // animated and asynchronous, so we simply append this callback to
690 // this.cardChangeEndedCallbacks_ to be done on the next animated
691 // CARD_CHANGE_ENDED event that switches to the proposed index.
692 if (opt_animate) {
693 this.cardChangeEndedCallbacks_.push(function(e) {
694 if (!e.wasAnimated || e.changedTo != newIndex)
695 return false;
696 whenOnCorrectPage.call(this);
697 });
698 } else {
699 whenOnCorrectPage.call(this);
700 }
701 } else {
702 whenOnCorrectPage.call(this);
703 }
704
705 return true;
706 },
707
708 /**
544 * Window resize handler. 709 * Window resize handler.
545 * @private 710 * @private
546 */ 711 */
547 onWindowResize_: function(e) { 712 onWindowResize_: function(e) {
548 this.cardSlider.resize(this.sliderFrame.offsetWidth); 713 this.cardSlider.resize(this.sliderFrame.offsetWidth);
549 this.updatePageSwitchers(); 714 this.updatePageSwitchers();
550 }, 715 },
551 716
552 /** 717 /**
553 * Listener for offline status change events. Updates apps that are 718 * Listener for offline status change events. Updates apps that are
(...skipping 27 matching lines...) Expand all
581 direction = 1; 746 direction = 1;
582 else 747 else
583 return; 748 return;
584 749
585 var cardIndex = 750 var cardIndex =
586 (this.cardSlider.currentCard + direction + 751 (this.cardSlider.currentCard + direction +
587 this.cardSlider.cardCount) % this.cardSlider.cardCount; 752 this.cardSlider.cardCount) % this.cardSlider.cardCount;
588 this.cardSlider.selectCard(cardIndex, true); 753 this.cardSlider.selectCard(cardIndex, true);
589 754
590 e.stopPropagation(); 755 e.stopPropagation();
591 } 756 },
757
758 /**
759 * Re-order apps on this inactive page when an active NTP gets re-ordered.
760 * @param {Object} data Position hashmap ordered by app id with indices:
761 * {id => {page_index: ##, app_launch_index: ##}}
762 */
763 appsReordered: function(data) {
764 },
765
766 /**
767 * Ensure there is an apps page at the given |ordinal| by checking for an
768 * existing page or creating a new once.
769 * @param {string} ordinal A string ordinal for which to check.
770 * @private
771 */
772 getOrCreateAppsPageAtOrdinal_: function(ordinal) {
773 var map = this.getAppsPageOrdinalMap_();
774 if (ordinal in map)
775 return map[ordinal];
776
777 var appsPage = new ntp4.AppsPage(ordinal);
778
779 var index = this.getIndexForNewOrdinal_(Object.keys(map));
780 assert(index >= 0 && index <= this.appsPage.length);
781
782 var origPageCount = this.appsPages.length;
783 this.appendTilePage(appsPage,
784 localStrings.getString('appDefaultPageName'),
785 true,
786 this.appsPages[index]);
787 // Confirm that appsPages is a live object, updated when a new page is
788 // added (otherwise we'd have an infinite loop)
789 assert(this.appsPages.length == origPageCount + 1,
790 'expected new page');
791
792 return appsPage;
793 },
794
795 /**
796 * Returns the index of a given tile page.
797 * @param {TilePage} page The TilePage we wish to find.
798 * @return {number} The index of |page| or -1 if it is not in the
799 * collection.
800 */
801 getTilePageIndex: function(page) {
802 return Array.prototype.indexOf.call(this.tilePages, page);
803 },
804
805 /**
806 * Removes a page and navigation dot (if the navdot exists).
807 * @param {TilePage} page The page to be removed.
808 * @param {boolean=} opt_animate If the removal should be animated.
809 */
810 removeTilePageAndDot_: function(page, opt_animate) {
811 if (page.navigationDot)
812 page.navigationDot.remove(opt_animate);
813 this.cardSlider.removeCard(page);
814 },
592 }; 815 };
593 816
594 return { 817 return {
595 PageListView: PageListView 818 PageListView: PageListView
596 }; 819 };
597 }); 820 });
OLDNEW
« no previous file with comments | « chrome/browser/resources/ntp4/new_tab.js ('k') | chrome/browser/resources/ntp4/tile_page.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698