Index: chrome/browser/resources/ntp4/page_list_view.js |
diff --git a/chrome/browser/resources/ntp4/page_list_view.js b/chrome/browser/resources/ntp4/page_list_view.js |
index f6da56bd1f1ac8747143513b259219f72dc04fe6..c36ff8b83ab0b50d273d806258b03c93795574ee 100644 |
--- a/chrome/browser/resources/ntp4/page_list_view.js |
+++ b/chrome/browser/resources/ntp4/page_list_view.js |
@@ -172,6 +172,26 @@ cr.define('ntp4', function() { |
cr.ui.CardSlider.EventType.CARD_CHANGED, |
this.cardChangedHandler_.bind(this)); |
+ // Handle the end of cards being changed (useful if animated). |
+ this.pageList.addEventListener( |
+ cr.ui.CardSlider.EventType.CARD_CHANGE_ENDED, |
+ this.cardChangeEndedHandler_.bind(this)); |
+ |
+ // Handle cards being added to the card slider. |
+ this.pageList.addEventListener( |
+ cr.ui.CardSlider.EventType.CARD_ADDED, |
+ this.cardAddedHandler_.bind(this)); |
+ |
+ // Handle cards being removed from the card slider. |
+ this.pageList.addEventListener( |
+ cr.ui.CardSlider.EventType.CARD_REMOVED, |
+ this.cardRemovedHandler_.bind(this)); |
+ |
+ // Handle tiles being removed from tile pages. |
+ this.pageList.addEventListener( |
+ ntp4.TilePage.EventType.TILE_REMOVED, |
+ this.tileRemovedHandler_.bind(this)); |
+ |
// Ensure the slider is resized appropriately with the window |
window.addEventListener('resize', this.onWindowResize_.bind(this)); |
@@ -194,8 +214,12 @@ cr.define('ntp4', function() { |
* the page list. |
*/ |
appendTilePage: function(page, title, titleIsEditable, opt_refNode) { |
- // When opt_refNode is falsey, insertBefore acts just like appendChild. |
- this.pageList.insertBefore(page, opt_refNode); |
+ if (opt_refNode) { |
+ var refIndex = this.getTilePageIndex(opt_refNode); |
+ this.cardSlider.insertCardAtIndex(page, refIndex); |
+ } else { |
+ this.cardSlider.appendCard(page); |
+ } |
// Remember special MostVisitedPage. |
if (typeof ntp4.MostVisitedPage != 'undefined' && |
@@ -227,15 +251,101 @@ cr.define('ntp4', function() { |
* the app. |
* @param {boolean} isUninstall True if the app is being uninstalled; |
* false if the app is being disabled. |
+ * @param {boolean} fromPage If the removal was from the current page. |
*/ |
- appRemoved: function(appData, isUninstall) { |
+ appRemoved: function(appData, isUninstall, fromPage) { |
var app = $(appData.id); |
assert(app, 'trying to remove an app that doesn\'t exist'); |
- if (!isUninstall) |
+ if (!isUninstall) { |
app.replaceAppData(appData); |
- else |
- app.remove(); |
+ } else { |
+ // If the uninstall was from this page, run the blipout animation and |
+ // the tile will be removed in TilePage#onContentsAnimationEnd_. |
+ // Otherwise delete the tile without auto-deleting the page to avoid |
+ // re-deleting the same page (or the page that slid in to take its |
+ // place). |
+ if (fromPage) { |
+ // Unset the ID immediately, because the app is already gone. But |
+ // leave the tile on the page as it animates out. |
+ app.id = ''; |
+ app.classList.add('removing-tile-contents'); |
+ } else { |
+ var tilePage = app.tile.tilePage; |
+ tilePage.removeTile(app.tile, false, true); |
+ this.removeAppsPageIfEmpty_(tilePage, false, true); |
+ } |
+ } |
+ }, |
+ |
+ /** |
+ * @return {boolean} If the page is still starting up. |
+ * @private |
+ */ |
+ isStartingUp_: function() { |
+ return document.documentElement.classList.contains('starting-up'); |
+ }, |
+ |
+ /** |
+ * Returns a hashmap of apps pages keyed by page ordinal. |
+ * @return {Object.<string, AppsPage>} Map of apps pages. |
+ * @private |
+ */ |
+ getAppsPageOrdinalMap_: function() { |
+ var map = {}; |
+ for (var i = 0; i < this.appsPages.length; ++i) |
+ map[this.appsPages[i].ordinal] = this.appsPages[i]; |
+ return map; |
+ }, |
+ |
+ /** |
+ * Get an unsorted list of apps page ordinals. |
+ */ |
+ getAppsPageOrdinals_: function() { |
+ return Array.prototype.map.call(this.appsPages, function(page) { |
+ return page.ordinal; |
+ }); |
+ }, |
+ |
+ /** |
+ * Gets an apps page by string ordinal. |
+ * @param {string} ordinal The page ordinal we're searching for. |
+ * @return {?AppsPage} The apps page with corresponding ordinal or null. |
+ * @private |
+ */ |
+ getAppsPageByOrdinal_: function(ordinal) { |
+ assert(typeof ordinal == 'string' && ordinal); |
+ return this.getAppsPageOrdinalMap_()[ordinal] || null; |
+ }, |
+ |
+ /** |
+ * Find an apps page index via ordinal. |
+ * @return {number} The index of the ordinal or -1 if it doesn't exist. |
+ */ |
+ getPageIndexFromOrdinal_: function(ordinal) { |
+ return this.getAppsPageOrdinals_().indexOf(ordinal); |
+ }, |
+ |
+ /** |
+ * Finds a position for a specific ordinal in an ordinal map. |
+ * @param {Array} ordinalList An array of ordinals. |
+ * @param {string} ordinal A specific ordinal to search for. |
+ * @return {number} The position where the ordinal should be positioned. |
+ */ |
+ getPositionForNewOrdinal_: function(ordinalList, ordinal) { |
+ assert(Array.isArray(ordinal) && ordinalList.indexOf(ordinal) == -1); |
+ var sorted = Array.prototype.concat.call(ordinalList).sort(); |
+ function split(num) { |
+ return Math.floor(num << 1); |
+ } |
+ var size = split(sorted.length); |
+ var pos = size; |
+ while (size > 0) { |
+ size = split(size); |
+ // This just no-ops when size = 0. |
+ pos = ordinal > sorted[pos] ? pos + size : pos - size; |
+ } |
+ return pos; |
}, |
/** |
@@ -247,132 +357,74 @@ cr.define('ntp4', function() { |
* @param {Object} data An object with all the data on available |
* applications. |
*/ |
- getAppsCallback: function(data) { |
- var startTime = Date.now(); |
- |
- // Clear any existing apps pages and dots. |
- // TODO(rbyers): It might be nice to preserve animation of dots after an |
- // uninstall. Could we re-use the existing page and dot elements? It |
- // seems unfortunate to have Chrome send us the entire apps list after an |
- // uninstall. |
- while (this.appsPages.length > 0) { |
- var page = this.appsPages[0]; |
- var dot = page.navigationDot; |
- |
- this.eventTracker.remove(page); |
- page.tearDown(); |
- page.parentNode.removeChild(page); |
- dot.parentNode.removeChild(dot); |
- } |
- |
- // Get the array of apps and add any special synthesized entries |
- var apps = data.apps; |
- |
- // Get a list of page names |
- var pageNames = data.appPageNames; |
- |
- function stringListIsEmpty(list) { |
- for (var i = 0; i < list.length; i++) { |
- if (list[i]) |
- return false; |
- } |
- return true; |
- } |
- |
- // Sort by launch ordinal |
- apps.sort(function(a, b) { |
- return a.app_launch_ordinal > b.app_launch_ordinal ? 1 : |
- a.app_launch_ordinal < b.app_launch_ordinal ? -1 : 0; |
- }); |
- |
- // An app to animate (in case it was just installed). |
- var highlightApp; |
- |
- // Add the apps, creating pages as necessary |
- for (var i = 0; i < apps.length; i++) { |
- var app = apps[i]; |
- var pageIndex = app.page_index || 0; |
- while (pageIndex >= this.appsPages.length) { |
- var pageName = localStrings.getString('appDefaultPageName'); |
- if (this.appsPages.length < pageNames.length) |
- pageName = pageNames[this.appsPages.length]; |
- |
- var origPageCount = this.appsPages.length; |
- this.appendTilePage(new ntp4.AppsPage(), pageName, true); |
- // Confirm that appsPages is a live object, updated when a new page is |
- // added (otherwise we'd have an infinite loop) |
- assert(this.appsPages.length == origPageCount + 1, |
- 'expected new page'); |
- } |
- |
- if (app.id == this.highlightAppId) |
- highlightApp = app; |
- else |
- this.appsPages[pageIndex].appendApp(app); |
- } |
+ getAppsCallback: function(data, measureTime) { |
+ var startTime; |
+ if (measureTime) |
+ startTime = Date.now(); |
+ this.syncAppsPages(data); |
ntp4.AppsPage.setPromo(data.showPromo ? data : null); |
- // Tell the slider about the pages. |
+ // Tell the slider/switchers about the pages. |
this.updateSliderCards(); |
+ this.updatePageSwitchers(); |
if (highlightApp) |
this.appAdded(highlightApp, true); |
// Mark the current page. |
this.cardSlider.currentCardValue.navigationDot.classList.add('selected'); |
- logEvent('apps.layout: ' + (Date.now() - startTime)); |
+ |
+ if (measureTime) |
+ logEvent('apps.layout: ' + (Date.now() - startTime)); |
document.documentElement.classList.remove('starting-up'); |
}, |
/** |
- * Called by chrome when a new app has been added to chrome or has been |
- * enabled if previously disabled. |
- * @param {Object} appData A data structure full of relevant information for |
- * the app. |
+ * @param {Object<>} |
*/ |
- appAdded: function(appData, opt_highlight) { |
- if (appData.id == this.highlightAppId) { |
- opt_highlight = true; |
- this.highlightAppId = null; |
- } |
- |
- var pageIndex = appData.page_index || 0; |
- |
- if (pageIndex >= this.appsPages.length) { |
- while (pageIndex >= this.appsPages.length) { |
- this.appendTilePage(new ntp4.AppsPage(), |
- localStrings.getString('appDefaultPageName'), |
- true); |
+ syncAppsPages: function() { |
+ // Remove pages that aren't in the data we were given. |
+ var existingPages = this.getAppsPageOrdinalMap_(); |
+ for (var i in existingPages) { |
+ if (existingPages.hasOwnProperty(i) && |
+ !(existingPages[i] in data.appsPages)) { |
+ this.removeTilePageAndDot_(existingPages[i]); |
+ delete existingPages[i]; |
} |
- this.updateSliderCards(); |
- } |
- |
- var page = this.appsPages[pageIndex]; |
- var app = $(appData.id); |
- if (app) |
- app.replaceAppData(appData); |
- else |
- page.appendApp(appData, opt_highlight); |
- }, |
- |
- /** |
- * Callback invoked by chrome whenever an app preference changes. |
- * @param {Object} data An object with all the data on available |
- * applications. |
- */ |
- appsPrefChangedCallback: function(data) { |
- for (var i = 0; i < data.apps.length; ++i) { |
- $(data.apps[i].id).appData = data.apps[i]; |
} |
- |
- // Set the App dot names. Skip the first dot (Most Visited). |
- var dots = this.dotList.getElementsByClassName('dot'); |
- var start = this.mostVisitedPage ? 1 : 0; |
- for (var i = start; i < dots.length; ++i) { |
- dots[i].displayTitle = data.appPageNames[i - start] || ''; |
+ // Create or re-use pages or apps and ensure we're synced. |
+ var pageOrdinals = Object.keys(data.appsPages).sort(); |
+ for (var i = 0; i < pageOrdinals.length; ++i) { |
+ // Re-use previous pages or create new pages as necessary. |
+ var appsPage = this.getOrCreateAppsPageAtOrdinal_(pageOrdinals[i]); |
+ existingPages[pageOrdinals[i]] = appsPage; |
+ var page = data.appsPages[pageOrdinals[i]]; |
+ appsPage.navigationDot.displayTitle = page.name; |
+ var appOrdinals = Object.keys(page.apps).sort(); |
+ for (var j = 0; j < appOrdinals.length; ++j) { |
+ var appData = page.apps[appOrdinals[j]]; |
+ var app = $(appData.id); |
+ if (app) { |
+ if (appData.page_ordinal != app.data.page_ordinal || |
+ appData.app_launch_ordinal != app.data.app_launch_ordinal) { |
+ var originalPage = app.tile.tilePage; |
+ var detached = originalPage.removeTile(app); |
+ var index = this.getIndexForNewOrdinal_( |
+ appOrdinals, appData.app_launch_ordinal); |
+ existingPages[appData.page_ordinal].addTileAt(detached, index); |
+ } else { |
+ app.replaceAppData(appData); |
+ } |
+ } else { |
+ var index = this.getIndexForNewOrdinal_(app.app_launch_ordinal); |
+ appsPage.addTileAt(app, app.id == data.highlightId); |
+ } |
+ } |
} |
+ for (var i = 0; i < this.appsPages.length; ++i) |
+ this.appsPages[i].cleanupDrag(); |
}, |
/** |
@@ -380,21 +432,30 @@ cr.define('ntp4', function() { |
* the Slider knows about the new elements. |
*/ |
updateSliderCards: function() { |
- var pageNo = Math.min(this.cardSlider.currentCard, |
- this.tilePages.length - 1); |
- this.cardSlider.setCards(Array.prototype.slice.call(this.tilePages), |
- pageNo); |
+ var index = -1; |
+ var tiles = Array.prototype.slice.call(this.tilePages); |
+ // Clamping this value each time helps self-heal unexpected input. |
+ this.shownPageIndex = Math.max(0, Math.min(this.shownPageIndex, |
+ this.appsPages.length - 1)); |
switch (this.shownPage) { |
- case templateData['apps_page_id']: |
- this.cardSlider.selectCardByValue( |
- this.appsPages[Math.min(this.shownPageIndex, |
- this.appsPages.length - 1)]); |
+ case templateData.apps_page_id: |
+ index = tiles.indexOf(this.appsPages[this.shownPageIndex]); |
break; |
- case templateData['most_visited_page_id']: |
- if (this.mostVisitedPage) |
- this.cardSlider.selectCardByValue(this.mostVisitedPage); |
+ case templateData.most_visited_page_id: |
+ index = tiles.indexOf(this.mostVisitedPage); |
break; |
} |
+ // If shownPage was saved as a page that's now disabled or the shownPage |
+ // doesn't exist any more, index will be -1. Change to the preferred |
+ // default page (first apps pane) in this case. |
+ if (index < 0) { |
+ this.shownPage = templateData.apps_page_id; |
+ index = tiles.indexOf(this.appsPages[0]); |
+ } |
+ // Set the new cards and index. |
+ this.cardSlider.setCards(tiles, index); |
+ |
+ chrome.send('pageSelected', [this.shownPage, this.shownPageIndex]); |
}, |
/** |
@@ -404,13 +465,8 @@ cr.define('ntp4', function() { |
enterRearrangeMode: function() { |
var tempPage = new ntp4.AppsPage(); |
tempPage.classList.add('temporary'); |
- this.appendTilePage(tempPage, |
- localStrings.getString('appDefaultPageName'), |
- true); |
- var tempIndex = Array.prototype.indexOf.call(this.tilePages, tempPage); |
- if (this.cardSlider.currentCard >= tempIndex) |
- this.cardSlider.currentCard += 1; |
- this.updateSliderCards(); |
+ var pageName = localStrings.getString('appDefaultPageName'); |
+ this.appendTilePage(tempPage, pageName, true); |
if (ntp4.getCurrentlyDraggingTile().firstChild.canBeRemoved()) |
$('footer').classList.add('showing-trash-mode'); |
@@ -421,18 +477,14 @@ cr.define('ntp4', function() { |
*/ |
leaveRearrangeMode: function() { |
var tempPage = document.querySelector('.tile-page.temporary'); |
- var dot = tempPage.navigationDot; |
- if (!tempPage.tileCount && tempPage != this.cardSlider.currentCardValue) { |
- dot.animateRemove(); |
- var tempIndex = Array.prototype.indexOf.call(this.tilePages, tempPage); |
- if (this.cardSlider.currentCard > tempIndex) |
- this.cardSlider.currentCard -= 1; |
- tempPage.parentNode.removeChild(tempPage); |
- this.updateSliderCards(); |
- } else { |
+ // Either remove a temp page if it's empty or save the page name (as an |
+ // app has just been dropped on it or created somehow). |
+ // TODO(dbeam): Animated removal if currently on temp page. |
+ if (tempPage && !this.removeAppsPageIfEmpty_(tempPage, true, true)) { |
+ this.saveAppsPageName(tempPage, |
+ tempPage.navigationDot.displayTitle, |
+ true); |
tempPage.classList.remove('temporary'); |
- this.saveAppPageName(tempPage, |
- localStrings.getString('appDefaultPageName')); |
} |
$('footer').classList.remove('showing-trash-mode'); |
@@ -507,7 +559,7 @@ cr.define('ntp4', function() { |
// Don't change shownPage until startup is done (and page changes actually |
// reflect user actions). |
- if (!document.documentElement.classList.contains('starting-up')) { |
+ if (!this.isStartingUp_()) { |
if (page.classList.contains('apps-page')) { |
this.shownPage = templateData['apps_page_id']; |
this.shownPageIndex = this.getAppsPageIndex(page); |
@@ -528,16 +580,129 @@ cr.define('ntp4', function() { |
this.updatePageSwitchers(); |
}, |
- /* |
- * Save the name of an app page. |
- * Store the app page name into the preferences store. |
- * @param {AppsPage} appPage The app page for which we wish to save. |
+ /** |
+ * Listen for card additions to update the page switchers or the current |
+ * card accordingly. |
+ * @param {Event} e A card removed or added event. |
+ */ |
+ cardAddedHandler_: function(e) { |
+ // When the second arg passed to insertBefore is falsey, it acts just like |
+ // appendChild. |
+ this.pageList.insertBefore(e.addedCard, this.tilePages[e.addedIndex]); |
+ if (!this.isStartingUp_()) |
+ this.updatePageSwitchers(); |
+ }, |
+ |
+ /** |
+ * Listen for card removals to update the page switchers or the current card |
+ * accordingly. |
+ * @param {Event} e A card removed or added event. |
+ */ |
+ cardRemovedHandler_: function(e) { |
+ if (!this.isStartingUp_()) |
+ this.updatePageSwitchers(); |
+ assert(!e.removedCard.classList.contains('selected-card')); |
+ e.removedCard.parentNode.removeChild(e.removedCard); |
+ }, |
+ |
+ /** |
+ * Save the name of an apps page. |
+ * Store the apps page name into the preferences store. |
+ * @param {AppsPage} appsPage The app page for which we wish to save. |
* @param {string} name The name of the page. |
+ * @param {boolean} notify If we should notify of when saving the pref. |
*/ |
- saveAppPageName: function(appPage, name) { |
- var index = this.getAppsPageIndex(appPage); |
+ saveAppsPageName: function(appsPage, name, notify) { |
+ var index = this.getAppsPageIndex(appsPage); |
assert(index != -1); |
- chrome.send('saveAppPageName', [name, index]); |
+ chrome.send('saveAppsPageName', [name, index, notify]); |
+ }, |
+ |
+ /** |
+ * An Array of callbacks to be called on the next CARD_CHANGE_ENDED event |
+ * handled from the cardSlider. |
+ * @private |
+ */ |
+ cardChangeEndedCallbacks_: [], |
+ |
+ /** |
+ * Handler for CARD_CHANGE_ENDED on cardSlider. |
+ * @param {Event} e The CARD_CHANGE_ENDED event. |
+ * @private |
+ */ |
+ cardChangeEndedHandler_: function(e) { |
+ if (!this.isStartingUp_()) { |
+ for (var i = 0; i < this.cardChangeEndedCallbacks_.length; ++i) { |
+ if (this.cardChangeEndedCallbacks_[i].call(this, e) !== false) |
+ this.cardChangeEndedCallbacks_.splice(i--, 1); |
+ } |
+ } |
+ }, |
+ |
+ /** |
+ * Happens when a tile is removed from a tile page. |
+ * @param {Event} e An event dispatched from a tile when it is removed. |
+ */ |
+ tileRemovedHandler_: function(e) { |
+ if (e.tilePage instanceof ntp4.AppsPage) |
+ this.removeAppsPageIfEmpty_(e.tilePage, e.wasAnimated); |
+ }, |
+ |
+ /** |
+ * Remove an apps page if it now has no tiles (is empty). |
+ * @param {AppsPage} appsPage A page to check for emptiness. |
+ * @param {boolean=} opt_animate Whether the prospective removal should be |
+ * animated. |
+ * @param {boolean=} opt_dontNotify Whether this NTP's AppLauncherHandler |
+ * shouldn't be notified of this pages' prospective removal (default is |
+ * to notify). |
+ * @return {boolean} If |appsPage| was removed or not. |
+ */ |
+ removeAppsPageIfEmpty_: function(appsPage, opt_animate, opt_dontNotify) { |
+ assert(appsPage instanceof ntp4.AppsPage, |
+ '|appsPage| is not really an AppsPage'); |
+ |
+ if (appsPage.tileCount !== 0) |
+ return false; |
+ |
+ // If the user is currently on the empty apps page, avoid visual issues |
+ // by selecting a different page before deleting. If the user isn't on |
+ // the empty/deleting page, just delete it right away. |
+ var whenOnCorrectPage = function() { |
+ if (!opt_dontNotify) |
+ chrome.send('deleteEmptyAppsPage', [appsPage.ordinal]); |
+ this.removeTilePageAndDot_(appsPage, opt_animate); |
+ }; |
+ |
+ if (appsPage == this.cardSlider.currentCardValue) { |
+ var index = this.getAppsPageIndex(appsPage); |
+ assert(index != -1); |
+ var tempPage = document.querySelector('.apps-page.temporary'); |
+ var tempOffset = tempPage.tileCount == 0 ? 1 : 0; |
+ // If the apps page being deleted is the last apps page (excluding soon |
+ // to be deleted temp pages), move backward one in the card slider. |
+ // Otherwise, move forward one. |
+ var change = index == this.appsPages.length - 1 - tempOffset ? -1 : 1; |
+ var newIndex = this.cardSlider.currentCard + change; |
+ this.cardSlider.selectCard(newIndex, opt_animate); |
+ // In the case where |opt_animate| is truthy, the card selection is |
+ // animated and asynchronous, so we simply append this callback to |
+ // this.cardChangeEndedCallbacks_ to be done on the next animated |
+ // CARD_CHANGE_ENDED event that switches to the proposed index. |
+ if (opt_animate) { |
+ this.cardChangeEndedCallbacks_.push(function(e) { |
+ if (!e.wasAnimated || e.changedTo != newIndex) |
+ return false; |
+ whenOnCorrectPage.call(this); |
+ }); |
+ } else { |
+ whenOnCorrectPage.call(this); |
+ } |
+ } else { |
+ whenOnCorrectPage.call(this); |
+ } |
+ |
+ return true; |
}, |
/** |
@@ -588,7 +753,65 @@ cr.define('ntp4', function() { |
this.cardSlider.selectCard(cardIndex, true); |
e.stopPropagation(); |
- } |
+ }, |
+ |
+ /** |
+ * Re-order apps on this inactive page when an active NTP gets re-ordered. |
+ * @param {Object} data Position hashmap ordered by app id with indices: |
+ * {id => {page_index: ##, app_launch_index: ##}} |
+ */ |
+ appsReordered: function(data) { |
+ }, |
+ |
+ /** |
+ * Ensure there is an apps page at the given |ordinal| by checking for an |
+ * existing page or creating a new once. |
+ * @param {string} ordinal A string ordinal for which to check. |
+ * @private |
+ */ |
+ getOrCreateAppsPageAtOrdinal_: function(ordinal) { |
+ var map = this.getAppsPageOrdinalMap_(); |
+ if (ordinal in map) |
+ return map[ordinal]; |
+ |
+ var appsPage = new ntp4.AppsPage(ordinal); |
+ |
+ var index = this.getIndexForNewOrdinal_(Object.keys(map)); |
+ assert(index >= 0 && index <= this.appsPage.length); |
+ |
+ var origPageCount = this.appsPages.length; |
+ this.appendTilePage(appsPage, |
+ localStrings.getString('appDefaultPageName'), |
+ true, |
+ this.appsPages[index]); |
+ // Confirm that appsPages is a live object, updated when a new page is |
+ // added (otherwise we'd have an infinite loop) |
+ assert(this.appsPages.length == origPageCount + 1, |
+ 'expected new page'); |
+ |
+ return appsPage; |
+ }, |
+ |
+ /** |
+ * Returns the index of a given tile page. |
+ * @param {TilePage} page The TilePage we wish to find. |
+ * @return {number} The index of |page| or -1 if it is not in the |
+ * collection. |
+ */ |
+ getTilePageIndex: function(page) { |
+ return Array.prototype.indexOf.call(this.tilePages, page); |
+ }, |
+ |
+ /** |
+ * Removes a page and navigation dot (if the navdot exists). |
+ * @param {TilePage} page The page to be removed. |
+ * @param {boolean=} opt_animate If the removal should be animated. |
+ */ |
+ removeTilePageAndDot_: function(page, opt_animate) { |
+ if (page.navigationDot) |
+ page.navigationDot.remove(opt_animate); |
+ this.cardSlider.removeCard(page); |
+ }, |
}; |
return { |