OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 /** | |
6 * @fileoverview PageListView implementation. | |
7 * PageListView manages page list, dot list, switcher buttons and handles apps | |
8 * pages callbacks from backend. | |
9 * | |
10 * Note that you need to have AppLauncherHandler in your WebUI to use this code. | |
11 */ | |
12 | |
13 cr.define('ntp4', function() { | |
14 'use strict'; | |
15 | |
16 /** | |
17 * Object for accessing localized strings. | |
18 * @type {!LocalStrings} | |
19 */ | |
20 var localStrings = new LocalStrings; | |
21 | |
22 /** | |
23 * Creates a PageListView object. | |
24 * @constructor | |
25 * @extends {Object} | |
26 */ | |
27 function PageListView() { | |
28 } | |
29 | |
30 PageListView.prototype = { | |
31 /** | |
32 * The CardSlider object to use for changing app pages. | |
33 * @type {CardSlider|undefined} | |
34 */ | |
35 cardSlider: undefined, | |
36 | |
37 /** | |
38 * The frame div for cardSlider. | |
39 * @type {!Element|undefined} | |
40 */ | |
41 sliderFrame: undefined, | |
42 | |
43 /** | |
44 * The 'page-list' element. | |
45 * @type {!Element|undefined} | |
46 */ | |
47 pageList: undefined, | |
48 | |
49 /** | |
50 * A list of all 'tile-page' elements. | |
51 * @type {!NodeList|undefined} | |
52 */ | |
53 tilePages: undefined, | |
54 | |
55 /** | |
56 * A list of all 'apps-page' elements. | |
57 * @type {!NodeList|undefined} | |
58 */ | |
59 appsPages: undefined, | |
60 | |
61 /** | |
62 * The Most Visited page. | |
63 * @type {!Element|undefined} | |
64 */ | |
65 mostVisitedPage: undefined, | |
66 | |
67 /** | |
68 * The Bookmarks page. | |
69 * @type {!Element|undefined} | |
70 */ | |
71 bookmarksPage: undefined, | |
72 | |
73 /** | |
74 * The 'dots-list' element. | |
75 * @type {!Element|undefined} | |
76 */ | |
77 dotList: undefined, | |
78 | |
79 /** | |
80 * The left and right paging buttons. | |
81 * @type {!Element|undefined} | |
82 */ | |
83 pageSwitcherStart: undefined, | |
84 pageSwitcherEnd: undefined, | |
85 | |
86 /** | |
87 * The 'trash' element. Note that technically this is unnecessary, | |
88 * JavaScript creates the object for us based on the id. But I don't want | |
89 * to rely on the ID being the same, and JSCompiler doesn't know about it. | |
90 * @type {!Element|undefined} | |
91 */ | |
92 trash: undefined, | |
93 | |
94 /** | |
95 * The type of page that is currently shown. The value is a numerical ID. | |
96 * @type {number} | |
97 */ | |
98 shownPage: 0, | |
99 | |
100 /** | |
101 * The index of the page that is currently shown, within the page type. | |
102 * For example if the third Apps page is showing, this will be 2. | |
103 * @type {number} | |
104 */ | |
105 shownPageIndex: 0, | |
106 | |
107 /** | |
108 * EventTracker for managing event listeners for page events. | |
109 * @type {!EventTracker} | |
110 */ | |
111 eventTracker: new EventTracker, | |
112 | |
113 /** | |
114 * If non-null, this is the ID of the app to highlight to the user the next | |
115 * time getAppsCallback runs. "Highlight" in this case means to switch to | |
116 * the page and run the new tile animation. | |
117 * @type {String} | |
118 */ | |
119 highlightAppId: null, | |
120 | |
121 /** | |
122 * Initializes page list view. | |
123 * @param {!Element} pageList A DIV element to host all pages. | |
124 * @param {!Element} dotList An UL element to host nav dots. Each dot | |
125 * represents a page. | |
126 * @param {!Element} cardSliderFrame The card slider frame that hosts | |
127 * pageList and switcher buttons. | |
128 * @param {!Element|undefined} opt_Trash Optional trash element. | |
Evan Stade
2011/11/10 00:02:21
opt_trash
xiyuan
2011/11/10 18:25:08
Done.
| |
129 * @param {!Element|undefined} opt_pageSwitcherStart Optional start page | |
130 * switcher button. | |
131 * @param {!Element|undefined} opt_pageSwitcherEnd Optional end page | |
132 * switcher button. | |
133 */ | |
134 initialize: function(pageList, dotList, cardSliderFrame, opt_Trash, | |
135 opt_pageSwitcherStart, opt_pageSwitcherEnd) { | |
136 this.pageList = pageList; | |
137 | |
138 this.dotList = dotList; | |
139 cr.ui.decorate(this.dotList, ntp4.DotList); | |
140 | |
141 this.trash = opt_Trash; | |
142 if (this.trash) | |
143 new ntp4.Trash(this.trash); | |
144 | |
145 this.pageSwitcherStart = opt_pageSwitcherStart; | |
146 if (this.pageSwitcherStart) | |
147 ntp4.initializePageSwitcher(this.pageSwitcherStart); | |
148 | |
149 this.pageSwitcherEnd = opt_pageSwitcherEnd; | |
150 if (this.pageSwitcherEnd) | |
151 ntp4.initializePageSwitcher(this.pageSwitcherEnd); | |
152 | |
153 this.shownPage = templateData['shown_page_type']; | |
154 this.shownPageIndex = templateData['shown_page_index']; | |
155 | |
156 // Request data on the apps so we can fill them in. | |
157 // Note that this is kicked off asynchronously. 'getAppsCallback' will be | |
158 // invoked at some point after this function returns. | |
159 chrome.send('getApps'); | |
160 | |
161 document.addEventListener('keydown', this.onDocKeyDown.bind(this)); | |
162 // Prevent touch events from triggering any sort of native scrolling | |
163 document.addEventListener('touchmove', function(e) { | |
164 e.preventDefault(); | |
165 }, true); | |
166 | |
167 this.tilePages = this.pageList.getElementsByClassName('tile-page'); | |
168 this.appsPages = this.pageList.getElementsByClassName('apps-page'); | |
169 | |
170 // Initialize the cardSlider without any cards at the moment | |
171 this.sliderFrame = cardSliderFrame; | |
172 this.cardSlider = new CardSlider(this.sliderFrame, this.pageList, | |
173 this.sliderFrame.offsetWidth); | |
174 this.cardSlider.initialize(); | |
175 | |
176 // Handle the page being changed | |
177 this.pageList.addEventListener( | |
178 CardSlider.EventType.CARD_CHANGED, | |
179 this.cardChangedHandler.bind(this)); | |
180 | |
181 // Ensure the slider is resized appropriately with the window | |
182 window.addEventListener('resize', this.onWindowResize_.bind(this)); | |
183 | |
184 // Update apps when online state changes. | |
185 window.addEventListener('online', this.updateOfflineEnabledApps); | |
Evan Stade
2011/11/10 00:02:21
.bind(this)
xiyuan
2011/11/10 18:25:08
Done.
| |
186 window.addEventListener('offline', this.updateOfflineEnabledApps); | |
Evan Stade
2011/11/10 00:02:21
.bind(this)
xiyuan
2011/11/10 18:25:08
Done.
| |
187 }, | |
188 | |
189 /** | |
190 * Appends a tile page (for bookmarks or most visited). | |
191 * | |
192 * @param {TilePage} page The page element. | |
193 * @param {string} title The title of the tile page. | |
194 * @param {bool} titleIsEditable If true, the title can be changed. | |
195 * @param {TilePage} opt_refNode Optional reference node to insert in front | |
196 * of. | |
197 * When opt_refNode is falsey, |page| will just be appended to the end of | |
198 * the page list. | |
199 */ | |
200 appendTilePage: function(page, title, titleIsEditable, opt_refNode) { | |
201 // If no opt_refNode given, use bookmarksPage (if any). | |
202 if (!opt_refNode) | |
203 opt_refNode = this.bookmarksPage; | |
204 | |
205 // When opt_refNode is falsey, insertBefore acts just like appendChild. | |
206 this.pageList.insertBefore(page, opt_refNode); | |
207 | |
208 // Remember special MostVisitedPage and BookmarksPage. | |
209 if (typeof ntp4.MostVisitedPage != 'undefined' && | |
210 page instanceof ntp4.MostVisitedPage) { | |
211 assert(this.tilePages.length == 1, | |
212 'MostVisitedPage should be added as first tile page'); | |
213 this.mostVisitedPage = page; | |
214 } | |
215 if (typeof ntp4.BookmarksPage != 'undefined' && | |
216 page instanceof ntp4.BookmarksPage) { | |
217 this.bookmarksPage = page; | |
218 } | |
219 | |
220 // If we're appending an AppsPage and it's a temporary page, animate it. | |
221 var animate = page instanceof ntp4.AppsPage && | |
222 page.classList.contains('temporary'); | |
223 // Make a deep copy of the dot template to add a new one. | |
224 var newDot = new ntp4.NavDot(page, title, titleIsEditable, animate); | |
225 page.navigationDot = newDot; | |
226 this.dotList.insertBefore(newDot, opt_refNode ? opt_refNode.navigationDot | |
227 : null); | |
228 // Set a tab index on the first dot. | |
229 if (this.dotList.dots.length == 1) | |
230 newDot.tabIndex = 3; | |
231 | |
232 this.eventTracker.add(page, 'pagelayout', this.onPageLayout_.bind(this)); | |
233 }, | |
234 | |
235 /** | |
236 * Called by chrome when an existing app has been disabled or | |
237 * removed/uninstalled from chrome. | |
238 * @param {Object} appData A data structure full of relevant information for | |
239 * the app. | |
240 * @param {boolean} isUninstall True if the app is being uninstalled; | |
241 * false if the app is being disabled. | |
242 */ | |
243 appRemoved: function(appData, isUninstall) { | |
244 var app = $(appData.id); | |
245 assert(app, 'trying to remove an app that doesn\'t exist'); | |
246 | |
247 if (!isUninstall) | |
248 app.replaceAppData(appData); | |
249 else | |
250 app.remove(); | |
251 }, | |
252 | |
253 /** | |
254 * Callback invoked by chrome with the apps available. | |
255 * | |
256 * Note that calls to this function can occur at any time, not just in | |
257 * response to a getApps request. For example, when a user | |
258 * installs/uninstalls an app on another synchronized devices. | |
259 * @param {Object} data An object with all the data on available | |
260 * applications. | |
261 */ | |
262 getAppsCallback: function(data) { | |
263 var startTime = Date.now(); | |
264 | |
265 // Clear any existing apps pages and dots. | |
266 // TODO(rbyers): It might be nice to preserve animation of dots after an | |
267 // uninstall. Could we re-use the existing page and dot elements? It | |
268 // seems unfortunate to have Chrome send us the entire apps list after an | |
269 // uninstall. | |
270 while (this.appsPages.length > 0) { | |
271 var page = this.appsPages[0]; | |
272 var dot = page.navigationDot; | |
273 | |
274 this.eventTracker.remove(page); | |
275 page.tearDown(); | |
276 page.parentNode.removeChild(page); | |
277 dot.parentNode.removeChild(dot); | |
278 } | |
279 | |
280 // Get the array of apps and add any special synthesized entries | |
281 var apps = data.apps; | |
282 | |
283 // Get a list of page names | |
284 var pageNames = data.appPageNames; | |
285 | |
286 function stringListIsEmpty(list) { | |
287 for (var i = 0; i < list.length; i++) { | |
288 if (list[i]) | |
289 return false; | |
290 } | |
291 return true; | |
292 } | |
293 | |
294 // Sort by launch index | |
295 apps.sort(function(a, b) { | |
296 return a.app_launch_index - b.app_launch_index; | |
297 }); | |
298 | |
299 // An app to animate (in case it was just installed). | |
300 var highlightApp; | |
301 | |
302 // Add the apps, creating pages as necessary | |
303 for (var i = 0; i < apps.length; i++) { | |
304 var app = apps[i]; | |
305 var pageIndex = (app.page_index || 0); | |
Evan Stade
2011/11/10 00:02:21
remove ()
xiyuan
2011/11/10 18:25:08
Done.
| |
306 while (pageIndex >= this.appsPages.length) { | |
307 var pageName = localStrings.getString('appDefaultPageName'); | |
308 if (this.appsPages.length < pageNames.length) | |
309 pageName = pageNames[this.appsPages.length]; | |
310 | |
311 var origPageCount = this.appsPages.length; | |
312 this.appendTilePage(new ntp4.AppsPage(), pageName, true); | |
313 // Confirm that appsPages is a live object, updated when a new page is | |
314 // added (otherwise we'd have an infinite loop) | |
315 assert(this.appsPages.length == origPageCount + 1, | |
316 'expected new page'); | |
317 } | |
318 | |
319 if (app.id == this.highlightAppId) | |
320 highlightApp = app; | |
321 else | |
322 this.appsPages[pageIndex].appendApp(app); | |
323 } | |
324 | |
325 ntp4.AppsPage.setPromo(data.showPromo ? data : null); | |
326 | |
327 // Tell the slider about the pages. | |
328 this.updateSliderCards(); | |
329 | |
330 if (highlightApp) | |
331 this.appAdded(highlightApp, true); | |
332 | |
333 // Mark the current page. | |
334 this.cardSlider.currentCardValue.navigationDot.classList.add('selected'); | |
335 logEvent('apps.layout: ' + (Date.now() - startTime)); | |
336 | |
337 document.documentElement.classList.remove('starting-up'); | |
338 }, | |
339 | |
340 /** | |
341 * Called by chrome when a new app has been added to chrome or has been | |
342 * enabled if previously disabled. | |
343 * @param {Object} appData A data structure full of relevant information for | |
344 * the app. | |
345 */ | |
346 appAdded: function(appData, opt_highlight) { | |
347 if (appData.id == this.highlightAppId) { | |
348 opt_highlight = true; | |
349 this.highlightAppId = null; | |
350 } | |
351 | |
352 var pageIndex = appData.page_index || 0; | |
353 | |
354 if (pageIndex >= this.appsPages.length) { | |
355 while (pageIndex >= this.appsPages.length) { | |
356 this.appendTilePage(new ntp4.AppsPage(), | |
357 localStrings.getString('appDefaultPageName'), | |
358 true); | |
359 } | |
360 this.updateSliderCards(); | |
361 } | |
362 | |
363 var page = this.appsPages[pageIndex]; | |
364 var app = $(appData.id); | |
365 if (app) | |
366 app.replaceAppData(appData); | |
367 else | |
368 page.appendApp(appData, opt_highlight); | |
369 }, | |
370 | |
371 /** | |
372 * Callback invoked by chrome whenever an app preference changes. | |
373 * @param {Object} data An object with all the data on available | |
374 * applications. | |
375 */ | |
376 appsPrefChangeCallback: function(data) { | |
377 for (var i = 0; i < data.apps.length; ++i) { | |
378 $(data.apps[i].id).appData = data.apps[i]; | |
379 } | |
380 | |
381 // Set the App dot names. Skip the first and last dots (Most Visited and | |
382 // Bookmarks). | |
383 var dots = this.dotList.getElementsByClassName('dot'); | |
384 // TODO(csilv): Remove this calcluation if/when we remove the flag for | |
385 // for the bookmarks page. | |
386 var start = this.mostVisitedPage ? 1 : 0; | |
387 var length = this.bookmarksPage ? dots.length - 1 : dots.length; | |
388 for (var i = start; i < length; ++i) { | |
389 dots[i].displayTitle = data.appPageNames[i - start] || ''; | |
390 } | |
391 }, | |
392 | |
393 /** | |
394 * Invoked whenever the pages in apps-page-list have changed so that | |
395 * the Slider knows about the new elements. | |
396 */ | |
397 updateSliderCards: function() { | |
398 var pageNo = Math.min(this.cardSlider.currentCard, | |
399 this.tilePages.length - 1); | |
400 this.cardSlider.setCards(Array.prototype.slice.call(this.tilePages), | |
401 pageNo); | |
402 switch (this.shownPage) { | |
403 case templateData['apps_page_id']: | |
404 this.cardSlider.selectCardByValue( | |
405 this.appsPages[Math.min(this.shownPageIndex, | |
406 this.appsPages.length - 1)]); | |
407 break; | |
408 case templateData['bookmarks_page_id']: | |
409 if (this.bookmarksPage) | |
410 this.cardSlider.selectCardByValue(this.bookmarksPage); | |
411 break; | |
412 case templateData['most_visited_page_id']: | |
413 if (this.mostVisitedPage) | |
414 this.cardSlider.selectCardByValue(this.mostVisitedPage); | |
415 break; | |
416 } | |
417 }, | |
418 | |
419 /** | |
420 * Called whenever tiles should be re-arranging themselves out of the way | |
421 * of a moving or insert tile. | |
422 */ | |
423 enterRearrangeMode: function() { | |
424 var tempPage = new ntp4.AppsPage(); | |
425 tempPage.classList.add('temporary'); | |
426 this.appendTilePage(tempPage, | |
427 localStrings.getString('appDefaultPageName'), | |
428 true); | |
429 var tempIndex = Array.prototype.indexOf.call(this.tilePages, tempPage); | |
430 if (this.cardSlider.currentCard >= tempIndex) | |
431 this.cardSlider.currentCard += 1; | |
432 this.updateSliderCards(); | |
433 | |
434 if (ntp4.getCurrentlyDraggingTile().firstChild.canBeRemoved()) | |
435 $('footer').classList.add('showing-trash-mode'); | |
436 }, | |
437 | |
438 /** | |
439 * Invoked whenever some app is released | |
440 * @param {Grabber.Event} e The Grabber RELEASE event. | |
441 */ | |
442 leaveRearrangeMode: function(e) { | |
443 var tempPage = document.querySelector('.tile-page.temporary'); | |
444 var dot = tempPage.navigationDot; | |
445 if (!tempPage.tileCount && tempPage != this.cardSlider.currentCardValue) { | |
446 dot.animateRemove(); | |
447 var tempIndex = Array.prototype.indexOf.call(this.tilePages, tempPage); | |
448 if (this.cardSlider.currentCard > tempIndex) | |
449 this.cardSlider.currentCard -= 1; | |
450 tempPage.parentNode.removeChild(tempPage); | |
451 this.updateSliderCards(); | |
452 } else { | |
453 tempPage.classList.remove('temporary'); | |
454 this.saveAppPageName(tempPage, | |
455 localStrings.getString('appDefaultPageName')); | |
456 } | |
457 | |
458 $('footer').classList.remove('showing-trash-mode'); | |
459 }, | |
460 | |
461 /** | |
462 * Callback for the 'pagelayout' event. | |
463 * @param {Event} e The event. | |
464 */ | |
465 onPageLayout_: function(e) { | |
466 if (Array.prototype.indexOf.call(this.tilePages, e.currentTarget) != | |
467 this.cardSlider.currentCard) { | |
468 return; | |
469 } | |
470 | |
471 this.updatePageSwitchers(); | |
472 }, | |
473 | |
474 /** | |
475 * Adjusts the size and position of the page switchers according to the | |
476 * layout of the current card. | |
477 */ | |
478 updatePageSwitchers: function() { | |
479 if (!this.pageSwitcherStart || !this.pageSwitcherEnd) | |
480 return; | |
481 | |
482 var page = this.cardSlider.currentCardValue; | |
483 | |
484 this.pageSwitcherStart.hidden = !page || | |
485 (this.cardSlider.currentCard == 0); | |
486 this.pageSwitcherEnd.hidden = !page || | |
487 (this.cardSlider.currentCard == this.cardSlider.cardCount - 1); | |
488 | |
489 if (!page) | |
490 return; | |
491 | |
492 var pageSwitcherLeft = isRTL() ? this.pageSwitcherEnd | |
493 : this.pageSwitcherStart; | |
494 var pageSwitcherRight = isRTL() ? this.pageSwitcherStart | |
495 : this.pageSwitcherEnd; | |
496 var scrollbarWidth = page.scrollbarWidth; | |
497 pageSwitcherLeft.style.width = | |
498 (page.sideMargin + 13) + 'px'; | |
499 pageSwitcherLeft.style.left = '0'; | |
500 pageSwitcherRight.style.width = | |
501 (page.sideMargin - scrollbarWidth + 13) + 'px'; | |
502 pageSwitcherRight.style.right = scrollbarWidth + 'px'; | |
503 | |
504 var offsetTop = page.querySelector('.tile-page-content').offsetTop + 'px'; | |
505 pageSwitcherLeft.style.top = offsetTop; | |
506 pageSwitcherRight.style.top = offsetTop; | |
507 pageSwitcherLeft.style.paddingBottom = offsetTop; | |
508 pageSwitcherRight.style.paddingBottom = offsetTop; | |
509 }, | |
510 | |
511 /** | |
512 * Returns the index of the given page. | |
513 * @param {AppsPage} page The AppsPage for we wish to find. | |
514 * @return {number} The index of |page|, or -1 if it is not here. | |
515 */ | |
516 getAppsPageIndex: function(page) { | |
517 return Array.prototype.indexOf.call(this.appsPages, page); | |
518 }, | |
519 | |
520 /** | |
521 * Handler for CARD_CHANGED on cardSlider. | |
522 * @param {Event} e The CARD_CHANGED event. | |
523 */ | |
524 cardChangedHandler: function(e) { | |
525 var page = e.cardSlider.currentCardValue; | |
526 | |
527 // Don't change shownPage until startup is done (and page changes actually | |
528 // reflect user actions). | |
529 if (!document.documentElement.classList.contains('starting-up')) { | |
530 if (page.classList.contains('apps-page')) { | |
531 this.shownPage = templateData['apps_page_id']; | |
532 this.shownPageIndex = this.getAppsPageIndex(page); | |
533 } else if (page.classList.contains('most-visited-page')) { | |
534 this.shownPage = templateData['most_visited_page_id']; | |
535 this.shownPageIndex = 0; | |
536 } else if (page.classList.contains('bookmarks-page')) { | |
537 this.shownPage = templateData['bookmarks_page_id']; | |
538 this.shownPageIndex = 0; | |
539 } else { | |
540 console.error('unknown page selected'); | |
541 } | |
542 chrome.send('pageSelected', [this.shownPage, this.shownPageIndex]); | |
543 } | |
544 | |
545 // Update the active dot | |
546 var curDot = this.dotList.getElementsByClassName('selected')[0]; | |
547 if (curDot) | |
548 curDot.classList.remove('selected'); | |
549 page.navigationDot.classList.add('selected'); | |
550 this.updatePageSwitchers(); | |
551 }, | |
552 | |
553 /* | |
554 * Save the name of an app page. | |
555 * Store the app page name into the preferences store. | |
556 * @param {AppsPage} appPage The app page for which we wish to save. | |
557 * @param {string} name The name of the page. | |
558 */ | |
559 saveAppPageName: function(appPage, name) { | |
560 var index = this.getAppsPageIndex(appPage); | |
561 assert(index != -1); | |
562 chrome.send('saveAppPageName', [name, index]); | |
563 }, | |
564 | |
565 /** | |
566 * Window resize handler. | |
567 * @private | |
568 */ | |
569 onWindowResize_: function(e) { | |
570 this.cardSlider.resize(this.sliderFrame.offsetWidth); | |
571 this.updatePageSwitchers(); | |
572 }, | |
573 | |
574 /** | |
575 * Listener for offline status change events. Updates apps that are | |
576 * not offline-enabled to be grayscale if the browser is offline. | |
577 */ | |
578 updateOfflineEnabledApps: function() { | |
Evan Stade
2011/11/10 00:02:21
should be private
xiyuan
2011/11/10 18:25:08
Done.
| |
579 var apps = document.querySelectorAll('.app'); | |
580 for (var i = 0; i < apps.length; ++i) { | |
581 if (apps[i].appData.enabled && !apps[i].appData.offline_enabled) { | |
582 apps[i].setIcon(); | |
583 apps[i].loadIcon(); | |
584 } | |
585 } | |
586 }, | |
587 | |
588 /** | |
589 * Handler for key events on the page. Ctrl-Arrow will switch the visible | |
590 * page. | |
591 * @param {Event} e The KeyboardEvent. | |
592 */ | |
593 onDocKeyDown: function(e) { | |
Evan Stade
2011/11/10 00:02:21
should be private
xiyuan
2011/11/10 18:25:08
Done.
| |
594 if (!e.ctrlKey || e.altKey || e.metaKey || e.shiftKey) | |
595 return; | |
596 | |
597 var direction = 0; | |
598 if (e.keyIdentifier == 'Left') | |
599 direction = -1; | |
600 else if (e.keyIdentifier == 'Right') | |
601 direction = 1; | |
602 else | |
603 return; | |
604 | |
605 var cardIndex = | |
606 (this.cardSlider.currentCard + direction + | |
607 this.cardSlider.cardCount) % this.cardSlider.cardCount; | |
608 this.cardSlider.selectCard(cardIndex, true); | |
609 | |
610 e.stopPropagation(); | |
611 } | |
612 }; | |
613 | |
614 return { | |
615 PageListView: PageListView | |
616 }; | |
617 }); | |
OLD | NEW |