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

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: bug fix + perf. hack, er, optimization 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/nav_dot.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) 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 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
160 160
161 this.tilePages = this.pageList.getElementsByClassName('tile-page'); 161 this.tilePages = this.pageList.getElementsByClassName('tile-page');
162 this.appsPages = this.pageList.getElementsByClassName('apps-page'); 162 this.appsPages = this.pageList.getElementsByClassName('apps-page');
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 events from the card slider.
171 this.pageList.addEventListener( 171 this.pageList.addEventListener('cardSlider:card_changed',
172 cr.ui.CardSlider.EventType.CARD_CHANGED, 172 this.onCardChanged_.bind(this));
173 this.cardChangedHandler_.bind(this)); 173 this.pageList.addEventListener('cardSlider:card_added',
174 this.onCardAdded_.bind(this));
175 this.pageList.addEventListener('cardSlider:card_removed',
176 this.onCardRemoved_.bind(this));
174 177
175 // Ensure the slider is resized appropriately with the window 178 // Ensure the slider is resized appropriately with the window
176 window.addEventListener('resize', this.onWindowResize_.bind(this)); 179 window.addEventListener('resize', this.onWindowResize_.bind(this));
177 180
178 // Update apps when online state changes. 181 // Update apps when online state changes.
179 window.addEventListener('online', 182 window.addEventListener('online',
180 this.updateOfflineEnabledApps_.bind(this)); 183 this.updateOfflineEnabledApps_.bind(this));
181 window.addEventListener('offline', 184 window.addEventListener('offline',
182 this.updateOfflineEnabledApps_.bind(this)); 185 this.updateOfflineEnabledApps_.bind(this));
183 }, 186 },
184 187
185 /** 188 /**
186 * Appends a tile page. 189 * Appends a tile page.
187 * 190 *
188 * @param {TilePage} page The page element. 191 * @param {TilePage} page The page element.
189 * @param {string} title The title of the tile page. 192 * @param {string} title The title of the tile page.
190 * @param {bool} titleIsEditable If true, the title can be changed. 193 * @param {bool} titleIsEditable If true, the title can be changed.
191 * @param {TilePage} opt_refNode Optional reference node to insert in front 194 * @param {TilePage} opt_refNode Optional reference node to insert in front
192 * of. 195 * of.
193 * When opt_refNode is falsey, |page| will just be appended to the end of 196 * When opt_refNode is falsey, |page| will just be appended to the end of
194 * the page list. 197 * the page list.
195 */ 198 */
196 appendTilePage: function(page, title, titleIsEditable, opt_refNode) { 199 appendTilePage: function(page, title, titleIsEditable, opt_refNode) {
197 // When opt_refNode is falsey, insertBefore acts just like appendChild. 200 if (opt_refNode) {
198 this.pageList.insertBefore(page, opt_refNode); 201 var refIndex = this.getTilePageIndex(opt_refNode);
202 this.cardSlider.insertCardAtIndex(page, refIndex);
203 } else {
204 this.cardSlider.appendCard(page);
205 }
199 206
200 // Remember special MostVisitedPage. 207 // Remember special MostVisitedPage.
201 if (typeof ntp4.MostVisitedPage != 'undefined' && 208 if (typeof ntp4.MostVisitedPage != 'undefined' &&
202 page instanceof ntp4.MostVisitedPage) { 209 page instanceof ntp4.MostVisitedPage) {
203 assert(this.tilePages.length == 1, 210 assert(this.tilePages.length == 1,
204 'MostVisitedPage should be added as first tile page'); 211 'MostVisitedPage should be added as first tile page');
205 this.mostVisitedPage = page; 212 this.mostVisitedPage = page;
206 } 213 }
207 214
208 // If we're appending an AppsPage and it's a temporary page, animate it. 215 // 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)); 227 this.eventTracker.add(page, 'pagelayout', this.onPageLayout_.bind(this));
221 }, 228 },
222 229
223 /** 230 /**
224 * Called by chrome when an existing app has been disabled or 231 * Called by chrome when an existing app has been disabled or
225 * removed/uninstalled from chrome. 232 * removed/uninstalled from chrome.
226 * @param {Object} appData A data structure full of relevant information for 233 * @param {Object} appData A data structure full of relevant information for
227 * the app. 234 * the app.
228 * @param {boolean} isUninstall True if the app is being uninstalled; 235 * @param {boolean} isUninstall True if the app is being uninstalled;
229 * false if the app is being disabled. 236 * false if the app is being disabled.
237 * @param {boolean} fromPage True if the removal was from the current page.
230 */ 238 */
231 appRemoved: function(appData, isUninstall) { 239 appRemoved: function(appData, isUninstall, fromPage) {
232 var app = $(appData.id); 240 var app = $(appData.id);
233 assert(app, 'trying to remove an app that doesn\'t exist'); 241 assert(app, 'trying to remove an app that doesn\'t exist');
234 242
235 if (!isUninstall) 243 if (!isUninstall)
236 app.replaceAppData(appData); 244 app.replaceAppData(appData);
237 else 245 else
238 app.remove(); 246 app.remove(!!fromPage);
239 }, 247 },
240 248
241 /** 249 /**
250 * @return {boolean} If the page is still starting up.
251 * @private
252 */
253 isStartingUp_: function() {
254 return document.documentElement.classList.contains('starting-up');
255 },
256
257 /**
242 * Callback invoked by chrome with the apps available. 258 * Callback invoked by chrome with the apps available.
243 * 259 *
244 * Note that calls to this function can occur at any time, not just in 260 * 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 261 * response to a getApps request. For example, when a user
246 * installs/uninstalls an app on another synchronized devices. 262 * installs/uninstalls an app on another synchronized devices.
247 * @param {Object} data An object with all the data on available 263 * @param {Object} data An object with all the data on available
248 * applications. 264 * applications.
249 */ 265 */
250 getAppsCallback: function(data) { 266 getAppsCallback: function(data) {
251 var startTime = Date.now(); 267 var startTime = Date.now();
252 268
269 // Remember this to select the correct card when rebuilding.
270 var prevCurrentCard = this.cardSlider.currentCard;
271
272 // HACK(dbeam): Make removal of pages and dots as quick as possible with
Evan Stade 2012/01/19 02:13:06 not really a hack, I'd just remove the HACK(dbeam)
273 // less DOM operations. We set currentCard = 0 and remove from the end to
274 // not encounter any auto-magic card selections in the process.
275 this.cardSlider.currentCard = 0;
276
253 // Clear any existing apps pages and dots. 277 // Clear any existing apps pages and dots.
254 // TODO(rbyers): It might be nice to preserve animation of dots after an 278 // 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 279 // 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 280 // seems unfortunate to have Chrome send us the entire apps list after an
257 // uninstall. 281 // uninstall.
258 while (this.appsPages.length > 0) { 282 while (this.appsPages.length > 0)
259 var page = this.appsPages[0]; 283 this.removeTilePageAndDot_(this.appsPages[this.appsPages.length - 1]);
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 284
268 // Get the array of apps and add any special synthesized entries 285 // Get the array of apps and add any special synthesized entries
269 var apps = data.apps; 286 var apps = data.apps;
270 287
271 // Get a list of page names 288 // Get a list of page names
272 var pageNames = data.appPageNames; 289 var pageNames = data.appPageNames;
273 290
274 function stringListIsEmpty(list) { 291 function stringListIsEmpty(list) {
275 for (var i = 0; i < list.length; i++) { 292 for (var i = 0; i < list.length; i++) {
276 if (list[i]) 293 if (list[i])
(...skipping 29 matching lines...) Expand all
306 } 323 }
307 324
308 if (app.id == this.highlightAppId) 325 if (app.id == this.highlightAppId)
309 highlightApp = app; 326 highlightApp = app;
310 else 327 else
311 this.appsPages[pageIndex].appendApp(app); 328 this.appsPages[pageIndex].appendApp(app);
312 } 329 }
313 330
314 ntp4.AppsPage.setPromo(data.showPromo ? data : null); 331 ntp4.AppsPage.setPromo(data.showPromo ? data : null);
315 332
333 this.cardSlider.currentCard = prevCurrentCard;
334
316 // Tell the slider about the pages. 335 // Tell the slider about the pages.
317 this.updateSliderCards(); 336 this.updateSliderCards();
318 337
319 if (highlightApp) 338 if (highlightApp)
320 this.appAdded(highlightApp, true); 339 this.appAdded(highlightApp, true);
321 340
322 // Mark the current page. 341 // Mark the current page.
323 this.cardSlider.currentCardValue.navigationDot.classList.add('selected'); 342 this.cardSlider.currentCardValue.navigationDot.classList.add('selected');
324 logEvent('apps.layout: ' + (Date.now() - startTime)); 343 logEvent('apps.layout: ' + (Date.now() - startTime));
325 344
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
373 for (var i = start; i < dots.length; ++i) { 392 for (var i = start; i < dots.length; ++i) {
374 dots[i].displayTitle = data.appPageNames[i - start] || ''; 393 dots[i].displayTitle = data.appPageNames[i - start] || '';
375 } 394 }
376 }, 395 },
377 396
378 /** 397 /**
379 * Invoked whenever the pages in apps-page-list have changed so that 398 * Invoked whenever the pages in apps-page-list have changed so that
380 * the Slider knows about the new elements. 399 * the Slider knows about the new elements.
381 */ 400 */
382 updateSliderCards: function() { 401 updateSliderCards: function() {
383 var pageNo = Math.min(this.cardSlider.currentCard, 402 var pageNo = Math.max(0, Math.min(this.cardSlider.currentCard,
384 this.tilePages.length - 1); 403 this.tilePages.length - 1));
385 this.cardSlider.setCards(Array.prototype.slice.call(this.tilePages), 404 this.cardSlider.setCards(Array.prototype.slice.call(this.tilePages),
386 pageNo); 405 pageNo);
387 switch (this.shownPage) { 406 switch (this.shownPage) {
388 case templateData['apps_page_id']: 407 case templateData['apps_page_id']:
389 this.cardSlider.selectCardByValue( 408 this.cardSlider.selectCardByValue(
390 this.appsPages[Math.min(this.shownPageIndex, 409 this.appsPages[Math.min(this.shownPageIndex,
391 this.appsPages.length - 1)]); 410 this.appsPages.length - 1)]);
392 break; 411 break;
393 case templateData['most_visited_page_id']: 412 case templateData['most_visited_page_id']:
394 if (this.mostVisitedPage) 413 if (this.mostVisitedPage)
395 this.cardSlider.selectCardByValue(this.mostVisitedPage); 414 this.cardSlider.selectCardByValue(this.mostVisitedPage);
396 break; 415 break;
397 } 416 }
398 }, 417 },
399 418
400 /** 419 /**
401 * Called whenever tiles should be re-arranging themselves out of the way 420 * Called whenever tiles should be re-arranging themselves out of the way
402 * of a moving or insert tile. 421 * of a moving or insert tile.
403 */ 422 */
404 enterRearrangeMode: function() { 423 enterRearrangeMode: function() {
405 var tempPage = new ntp4.AppsPage(); 424 var tempPage = new ntp4.AppsPage();
406 tempPage.classList.add('temporary'); 425 tempPage.classList.add('temporary');
407 this.appendTilePage(tempPage, 426 var pageName = localStrings.getString('appDefaultPageName');
408 localStrings.getString('appDefaultPageName'), 427 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 428
415 if (ntp4.getCurrentlyDraggingTile().firstChild.canBeRemoved()) 429 if (ntp4.getCurrentlyDraggingTile().firstChild.canBeRemoved())
416 $('footer').classList.add('showing-trash-mode'); 430 $('footer').classList.add('showing-trash-mode');
417 }, 431 },
418 432
419 /** 433 /**
420 * Invoked whenever some app is released 434 * Invoked whenever some app is released
421 */ 435 */
422 leaveRearrangeMode: function() { 436 leaveRearrangeMode: function() {
423 var tempPage = document.querySelector('.tile-page.temporary'); 437 var tempPage = document.querySelector('.tile-page.temporary');
424 var dot = tempPage.navigationDot; 438 var dot = tempPage.navigationDot;
425 if (!tempPage.tileCount && tempPage != this.cardSlider.currentCardValue) { 439 if (!tempPage.tileCount && tempPage != this.cardSlider.currentCardValue) {
426 dot.animateRemove(); 440 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 { 441 } else {
433 tempPage.classList.remove('temporary'); 442 tempPage.classList.remove('temporary');
434 this.saveAppPageName(tempPage, 443 this.saveAppPageName(tempPage,
435 localStrings.getString('appDefaultPageName')); 444 localStrings.getString('appDefaultPageName'));
436 } 445 }
437 446
438 $('footer').classList.remove('showing-trash-mode'); 447 $('footer').classList.remove('showing-trash-mode');
439 }, 448 },
440 449
441 /** 450 /**
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
492 * Returns the index of the given apps page. 501 * Returns the index of the given apps page.
493 * @param {AppsPage} page The AppsPage we wish to find. 502 * @param {AppsPage} page The AppsPage we wish to find.
494 * @return {number} The index of |page| or -1 if it is not in the 503 * @return {number} The index of |page| or -1 if it is not in the
495 * collection. 504 * collection.
496 */ 505 */
497 getAppsPageIndex: function(page) { 506 getAppsPageIndex: function(page) {
498 return Array.prototype.indexOf.call(this.appsPages, page); 507 return Array.prototype.indexOf.call(this.appsPages, page);
499 }, 508 },
500 509
501 /** 510 /**
502 * Handler for CARD_CHANGED on cardSlider. 511 * Handler for cardSlider:card_changed events from this.cardSlider.
503 * @param {Event} e The CARD_CHANGED event. 512 * @param {Event} e The cardSlider:card_changed event.
504 * @private 513 * @private
505 */ 514 */
506 cardChangedHandler_: function(e) { 515 onCardChanged_: function(e) {
507 var page = e.cardSlider.currentCardValue; 516 var page = e.cardSlider.currentCardValue;
508 517
509 // Don't change shownPage until startup is done (and page changes actually 518 // Don't change shownPage until startup is done (and page changes actually
510 // reflect user actions). 519 // reflect user actions).
511 if (!document.documentElement.classList.contains('starting-up')) { 520 if (!this.isStartingUp_()) {
512 if (page.classList.contains('apps-page')) { 521 if (page.classList.contains('apps-page')) {
513 this.shownPage = templateData.apps_page_id; 522 this.shownPage = templateData.apps_page_id;
514 this.shownPageIndex = this.getAppsPageIndex(page); 523 this.shownPageIndex = this.getAppsPageIndex(page);
515 } else if (page.classList.contains('most-visited-page')) { 524 } else if (page.classList.contains('most-visited-page')) {
516 this.shownPage = templateData.most_visited_page_id; 525 this.shownPage = templateData.most_visited_page_id;
517 this.shownPageIndex = 0; 526 this.shownPageIndex = 0;
518 } else { 527 } else {
519 console.error('unknown page selected'); 528 console.error('unknown page selected');
520 } 529 }
521 chrome.send('pageSelected', [this.shownPage, this.shownPageIndex]); 530 chrome.send('pageSelected', [this.shownPage, this.shownPageIndex]);
522 } 531 }
523 532
524 // Update the active dot 533 // Update the active dot
525 var curDot = this.dotList.getElementsByClassName('selected')[0]; 534 var curDot = this.dotList.getElementsByClassName('selected')[0];
526 if (curDot) 535 if (curDot)
527 curDot.classList.remove('selected'); 536 curDot.classList.remove('selected');
528 page.navigationDot.classList.add('selected'); 537 page.navigationDot.classList.add('selected');
529 this.updatePageSwitchers(); 538 this.updatePageSwitchers();
530 }, 539 },
531 540
532 /* 541 /**
533 * Save the name of an app page. 542 * Listen for card additions to update the page switchers or the current
534 * Store the app page name into the preferences store. 543 * card accordingly.
535 * @param {AppsPage} appPage The app page for which we wish to save. 544 * @param {Event} e A card removed or added event.
545 */
546 onCardAdded_: function(e) {
547 // When the second arg passed to insertBefore is falsey, it acts just like
548 // appendChild.
549 this.pageList.insertBefore(e.addedCard, this.tilePages[e.addedIndex]);
550 if (!this.isStartingUp_())
551 this.updatePageSwitchers();
552 },
553
554 /**
555 * Listen for card removals to update the page switchers or the current card
556 * accordingly.
557 * @param {Event} e A card removed or added event.
558 */
559 onCardRemoved_: function(e) {
560 e.removedCard.parentNode.removeChild(e.removedCard);
561 if (!this.isStartingUp_())
562 this.updatePageSwitchers();
563 },
564
565 /**
566 * Save the name of an apps page.
567 * Store the apps page name into the preferences store.
568 * @param {AppsPage} appsPage The app page for which we wish to save.
536 * @param {string} name The name of the page. 569 * @param {string} name The name of the page.
537 */ 570 */
538 saveAppPageName: function(appPage, name) { 571 saveAppPageName: function(appPage, name) {
539 var index = this.getAppsPageIndex(appPage); 572 var index = this.getAppsPageIndex(appPage);
540 assert(index != -1); 573 assert(index != -1);
541 chrome.send('saveAppPageName', [name, index]); 574 chrome.send('saveAppPageName', [name, index]);
542 }, 575 },
543 576
544 /** 577 /**
545 * Window resize handler. 578 * Window resize handler.
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
582 direction = 1; 615 direction = 1;
583 else 616 else
584 return; 617 return;
585 618
586 var cardIndex = 619 var cardIndex =
587 (this.cardSlider.currentCard + direction + 620 (this.cardSlider.currentCard + direction +
588 this.cardSlider.cardCount) % this.cardSlider.cardCount; 621 this.cardSlider.cardCount) % this.cardSlider.cardCount;
589 this.cardSlider.selectCard(cardIndex, true); 622 this.cardSlider.selectCard(cardIndex, true);
590 623
591 e.stopPropagation(); 624 e.stopPropagation();
592 } 625 },
626
627 /**
628 * Returns the index of a given tile page.
629 * @param {TilePage} page The TilePage we wish to find.
630 * @return {number} The index of |page| or -1 if it is not in the
631 * collection.
632 */
633 getTilePageIndex: function(page) {
634 return Array.prototype.indexOf.call(this.tilePages, page);
635 },
636
637 /**
638 * Removes a page and navigation dot (if the navdot exists).
639 * @param {TilePage} page The page to be removed.
640 * @param {boolean=} opt_animate If the removal should be animated.
641 */
642 removeTilePageAndDot_: function(page, opt_animate) {
643 if (page.navigationDot)
644 page.navigationDot.remove(opt_animate);
645 this.cardSlider.removeCard(page);
646 },
593 }; 647 };
594 648
595 return { 649 return {
596 PageListView: PageListView 650 PageListView: PageListView
597 }; 651 };
598 }); 652 });
OLDNEW
« no previous file with comments | « chrome/browser/resources/ntp4/nav_dot.js ('k') | chrome/browser/resources/ntp4/tile_page.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698