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