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

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

Issue 9116037: [NTP4] Make TilePage and CardSlider evented to simplify code and fix page switcher bug (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase + event order change 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
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 152 matching lines...) Expand 10 before | Expand all | Expand 10 after
163 163
164 // Initialize the cardSlider without any cards at the moment 164 // Initialize the cardSlider without any cards at the moment
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.onCardChanged_.bind(this));
174
175 // Handle cards being added to the card slider.
176 this.pageList.addEventListener(
177 cr.ui.CardSlider.EventType.CARD_ADDED,
178 this.onCardAdded_.bind(this));
179
180 // Handle cards being removed from the card slider.
181 this.pageList.addEventListener(
182 cr.ui.CardSlider.EventType.CARD_REMOVED,
183 this.onCardRemoved_.bind(this));
184
185 // Handle tiles being removed from tile pages.
186 this.pageList.addEventListener(
187 ntp4.TilePage.EventType.TILE_REMOVED,
188 this.onTileRemoved_.bind(this));
174 189
175 // Ensure the slider is resized appropriately with the window 190 // Ensure the slider is resized appropriately with the window
176 window.addEventListener('resize', this.onWindowResize_.bind(this)); 191 window.addEventListener('resize', this.onWindowResize_.bind(this));
177 192
178 // Update apps when online state changes. 193 // Update apps when online state changes.
179 window.addEventListener('online', 194 window.addEventListener('online',
180 this.updateOfflineEnabledApps_.bind(this)); 195 this.updateOfflineEnabledApps_.bind(this));
181 window.addEventListener('offline', 196 window.addEventListener('offline',
182 this.updateOfflineEnabledApps_.bind(this)); 197 this.updateOfflineEnabledApps_.bind(this));
183 }, 198 },
184 199
185 /** 200 /**
186 * Appends a tile page. 201 * Appends a tile page.
187 * 202 *
188 * @param {TilePage} page The page element. 203 * @param {TilePage} page The page element.
189 * @param {string} title The title of the tile page. 204 * @param {string} title The title of the tile page.
190 * @param {bool} titleIsEditable If true, the title can be changed. 205 * @param {bool} titleIsEditable If true, the title can be changed.
191 * @param {TilePage} opt_refNode Optional reference node to insert in front 206 * @param {TilePage} opt_refNode Optional reference node to insert in front
192 * of. 207 * of.
193 * When opt_refNode is falsey, |page| will just be appended to the end of 208 * When opt_refNode is falsey, |page| will just be appended to the end of
194 * the page list. 209 * the page list.
195 */ 210 */
196 appendTilePage: function(page, title, titleIsEditable, opt_refNode) { 211 appendTilePage: function(page, title, titleIsEditable, opt_refNode) {
197 // When opt_refNode is falsey, insertBefore acts just like appendChild. 212 if (opt_refNode) {
198 this.pageList.insertBefore(page, opt_refNode); 213 var refIndex = this.getTilePageIndex(opt_refNode);
214 this.cardSlider.insertCardAtIndex(page, refIndex);
215 } else {
216 this.cardSlider.appendCard(page);
217 }
199 218
200 // Remember special MostVisitedPage. 219 // Remember special MostVisitedPage.
201 if (typeof ntp4.MostVisitedPage != 'undefined' && 220 if (typeof ntp4.MostVisitedPage != 'undefined' &&
202 page instanceof ntp4.MostVisitedPage) { 221 page instanceof ntp4.MostVisitedPage) {
203 assert(this.tilePages.length == 1, 222 assert(this.tilePages.length == 1,
204 'MostVisitedPage should be added as first tile page'); 223 'MostVisitedPage should be added as first tile page');
205 this.mostVisitedPage = page; 224 this.mostVisitedPage = page;
206 } 225 }
207 226
208 // If we're appending an AppsPage and it's a temporary page, animate it. 227 // 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)); 239 this.eventTracker.add(page, 'pagelayout', this.onPageLayout_.bind(this));
221 }, 240 },
222 241
223 /** 242 /**
224 * Called by chrome when an existing app has been disabled or 243 * Called by chrome when an existing app has been disabled or
225 * removed/uninstalled from chrome. 244 * removed/uninstalled from chrome.
226 * @param {Object} appData A data structure full of relevant information for 245 * @param {Object} appData A data structure full of relevant information for
227 * the app. 246 * the app.
228 * @param {boolean} isUninstall True if the app is being uninstalled; 247 * @param {boolean} isUninstall True if the app is being uninstalled;
229 * false if the app is being disabled. 248 * false if the app is being disabled.
249 * @param {boolean} fromPage True if the removal was from the current page.
230 */ 250 */
231 appRemoved: function(appData, isUninstall) { 251 appRemoved: function(appData, isUninstall, fromPage) {
232 var app = $(appData.id); 252 var app = $(appData.id);
233 assert(app, 'trying to remove an app that doesn\'t exist'); 253 assert(app, 'trying to remove an app that doesn\'t exist');
234 254
235 if (!isUninstall) 255 if (!isUninstall)
236 app.replaceAppData(appData); 256 app.replaceAppData(appData);
237 else 257 else
238 app.remove(); 258 app.remove(!!fromPage);
239 }, 259 },
240 260
241 /** 261 /**
262 * @return {boolean} If the page is still starting up.
263 * @private
264 */
265 isStartingUp_: function() {
266 return document.documentElement.classList.contains('starting-up');
267 },
268
269 /**
242 * Callback invoked by chrome with the apps available. 270 * Callback invoked by chrome with the apps available.
243 * 271 *
244 * Note that calls to this function can occur at any time, not just in 272 * 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 273 * response to a getApps request. For example, when a user
246 * installs/uninstalls an app on another synchronized devices. 274 * installs/uninstalls an app on another synchronized devices.
247 * @param {Object} data An object with all the data on available 275 * @param {Object} data An object with all the data on available
248 * applications. 276 * applications.
249 */ 277 */
250 getAppsCallback: function(data) { 278 getAppsCallback: function(data) {
251 var startTime = Date.now(); 279 var startTime = Date.now();
252 280
253 // Clear any existing apps pages and dots. 281 // Clear any existing apps pages and dots.
254 // TODO(rbyers): It might be nice to preserve animation of dots after an 282 // 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 283 // 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 284 // seems unfortunate to have Chrome send us the entire apps list after an
257 // uninstall. 285 // uninstall.
258 while (this.appsPages.length > 0) { 286 while (this.appsPages.length > 0)
259 var page = this.appsPages[0]; 287 this.removeTilePageAndDot_(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 288
268 // Get the array of apps and add any special synthesized entries 289 // Get the array of apps and add any special synthesized entries
269 var apps = data.apps; 290 var apps = data.apps;
270 291
271 // Get a list of page names 292 // Get a list of page names
272 var pageNames = data.appPageNames; 293 var pageNames = data.appPageNames;
273 294
274 function stringListIsEmpty(list) { 295 function stringListIsEmpty(list) {
275 for (var i = 0; i < list.length; i++) { 296 for (var i = 0; i < list.length; i++) {
276 if (list[i]) 297 if (list[i])
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
397 } 418 }
398 }, 419 },
399 420
400 /** 421 /**
401 * Called whenever tiles should be re-arranging themselves out of the way 422 * Called whenever tiles should be re-arranging themselves out of the way
402 * of a moving or insert tile. 423 * of a moving or insert tile.
403 */ 424 */
404 enterRearrangeMode: function() { 425 enterRearrangeMode: function() {
405 var tempPage = new ntp4.AppsPage(); 426 var tempPage = new ntp4.AppsPage();
406 tempPage.classList.add('temporary'); 427 tempPage.classList.add('temporary');
407 this.appendTilePage(tempPage, 428 var pageName = localStrings.getString('appDefaultPageName');
408 localStrings.getString('appDefaultPageName'), 429 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 430
415 if (ntp4.getCurrentlyDraggingTile().firstChild.canBeRemoved()) 431 if (ntp4.getCurrentlyDraggingTile().firstChild.canBeRemoved())
416 $('footer').classList.add('showing-trash-mode'); 432 $('footer').classList.add('showing-trash-mode');
417 }, 433 },
418 434
419 /** 435 /**
420 * Invoked whenever some app is released 436 * Invoked whenever some app is released
421 */ 437 */
422 leaveRearrangeMode: function() { 438 leaveRearrangeMode: function() {
423 var tempPage = document.querySelector('.tile-page.temporary'); 439 var tempPage = document.querySelector('.tile-page.temporary');
424 var dot = tempPage.navigationDot; 440 var dot = tempPage.navigationDot;
425 if (!tempPage.tileCount && tempPage != this.cardSlider.currentCardValue) { 441 if (!tempPage.tileCount && tempPage != this.cardSlider.currentCardValue) {
426 dot.animateRemove(); 442 this.removeTilePageAndDot_(tempPage, true);
427 var tempIndex = Array.prototype.indexOf.call(this.tilePages, tempPage);
428 if (this.cardSlider.currentCard > tempIndex)
429 this.cardSlider.currentCard -= 1;
430 tempPage.parentNode.removeChild(tempPage);
431 this.updateSliderCards();
432 } else { 443 } else {
433 tempPage.classList.remove('temporary'); 444 tempPage.classList.remove('temporary');
434 this.saveAppPageName(tempPage, 445 this.saveAppPageName(tempPage,
435 localStrings.getString('appDefaultPageName')); 446 localStrings.getString('appDefaultPageName'));
436 } 447 }
437 448
438 $('footer').classList.remove('showing-trash-mode'); 449 $('footer').classList.remove('showing-trash-mode');
439 }, 450 },
440 451
441 /** 452 /**
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
496 */ 507 */
497 getAppsPageIndex: function(page) { 508 getAppsPageIndex: function(page) {
498 return Array.prototype.indexOf.call(this.appsPages, page); 509 return Array.prototype.indexOf.call(this.appsPages, page);
499 }, 510 },
500 511
501 /** 512 /**
502 * Handler for CARD_CHANGED on cardSlider. 513 * Handler for CARD_CHANGED on cardSlider.
503 * @param {Event} e The CARD_CHANGED event. 514 * @param {Event} e The CARD_CHANGED event.
504 * @private 515 * @private
505 */ 516 */
506 cardChangedHandler_: function(e) { 517 onCardChanged_: function(e) {
507 var page = e.cardSlider.currentCardValue; 518 var page = e.cardSlider.currentCardValue;
508 519
509 // Don't change shownPage until startup is done (and page changes actually 520 // Don't change shownPage until startup is done (and page changes actually
510 // reflect user actions). 521 // reflect user actions).
511 if (!document.documentElement.classList.contains('starting-up')) { 522 if (!this.isStartingUp_()) {
512 if (page.classList.contains('apps-page')) { 523 if (page.classList.contains('apps-page')) {
513 this.shownPage = templateData.apps_page_id; 524 this.shownPage = templateData.apps_page_id;
514 this.shownPageIndex = this.getAppsPageIndex(page); 525 this.shownPageIndex = this.getAppsPageIndex(page);
515 } else if (page.classList.contains('most-visited-page')) { 526 } else if (page.classList.contains('most-visited-page')) {
516 this.shownPage = templateData.most_visited_page_id; 527 this.shownPage = templateData.most_visited_page_id;
517 this.shownPageIndex = 0; 528 this.shownPageIndex = 0;
518 } else { 529 } else {
519 console.error('unknown page selected'); 530 console.error('unknown page selected');
520 } 531 }
521 chrome.send('pageSelected', [this.shownPage, this.shownPageIndex]); 532 chrome.send('pageSelected', [this.shownPage, this.shownPageIndex]);
522 } 533 }
523 534
524 // Update the active dot 535 // Update the active dot
525 var curDot = this.dotList.getElementsByClassName('selected')[0]; 536 var curDot = this.dotList.getElementsByClassName('selected')[0];
526 if (curDot) 537 if (curDot)
527 curDot.classList.remove('selected'); 538 curDot.classList.remove('selected');
528 page.navigationDot.classList.add('selected'); 539 page.navigationDot.classList.add('selected');
529 this.updatePageSwitchers(); 540 this.updatePageSwitchers();
530 }, 541 },
531 542
532 /* 543 /**
533 * Save the name of an app page. 544 * Listen for card additions to update the page switchers or the current
534 * Store the app page name into the preferences store. 545 * card accordingly.
535 * @param {AppsPage} appPage The app page for which we wish to save. 546 * @param {Event} e A card removed or added event.
547 */
548 onCardAdded_: function(e) {
549 // When the second arg passed to insertBefore is falsey, it acts just like
550 // appendChild.
551 this.pageList.insertBefore(e.addedCard, this.tilePages[e.addedIndex]);
552 if (!this.isStartingUp_())
553 this.updatePageSwitchers();
554 },
555
556 /**
557 * Listen for card removals to update the page switchers or the current card
558 * accordingly.
559 * @param {Event} e A card removed or added event.
560 */
561 onCardRemoved_: function(e) {
562 e.removedCard.parentNode.removeChild(e.removedCard);
563 if (!this.isStartingUp_())
564 this.updatePageSwitchers();
565 },
566
567 /**
568 * Save the name of an apps page.
569 * Store the apps page name into the preferences store.
570 * @param {AppsPage} appsPage The app page for which we wish to save.
536 * @param {string} name The name of the page. 571 * @param {string} name The name of the page.
537 */ 572 */
538 saveAppPageName: function(appPage, name) { 573 saveAppPageName: function(appPage, name) {
539 var index = this.getAppsPageIndex(appPage); 574 var index = this.getAppsPageIndex(appPage);
540 assert(index != -1); 575 assert(index != -1);
541 chrome.send('saveAppPageName', [name, index]); 576 chrome.send('saveAppPageName', [name, index]);
542 }, 577 },
543 578
544 /** 579 /**
545 * Window resize handler. 580 * Window resize handler.
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
582 direction = 1; 617 direction = 1;
583 else 618 else
584 return; 619 return;
585 620
586 var cardIndex = 621 var cardIndex =
587 (this.cardSlider.currentCard + direction + 622 (this.cardSlider.currentCard + direction +
588 this.cardSlider.cardCount) % this.cardSlider.cardCount; 623 this.cardSlider.cardCount) % this.cardSlider.cardCount;
589 this.cardSlider.selectCard(cardIndex, true); 624 this.cardSlider.selectCard(cardIndex, true);
590 625
591 e.stopPropagation(); 626 e.stopPropagation();
592 } 627 },
628
629 /**
630 * Happens when a tile is removed from a tile page.
631 * @param {Event} e An event dispatched from a tile when it is removed.
632 */
633 onTileRemoved_: function(e) {
634 // TODO(dbeam): Remove empty apps pages.
635 this.updateSliderCards();
636 },
637
638 /**
639 * Returns the index of a given tile page.
640 * @param {TilePage} page The TilePage we wish to find.
641 * @return {number} The index of |page| or -1 if it is not in the
642 * collection.
643 */
644 getTilePageIndex: function(page) {
645 return Array.prototype.indexOf.call(this.tilePages, page);
646 },
647
648 /**
649 * Removes a page and navigation dot (if the navdot exists).
650 * @param {TilePage} page The page to be removed.
651 * @param {boolean=} opt_animate If the removal should be animated.
652 */
653 removeTilePageAndDot_: function(page, opt_animate) {
654 if (page.navigationDot)
655 page.navigationDot.remove(opt_animate);
656 this.cardSlider.removeCard(page);
657 },
593 }; 658 };
594 659
595 return { 660 return {
596 PageListView: PageListView 661 PageListView: PageListView
597 }; 662 };
598 }); 663 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698