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

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

Issue 8423055: [Aura] Initial app list webui. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: move onKeyDown into PageListView onDocKeyDown Created 9 years, 1 month 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) 2011 The Chromium Authors. All rights reserved. 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 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 New tab page 6 * @fileoverview New tab page
7 * This is the main code for the new tab page used by touch-enabled Chrome 7 * This is the main code for the new tab page used by touch-enabled Chrome
8 * browsers. For now this is still a prototype. 8 * browsers. For now this is still a prototype.
9 */ 9 */
10 10
11 // Use an anonymous function to enable strict mode just for this file (which 11 // Use an anonymous function to enable strict mode just for this file (which
12 // will be concatenated with other files when embedded in Chrome 12 // will be concatenated with other files when embedded in Chrome
13 cr.define('ntp4', function() { 13 cr.define('ntp4', function() {
14 'use strict'; 14 'use strict';
15 15
16 /** 16 /**
17 * The CardSlider object to use for changing app pages. 17 * NewTabView instance.
18 * @type {CardSlider|undefined} 18 * @type {!Object|undefined}
19 */ 19 */
20 var cardSlider; 20 var newTabView;
21
22 /**
23 * The 'page-list' element.
24 * @type {!Element|undefined}
25 */
26 var pageList;
27
28 /**
29 * A list of all 'tile-page' elements.
30 * @type {!NodeList|undefined}
31 */
32 var tilePages;
33
34 /**
35 * The Most Visited page.
36 * @type {!Element|undefined}
37 */
38 var mostVisitedPage;
39
40 /**
41 * A list of all 'apps-page' elements.
42 * @type {!NodeList|undefined}
43 */
44 var appsPages;
45
46 /**
47 * The Bookmarks page.
48 * @type {!Element|undefined}
49 */
50 var bookmarksPage;
51
52 /**
53 * The 'dots-list' element.
54 * @type {!Element|undefined}
55 */
56 var dotList;
57
58 /**
59 * Live list of the navigation dots.
60 * @type {!NodeList|undefined}
61 */
62 var navDots;
63 21
64 /** 22 /**
65 * The 'notification-container' element. 23 * The 'notification-container' element.
66 * @type {!Element|undefined} 24 * @type {!Element|undefined}
67 */ 25 */
68 var notificationContainer; 26 var notificationContainer;
69 27
70 /** 28 /**
71 * The left and right paging buttons.
72 * @type {!Element|undefined}
73 */
74 var pageSwitcherStart;
75 var pageSwitcherEnd;
76
77 /**
78 * The 'trash' element. Note that technically this is unnecessary,
79 * JavaScript creates the object for us based on the id. But I don't want
80 * to rely on the ID being the same, and JSCompiler doesn't know about it.
81 * @type {!Element|undefined}
82 */
83 var trash;
84
85 /**
86 * The type of page that is currently shown. The value is a numerical ID.
87 * @type {number}
88 */
89 var shownPage = 0;
90
91 /**
92 * The index of the page that is currently shown, within the page type.
93 * For example if the third Apps page is showing, this will be 2.
94 * @type {number}
95 */
96 var shownPageIndex = 0;
97
98 /**
99 * EventTracker for managing event listeners for page events.
100 * @type {!EventTracker}
101 */
102 var eventTracker = new EventTracker;
103
104 /**
105 * Object for accessing localized strings. 29 * Object for accessing localized strings.
106 * @type {!LocalStrings} 30 * @type {!LocalStrings}
107 */ 31 */
108 var localStrings = new LocalStrings; 32 var localStrings = new LocalStrings;
109 33
110 /** 34 /**
111 * If non-null, this is the ID of the app to highlight to the user the next
112 * time getAppsCallback runs. "Highlight" in this case means to switch to
113 * the page and run the new tile animation.
114 * @type {String}
115 */
116 var highlightAppId = null;
117
118 /**
119 * If non-null, an info bubble for showing messages to the user. It points at 35 * If non-null, an info bubble for showing messages to the user. It points at
120 * the Most Visited label, and is used to draw more attention to the 36 * the Most Visited label, and is used to draw more attention to the
121 * navigation dot UI. 37 * navigation dot UI.
122 * @type {!Element|undefined} 38 * @type {!Element|undefined}
123 */ 39 */
124 var infoBubble; 40 var infoBubble;
125 41
126 /** 42 /**
127 * If non-null, an bubble confirming that the user has signed into sync. It 43 * If non-null, an bubble confirming that the user has signed into sync. It
128 * points at the login status at the top of the page. 44 * points at the login status at the top of the page.
(...skipping 10 matching lines...) Expand all
139 /** 55 /**
140 * The time in milliseconds for most transitions. This should match what's 56 * The time in milliseconds for most transitions. This should match what's
141 * in new_tab.css. Unfortunately there's no better way to try to time 57 * in new_tab.css. Unfortunately there's no better way to try to time
142 * something to occur until after a transition has completed. 58 * something to occur until after a transition has completed.
143 * @type {number} 59 * @type {number}
144 * @const 60 * @const
145 */ 61 */
146 var DEFAULT_TRANSITION_TIME = 500; 62 var DEFAULT_TRANSITION_TIME = 500;
147 63
148 /** 64 /**
65 * Creates a NewTabView object.
66 * @constructor
67 * @extends {PageListView}
68 */
69 function NewTabView() {
70 this.__proto__ = NewTabView.prototype;
71 this.initialize();
72 }
73
74 NewTabView.prototype = {
75 __proto__: ntp4.PageListView.prototype,
76
77 /** @inheritDoc */
78 appendTilePage: function(page, title, titleIsEditable, opt_refNode) {
79 ntp4.PageListView.prototype.appendTilePage.apply(this, arguments);
80
81 if (infoBubble)
82 window.setTimeout(infoBubble.reposition.bind(infoBubble), 0);
83 }
84 };
85
86 /**
149 * Invoked at startup once the DOM is available to initialize the app. 87 * Invoked at startup once the DOM is available to initialize the app.
150 */ 88 */
151 function initialize() { 89 function initialize() {
152 cr.enablePlatformSpecificCSSRules(); 90 cr.enablePlatformSpecificCSSRules();
153 91
154 // Load the current theme colors. 92 // Load the current theme colors.
155 themeChanged(); 93 themeChanged();
156 94
157 dotList = getRequiredElement('dot-list'); 95 newTabView = new NewTabView();
158 dotList.addEventListener('keydown', onDotListKeyDown);
159 navDots = dotList.getElementsByClassName('dot');
160
161 pageList = getRequiredElement('page-list');
162 trash = getRequiredElement('trash');
163 new ntp4.Trash(trash);
164
165 shownPage = templateData['shown_page_type'];
166 shownPageIndex = templateData['shown_page_index'];
167
168 // Request data on the apps so we can fill them in.
169 // Note that this is kicked off asynchronously. 'getAppsCallback' will be
170 // invoked at some point after this function returns.
171 chrome.send('getApps');
172
173 document.addEventListener('keydown', onKeyDown);
174 // Prevent touch events from triggering any sort of native scrolling
175 document.addEventListener('touchmove', function(e) {
176 e.preventDefault();
177 }, true);
178
179 tilePages = pageList.getElementsByClassName('tile-page');
180 appsPages = pageList.getElementsByClassName('apps-page');
181
182 pageSwitcherStart = getRequiredElement('page-switcher-start');
183 ntp4.initializePageSwitcher(pageSwitcherStart);
184 pageSwitcherEnd = getRequiredElement('page-switcher-end');
185 ntp4.initializePageSwitcher(pageSwitcherEnd);
186 96
187 notificationContainer = getRequiredElement('notification-container'); 97 notificationContainer = getRequiredElement('notification-container');
188 notificationContainer.addEventListener( 98 notificationContainer.addEventListener(
189 'webkitTransitionEnd', onNotificationTransitionEnd); 99 'webkitTransitionEnd', onNotificationTransitionEnd);
190 100
191 // Initialize the cardSlider without any cards at the moment
192 var sliderFrame = getRequiredElement('card-slider-frame');
193 cardSlider = new CardSlider(sliderFrame, pageList, sliderFrame.offsetWidth);
194 cardSlider.initialize();
195
196 // Ensure the slider is resized appropriately with the window
197 window.addEventListener('resize', function() {
198 cardSlider.resize(sliderFrame.offsetWidth);
199 updatePageSwitchers();
200 });
201
202 // Handle the page being changed
203 pageList.addEventListener(
204 CardSlider.EventType.CARD_CHANGED,
205 cardChangedHandler);
206
207 cr.ui.decorate($('recently-closed-menu-button'), ntp4.RecentMenuButton); 101 cr.ui.decorate($('recently-closed-menu-button'), ntp4.RecentMenuButton);
208 chrome.send('getRecentlyClosedTabs'); 102 chrome.send('getRecentlyClosedTabs');
209 103
210 mostVisitedPage = new ntp4.MostVisitedPage(); 104 newTabView.appendTilePage(new ntp4.MostVisitedPage(),
211 appendTilePage(mostVisitedPage, localStrings.getString('mostvisited'), 105 localStrings.getString('mostvisited'),
212 false); 106 false);
213 chrome.send('getMostVisited'); 107 chrome.send('getMostVisited');
214 108
215 if (localStrings.getString('login_status_message')) { 109 if (localStrings.getString('login_status_message')) {
216 loginBubble = new cr.ui.Bubble; 110 loginBubble = new cr.ui.Bubble;
217 loginBubble.anchorNode = $('login-container'); 111 loginBubble.anchorNode = $('login-container');
218 loginBubble.setArrowLocation(cr.ui.ArrowLocation.TOP_END); 112 loginBubble.setArrowLocation(cr.ui.ArrowLocation.TOP_END);
219 loginBubble.bubbleAlignment = 113 loginBubble.bubbleAlignment =
220 cr.ui.BubbleAlignment.BUBBLE_EDGE_TO_ANCHOR_EDGE; 114 cr.ui.BubbleAlignment.BUBBLE_EDGE_TO_ANCHOR_EDGE;
221 loginBubble.deactivateToDismissDelay = 2000; 115 loginBubble.deactivateToDismissDelay = 2000;
222 loginBubble.setCloseButtonVisible(false); 116 loginBubble.setCloseButtonVisible(false);
(...skipping 12 matching lines...) Expand all
235 } 129 }
236 130
237 var dismissButton = loginBubble.querySelector('#login-status-dismiss'); 131 var dismissButton = loginBubble.querySelector('#login-status-dismiss');
238 dismissButton.onclick = loginBubble.hide.bind(loginBubble); 132 dismissButton.onclick = loginBubble.hide.bind(loginBubble);
239 133
240 // The anchor node won't be updated until updateLogin is called so don't 134 // The anchor node won't be updated until updateLogin is called so don't
241 // show the bubble yet. 135 // show the bubble yet.
242 shouldShowLoginBubble = true; 136 shouldShowLoginBubble = true;
243 } else if (localStrings.getString('ntp4_intro_message')) { 137 } else if (localStrings.getString('ntp4_intro_message')) {
244 infoBubble = new cr.ui.Bubble; 138 infoBubble = new cr.ui.Bubble;
245 infoBubble.anchorNode = mostVisitedPage.navigationDot; 139 infoBubble.anchorNode = newTabView.mostVisitedPage.navigationDot;
246 infoBubble.setArrowLocation(cr.ui.ArrowLocation.BOTTOM_START); 140 infoBubble.setArrowLocation(cr.ui.ArrowLocation.BOTTOM_START);
247 infoBubble.handleCloseEvent = function() { 141 infoBubble.handleCloseEvent = function() {
248 this.hide(); 142 this.hide();
249 chrome.send('introMessageDismissed'); 143 chrome.send('introMessageDismissed');
250 } 144 }
251 145
252 var bubbleContent = $('ntp4-intro-bubble-contents'); 146 var bubbleContent = $('ntp4-intro-bubble-contents');
253 infoBubble.content = bubbleContent; 147 infoBubble.content = bubbleContent;
254 bubbleContent.hidden = false; 148 bubbleContent.hidden = false;
255 149
256 var learnMoreLink = infoBubble.querySelector('a'); 150 var learnMoreLink = infoBubble.querySelector('a');
257 learnMoreLink.href = localStrings.getString('ntp4_intro_url'); 151 learnMoreLink.href = localStrings.getString('ntp4_intro_url');
258 learnMoreLink.onclick = infoBubble.hide.bind(infoBubble); 152 learnMoreLink.onclick = infoBubble.hide.bind(infoBubble);
259 153
260 infoBubble.show(); 154 infoBubble.show();
261 } 155 }
262 156
263 var bookmarkFeatures = localStrings.getString('bookmark_features'); 157 var bookmarkFeatures = localStrings.getString('bookmark_features');
264 if (bookmarkFeatures == 'true') { 158 if (bookmarkFeatures == 'true') {
265 bookmarksPage = new ntp4.BookmarksPage(); 159 newTabView.appendTilePage(new ntp4.BookmarksPage(),
266 appendTilePage(bookmarksPage, localStrings.getString('bookmarksPage'), 160 localStrings.getString('bookmarksPage'),
267 false); 161 false);
268 chrome.send('getBookmarksData'); 162 chrome.send('getBookmarksData');
269 } 163 }
270 164
271 var serverpromo = localStrings.getString('serverpromo'); 165 var serverpromo = localStrings.getString('serverpromo');
272 if (serverpromo) { 166 if (serverpromo) {
273 showNotification(parseHtmlSubset(serverpromo), [], function() { 167 showNotification(parseHtmlSubset(serverpromo), [], function() {
274 chrome.send('closeNotificationPromo'); 168 chrome.send('closeNotificationPromo');
275 }, 60000); 169 }, 60000);
276 chrome.send('notificationPromoViewed'); 170 chrome.send('notificationPromoViewed');
277 } 171 }
278 172
279 var loginContainer = getRequiredElement('login-container'); 173 var loginContainer = getRequiredElement('login-container');
280 loginContainer.addEventListener('click', function() { 174 loginContainer.addEventListener('click', function() {
281 var rect = loginContainer.getBoundingClientRect(); 175 var rect = loginContainer.getBoundingClientRect();
282 chrome.send('showSyncLoginUI', 176 chrome.send('showSyncLoginUI',
283 [rect.left, rect.top, rect.width, rect.height]); 177 [rect.left, rect.top, rect.width, rect.height]);
284 }); 178 });
285 chrome.send('initializeSyncLogin'); 179 chrome.send('initializeSyncLogin');
286 } 180 }
287 181
288 /**
289 * Simple common assertion API
290 * @param {*} condition The condition to test. Note that this may be used to
291 * test whether a value is defined or not, and we don't want to force a
292 * cast to Boolean.
293 * @param {string=} opt_message A message to use in any error.
294 */
295 function assert(condition, opt_message) {
296 'use strict';
297 if (!condition) {
298 var msg = 'Assertion failed';
299 if (opt_message)
300 msg = msg + ': ' + opt_message;
301 throw new Error(msg);
302 }
303 }
304
305 /**
306 * Get an element that's known to exist by its ID. We use this instead of just
307 * calling getElementById and not checking the result because this lets us
308 * satisfy the JSCompiler type system.
309 * @param {string} id The identifier name.
310 * @return {!Element} the Element.
311 */
312 function getRequiredElement(id) {
313 var element = document.getElementById(id);
314 assert(element, 'Missing required element: ' + id);
315 return element;
316 }
317
318 /**
319 * Callback invoked by chrome with the apps available.
320 *
321 * Note that calls to this function can occur at any time, not just in
322 * response to a getApps request. For example, when a user installs/uninstalls
323 * an app on another synchronized devices.
324 * @param {Object} data An object with all the data on available
325 * applications.
326 */
327 function getAppsCallback(data) {
328 var startTime = Date.now();
329
330 // Clear any existing apps pages and dots.
331 // TODO(rbyers): It might be nice to preserve animation of dots after an
332 // uninstall. Could we re-use the existing page and dot elements? It seems
333 // unfortunate to have Chrome send us the entire apps list after an
334 // uninstall.
335 while (appsPages.length > 0) {
336 var page = appsPages[0];
337 var dot = page.navigationDot;
338
339 eventTracker.remove(page);
340 page.tearDown();
341 page.parentNode.removeChild(page);
342 dot.parentNode.removeChild(dot);
343 }
344
345 // Get the array of apps and add any special synthesized entries
346 var apps = data.apps;
347
348 // Get a list of page names
349 var pageNames = data.appPageNames;
350
351 function stringListIsEmpty(list) {
352 for (var i = 0; i < list.length; i++) {
353 if (list[i])
354 return false;
355 }
356 return true;
357 }
358
359 // Sort by launch index
360 apps.sort(function(a, b) {
361 return a.app_launch_index - b.app_launch_index;
362 });
363
364 // An app to animate (in case it was just installed).
365 var highlightApp;
366
367 // Add the apps, creating pages as necessary
368 for (var i = 0; i < apps.length; i++) {
369 var app = apps[i];
370 var pageIndex = (app.page_index || 0);
371 while (pageIndex >= appsPages.length) {
372 var pageName = localStrings.getString('appDefaultPageName');
373 if (appsPages.length < pageNames.length)
374 pageName = pageNames[appsPages.length];
375
376 var origPageCount = appsPages.length;
377 appendTilePage(new ntp4.AppsPage(), pageName, true, bookmarksPage);
378 // Confirm that appsPages is a live object, updated when a new page is
379 // added (otherwise we'd have an infinite loop)
380 assert(appsPages.length == origPageCount + 1, 'expected new page');
381 }
382
383 if (app.id == highlightAppId)
384 highlightApp = app;
385 else
386 appsPages[pageIndex].appendApp(app);
387 }
388
389 ntp4.AppsPage.setPromo(data.showPromo ? data : null);
390
391 // Tell the slider about the pages.
392 updateSliderCards();
393
394 if (highlightApp)
395 appAdded(highlightApp, true);
396
397 // Mark the current page.
398 cardSlider.currentCardValue.navigationDot.classList.add('selected');
399 logEvent('apps.layout: ' + (Date.now() - startTime));
400
401 document.documentElement.classList.remove('starting-up');
402 }
403
404 /**
405 * Called by chrome when a new app has been added to chrome or has been
406 * enabled if previously disabled.
407 * @param {Object} appData A data structure full of relevant information for
408 * the app.
409 */
410 function appAdded(appData, opt_highlight) {
411 if (appData.id == highlightAppId) {
412 opt_highlight = true;
413 highlightAppId = null;
414 }
415
416 var pageIndex = appData.page_index || 0;
417
418 if (pageIndex >= appsPages.length) {
419 while (pageIndex >= appsPages.length) {
420 appendTilePage(new ntp4.AppsPage(),
421 localStrings.getString('appDefaultPageName'), true,
422 bookmarksPage);
423 }
424 updateSliderCards();
425 }
426
427 var page = appsPages[pageIndex];
428 var app = $(appData.id);
429 if (app)
430 app.replaceAppData(appData);
431 else
432 page.appendApp(appData, opt_highlight);
433 }
434
435 /**
436 * Sets that an app should be highlighted if it is added. Called right before
437 * appAdded for new installs.
438 */
439 function setAppToBeHighlighted(appId) {
440 highlightAppId = appId;
441 }
442
443 /**
444 * Called by chrome when an existing app has been disabled or
445 * removed/uninstalled from chrome.
446 * @param {Object} appData A data structure full of relevant information for
447 * the app.
448 * @param {boolean} isUninstall True if the app is being uninstalled;
449 * false if the app is being disabled.
450 */
451 function appRemoved(appData, isUninstall) {
452 var app = $(appData.id);
453 assert(app, 'trying to remove an app that doesn\'t exist');
454
455 if (!isUninstall)
456 app.replaceAppData(appData);
457 else
458 app.remove();
459 }
460
461 /**
462 * Given a theme resource name, construct a URL for it.
463 * @param {string} resourceName The name of the resource.
464 * @return {string} A url which can be used to load the resource.
465 */
466 function getThemeUrl(resourceName) {
467 return 'chrome://theme/' + resourceName;
468 }
469
470 /**
471 * Callback invoked by chrome whenever an app preference changes.
472 * @param {Object} data An object with all the data on available
473 * applications.
474 */
475 function appsPrefChangeCallback(data) {
476 for (var i = 0; i < data.apps.length; ++i) {
477 $(data.apps[i].id).appData = data.apps[i];
478 }
479
480 // Set the App dot names. Skip the first and last dots (Most Visited and
481 // Bookmarks).
482 var dots = dotList.getElementsByClassName('dot');
483 // TODO(csilv): Remove this calcluation if/when we remove the flag for
484 // for the bookmarks page.
485 var length = bookmarksPage ? dots.length - 1 : dots.length;
486 for (var i = 1; i < length; ++i) {
487 dots[i].displayTitle = data.appPageNames[i - 1] || '';
488 }
489 }
490
491 /**
492 * Listener for offline status change events. Updates apps that are
493 * not offline-enabled to be grayscale if the browser is offline.
494 */
495 function updateOfflineEnabledApps() {
496 var apps = document.querySelectorAll('.app');
497 for (var i = 0; i < apps.length; ++i) {
498 if (apps[i].appData.enabled && !apps[i].appData.offline_enabled) {
499 apps[i].setIcon();
500 apps[i].loadIcon();
501 }
502 }
503 }
504
505 function getCardSlider() {
506 return cardSlider;
507 }
508
509 /**
510 * Invoked whenever the pages in apps-page-list have changed so that
511 * the Slider knows about the new elements.
512 */
513 function updateSliderCards() {
514 var pageNo = Math.min(cardSlider.currentCard, tilePages.length - 1);
515 cardSlider.setCards(Array.prototype.slice.call(tilePages), pageNo);
516 switch (shownPage) {
517 case templateData['apps_page_id']:
518 cardSlider.selectCardByValue(
519 appsPages[Math.min(shownPageIndex, appsPages.length - 1)]);
520 break;
521 case templateData['bookmarks_page_id']:
522 if (bookmarksPage)
523 cardSlider.selectCardByValue(bookmarksPage);
524 break;
525 case templateData['most_visited_page_id']:
526 cardSlider.selectCardByValue(mostVisitedPage);
527 break;
528 }
529 }
530
531 /**
532 * Appends a tile page (for bookmarks or most visited).
533 *
534 * @param {TilePage} page The page element.
535 * @param {string} title The title of the tile page.
536 * @param {bool} titleIsEditable If true, the title can be changed.
537 * @param {TilePage} opt_refNode Optional reference node to insert in front
538 * of.
539 * When opt_refNode is falsey, |page| will just be appended to the end of the
540 * page list.
541 */
542 function appendTilePage(page, title, titleIsEditable, opt_refNode) {
543 // When opt_refNode is falsey, insertBefore acts just like appendChild.
544 pageList.insertBefore(page, opt_refNode);
545
546 // If we're appending an AppsPage and it's a temporary page, animate it.
547 var animate = page instanceof ntp4.AppsPage &&
548 page.classList.contains('temporary');
549 // Make a deep copy of the dot template to add a new one.
550 var newDot = new ntp4.NavDot(page, title, titleIsEditable, animate);
551 page.navigationDot = newDot;
552 dotList.insertBefore(newDot, opt_refNode ? opt_refNode.navigationDot
553 : null);
554 // Set a tab index on the first dot.
555 if (navDots.length == 1)
556 newDot.tabIndex = 3;
557
558 if (infoBubble)
559 window.setTimeout(infoBubble.reposition.bind(infoBubble), 0);
560
561 eventTracker.add(page, 'pagelayout', onPageLayout);
562 }
563
564 /**
565 * Search an elements ancestor chain for the nearest element that is a member
566 * of the specified class.
567 * @param {!Element} element The element to start searching from.
568 * @param {string} className The name of the class to locate.
569 * @return {Element} The first ancestor of the specified class or null.
570 */
571 function getParentByClassName(element, className) {
572 for (var e = element; e; e = e.parentElement) {
573 if (e.classList.contains(className))
574 return e;
575 }
576 return null;
577 }
578
579 /**
580 * Called whenever tiles should be re-arranging themselves out of the way of a
581 * moving or insert tile.
582 */
583 function enterRearrangeMode() {
584 var tempPage = new ntp4.AppsPage();
585 tempPage.classList.add('temporary');
586 appendTilePage(tempPage, localStrings.getString('appDefaultPageName'),
587 true, bookmarksPage);
588 var tempIndex = Array.prototype.indexOf.call(tilePages, tempPage);
589 if (cardSlider.currentCard >= tempIndex)
590 cardSlider.currentCard += 1;
591 updateSliderCards();
592
593 if (ntp4.getCurrentlyDraggingTile().firstChild.canBeRemoved())
594 $('footer').classList.add('showing-trash-mode');
595 }
596
597 /**
598 * Invoked whenever some app is released
599 * @param {Grabber.Event} e The Grabber RELEASE event.
600 */
601 function leaveRearrangeMode(e) {
602 var tempPage = document.querySelector('.tile-page.temporary');
603 var dot = tempPage.navigationDot;
604 if (!tempPage.tileCount && tempPage != cardSlider.currentCardValue) {
605 dot.animateRemove();
606 var tempIndex = Array.prototype.indexOf.call(tilePages, tempPage);
607 if (cardSlider.currentCard > tempIndex)
608 cardSlider.currentCard -= 1;
609 tempPage.parentNode.removeChild(tempPage);
610 updateSliderCards();
611 } else {
612 tempPage.classList.remove('temporary');
613 saveAppPageName(tempPage, localStrings.getString('appDefaultPageName'));
614 }
615
616 $('footer').classList.remove('showing-trash-mode');
617 }
618
619 /**
620 * Handler for key events on the page. Ctrl-Arrow will switch the visible
621 * page.
622 * @param {Event} e The KeyboardEvent.
623 */
624 function onKeyDown(e) {
625 if (!e.ctrlKey || e.altKey || e.metaKey || e.shiftKey)
626 return;
627
628 var direction = 0;
629 if (e.keyIdentifier == 'Left')
630 direction = -1;
631 else if (e.keyIdentifier == 'Right')
632 direction = 1;
633 else
634 return;
635
636 var cardIndex =
637 (cardSlider.currentCard + direction + cardSlider.cardCount) %
638 cardSlider.cardCount;
639 cardSlider.selectCard(cardIndex, true);
640
641 e.stopPropagation();
642 }
643
644 /**
645 * Handler for key events on the dot list. These keys will change the focus
646 * element.
647 * @param {Event} e The KeyboardEvent.
648 */
649 function onDotListKeyDown(e) {
650 if (e.metaKey || e.shiftKey || e.altKey || e.ctrlKey)
651 return;
652
653 var direction = 0;
654 if (e.keyIdentifier == 'Left')
655 direction = -1;
656 else if (e.keyIdentifier == 'Right')
657 direction = 1;
658 else
659 return;
660
661 var focusDot = dotList.querySelector('.dot:focus');
662 if (!focusDot)
663 return;
664 var focusIndex = Array.prototype.indexOf.call(navDots, focusDot);
665 var newFocusIndex = focusIndex + direction;
666 if (focusIndex == newFocusIndex)
667 return;
668
669 newFocusIndex = (newFocusIndex + navDots.length) % navDots.length;
670 navDots[newFocusIndex].tabIndex = 3;
671 navDots[newFocusIndex].focus();
672 focusDot.tabIndex = -1;
673
674 e.stopPropagation();
675 e.preventDefault();
676 }
677
678 /**
679 * Callback for the 'click' event on a page switcher.
680 * @param {Event} e The event.
681 */
682 function onPageSwitcherClicked(e) {
683 cardSlider.selectCard(cardSlider.currentCard +
684 (e.currentTarget == pageSwitcherStart ? -1 : 1), true);
685 }
686
687 /**
688 * Handler for the mousewheel event on a pager. We pass through the scroll
689 * to the page.
690 * @param {Event} e The mousewheel event.
691 */
692 function onPageSwitcherScrolled(e) {
693 cardSlider.currentCardValue.scrollBy(-e.wheelDeltaY);
694 };
695
696 /**
697 * Callback for the 'pagelayout' event.
698 * @param {Event} e The event.
699 */
700 function onPageLayout(e) {
701 if (Array.prototype.indexOf.call(tilePages, e.currentTarget) !=
702 cardSlider.currentCard) {
703 return;
704 }
705
706 updatePageSwitchers();
707 }
708
709 /**
710 * Adjusts the size and position of the page switchers according to the
711 * layout of the current card.
712 */
713 function updatePageSwitchers() {
714 var page = cardSlider.currentCardValue;
715
716 pageSwitcherStart.hidden = !page || (cardSlider.currentCard == 0);
717 pageSwitcherEnd.hidden = !page ||
718 (cardSlider.currentCard == cardSlider.cardCount - 1);
719
720 if (!page)
721 return;
722
723 var pageSwitcherLeft = isRTL() ? pageSwitcherEnd : pageSwitcherStart;
724 var pageSwitcherRight = isRTL() ? pageSwitcherStart : pageSwitcherEnd;
725 var scrollbarWidth = page.scrollbarWidth;
726 pageSwitcherLeft.style.width =
727 (page.sideMargin + 13) + 'px';
728 pageSwitcherLeft.style.left = '0';
729 pageSwitcherRight.style.width =
730 (page.sideMargin - scrollbarWidth + 13) + 'px';
731 pageSwitcherRight.style.right = scrollbarWidth + 'px';
732
733 var offsetTop = page.querySelector('.tile-page-content').offsetTop + 'px';
734 pageSwitcherLeft.style.top = offsetTop;
735 pageSwitcherRight.style.top = offsetTop;
736 pageSwitcherLeft.style.paddingBottom = offsetTop;
737 pageSwitcherRight.style.paddingBottom = offsetTop;
738 }
739
740 /**
741 * Returns the index of the given page.
742 * @param {AppsPage} page The AppsPage for we wish to find.
743 * @return {number} The index of |page|, or -1 if it is not here.
744 */
745 function getAppsPageIndex(page) {
746 return Array.prototype.indexOf.call(appsPages, page);
747 }
748
749 // TODO(estade): rename newtab.css to new_tab_theme.css 182 // TODO(estade): rename newtab.css to new_tab_theme.css
750 function themeChanged(hasAttribution) { 183 function themeChanged(hasAttribution) {
751 $('themecss').href = 'chrome://theme/css/newtab.css?' + Date.now(); 184 $('themecss').href = 'chrome://theme/css/newtab.css?' + Date.now();
752 if (typeof hasAttribution != 'undefined') 185 if (typeof hasAttribution != 'undefined')
753 document.documentElement.setAttribute('hasattribution', hasAttribution); 186 document.documentElement.setAttribute('hasattribution', hasAttribution);
754 updateLogo(); 187 updateLogo();
755 updateAttribution(); 188 updateAttribution();
756 } 189 }
757 190
758 /** 191 /**
(...skipping 15 matching lines...) Expand all
774 if (document.documentElement.getAttribute('hasattribution') == 'true') { 207 if (document.documentElement.getAttribute('hasattribution') == 'true') {
775 $('attribution-img').src = 208 $('attribution-img').src =
776 'chrome://theme/IDR_THEME_NTP_ATTRIBUTION?' + Date.now(); 209 'chrome://theme/IDR_THEME_NTP_ATTRIBUTION?' + Date.now();
777 attribution.hidden = false; 210 attribution.hidden = false;
778 } else { 211 } else {
779 attribution.hidden = true; 212 attribution.hidden = true;
780 } 213 }
781 } 214 }
782 215
783 /** 216 /**
784 * Handler for CARD_CHANGED on cardSlider.
785 * @param {Event} e The CARD_CHANGED event.
786 */
787 function cardChangedHandler(e) {
788 var page = e.cardSlider.currentCardValue;
789
790 // Don't change shownPage until startup is done (and page changes actually
791 // reflect user actions).
792 if (!document.documentElement.classList.contains('starting-up')) {
793 if (page.classList.contains('apps-page')) {
794 shownPage = templateData['apps_page_id'];
795 shownPageIndex = getAppsPageIndex(page);
796 } else if (page.classList.contains('most-visited-page')) {
797 shownPage = templateData['most_visited_page_id'];
798 shownPageIndex = 0;
799 } else if (page.classList.contains('bookmarks-page')) {
800 shownPage = templateData['bookmarks_page_id'];
801 shownPageIndex = 0;
802 } else {
803 console.error('unknown page selected');
804 }
805 chrome.send('pageSelected', [shownPage, shownPageIndex]);
806 }
807
808 // Update the active dot
809 var curDot = dotList.getElementsByClassName('selected')[0];
810 if (curDot)
811 curDot.classList.remove('selected');
812 page.navigationDot.classList.add('selected');
813 updatePageSwitchers();
814 }
815
816 /**
817 * Timeout ID. 217 * Timeout ID.
818 * @type {number} 218 * @type {number}
819 */ 219 */
820 var notificationTimeout_ = 0; 220 var notificationTimeout_ = 0;
821 221
822 /** 222 /**
823 * Shows the notification bubble. 223 * Shows the notification bubble.
824 * @param {string|Node} message The notification message or node to use as 224 * @param {string|Node} message The notification message or node to use as
825 * message. 225 * message.
826 * @param {Array.<{text: string, action: function()}>} links An array of 226 * @param {Array.<{text: string, action: function()}>} links An array of
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
883 function onNotificationTransitionEnd(e) { 283 function onNotificationTransitionEnd(e) {
884 if (notificationContainer.classList.contains('inactive')); 284 if (notificationContainer.classList.contains('inactive'));
885 notificationContainer.hidden = true; 285 notificationContainer.hidden = true;
886 } 286 }
887 287
888 function setRecentlyClosedTabs(dataItems) { 288 function setRecentlyClosedTabs(dataItems) {
889 $('recently-closed-menu-button').dataItems = dataItems; 289 $('recently-closed-menu-button').dataItems = dataItems;
890 } 290 }
891 291
892 function setMostVisitedPages(data, hasBlacklistedUrls) { 292 function setMostVisitedPages(data, hasBlacklistedUrls) {
893 mostVisitedPage.data = data; 293 newTabView.mostVisitedPage.data = data;
894 } 294 }
895 295
896 function setBookmarksData(data) { 296 function setBookmarksData(data) {
897 bookmarksPage.data = data; 297 newTabView.bookmarksPage.data = data;
298 }
299
300 function bookmarkImportBegan() {
301 newTabView.bookmarksPage.bookmarkImportBegan.apply(
302 newTabView.bookmarksPage,
303 arguments);
304 }
305
306 function bookmarkImportEnded() {
307 newTabView.bookmarksPage.bookmarkImportEnded.apply(
308 newTabView.bookmarksPage,
309 arguments);
310 }
311
312 function bookmarkNodeAdded() {
313 newTabView.bookmarksPage.bookmarkNodeAdded.apply(
314 newTabView.bookmarksPage,
315 arguments);
316 }
317
318 function bookmarkNodeChanged() {
319 newTabView.bookmarksPage.bookmarkNodeChanged.apply(
320 newTabView.bookmarksPage,
321 arguments);
322 }
323
324 function bookmarkNodeChildrenReordered() {
325 newTabView.bookmarksPage.bookmarkNodeChildrenReordered.apply(
326 newTabView.bookmarksPage,
327 arguments);
328 }
329
330 function bookmarkNodeMoved() {
331 newTabView.bookmarksPage.bookmarkNodeMoved.apply(
332 newTabView.bookmarksPage,
333 arguments);
334 }
335
336 function bookmarkNodeRemoved() {
337 newTabView.bookmarksPage.bookmarkNodeRemoved.apply(
338 newTabView.bookmarksPage,
339 arguments);
898 } 340 }
899 341
900 /** 342 /**
901 * Check the directionality of the page.
902 * @return {boolean} True if Chrome is running an RTL UI.
903 */
904 function isRTL() {
905 return document.documentElement.dir == 'rtl';
906 }
907
908 /*
909 * Save the name of an app page.
910 * Store the app page name into the preferences store.
911 * @param {AppsPage} appPage The app page for which we wish to save.
912 * @param {string} name The name of the page.
913 */
914 function saveAppPageName(appPage, name) {
915 var index = getAppsPageIndex(appPage);
916 assert(index != -1);
917 chrome.send('saveAppPageName', [name, index]);
918 }
919
920 function bookmarkImportBegan() {
921 bookmarksPage.bookmarkImportBegan.apply(bookmarksPage, arguments);
922 }
923
924 function bookmarkImportEnded() {
925 bookmarksPage.bookmarkImportEnded.apply(bookmarksPage, arguments);
926 }
927
928 function bookmarkNodeAdded() {
929 bookmarksPage.bookmarkNodeAdded.apply(bookmarksPage, arguments);
930 }
931
932 function bookmarkNodeChanged() {
933 bookmarksPage.bookmarkNodeChanged.apply(bookmarksPage, arguments);
934 }
935
936 function bookmarkNodeChildrenReordered() {
937 bookmarksPage.bookmarkNodeChildrenReordered.apply(bookmarksPage, arguments);
938 }
939
940 function bookmarkNodeMoved() {
941 bookmarksPage.bookmarkNodeMoved.apply(bookmarksPage, arguments);
942 }
943
944 function bookmarkNodeRemoved() {
945 bookmarksPage.bookmarkNodeRemoved.apply(bookmarksPage, arguments);
946 }
947
948 /**
949 * Set the dominant color for a node. This will be called in response to 343 * Set the dominant color for a node. This will be called in response to
950 * getFaviconDominantColor. The node represented by |id| better have a setter 344 * getFaviconDominantColor. The node represented by |id| better have a setter
951 * for stripeColor. 345 * for stripeColor.
952 * @param {string} id The ID of a node. 346 * @param {string} id The ID of a node.
953 * @param {string} color The color represented as a CSS string. 347 * @param {string} color The color represented as a CSS string.
954 */ 348 */
955 function setStripeColor(id, color) { 349 function setStripeColor(id, color) {
956 var node = $(id); 350 var node = $(id);
957 if (node) 351 if (node)
958 node.stripeColor = color; 352 node.stripeColor = color;
(...skipping 17 matching lines...) Expand all
976 } 370 }
977 if (shouldShowLoginBubble) { 371 if (shouldShowLoginBubble) {
978 window.setTimeout(loginBubble.show.bind(loginBubble), 0); 372 window.setTimeout(loginBubble.show.bind(loginBubble), 0);
979 chrome.send('loginMessageSeen'); 373 chrome.send('loginMessageSeen');
980 shouldShowLoginBubble = false; 374 shouldShowLoginBubble = false;
981 } 375 }
982 } 376 }
983 377
984 // Return an object with all the exports 378 // Return an object with all the exports
985 return { 379 return {
986 appAdded: appAdded,
987 appRemoved: appRemoved,
988 appsPrefChangeCallback: appsPrefChangeCallback,
989 assert: assert,
990 bookmarkImportBegan: bookmarkImportBegan, 380 bookmarkImportBegan: bookmarkImportBegan,
991 bookmarkImportEnded: bookmarkImportEnded, 381 bookmarkImportEnded: bookmarkImportEnded,
992 bookmarkNodeAdded: bookmarkNodeAdded, 382 bookmarkNodeAdded: bookmarkNodeAdded,
993 bookmarkNodeChanged: bookmarkNodeChanged, 383 bookmarkNodeChanged: bookmarkNodeChanged,
994 bookmarkNodeChildrenReordered: bookmarkNodeChildrenReordered, 384 bookmarkNodeChildrenReordered: bookmarkNodeChildrenReordered,
995 bookmarkNodeMoved: bookmarkNodeMoved, 385 bookmarkNodeMoved: bookmarkNodeMoved,
996 bookmarkNodeRemoved: bookmarkNodeRemoved, 386 bookmarkNodeRemoved: bookmarkNodeRemoved,
997 enterRearrangeMode: enterRearrangeMode,
998 getAppsCallback: getAppsCallback,
999 getAppsPageIndex: getAppsPageIndex,
1000 getCardSlider: getCardSlider,
1001 initialize: initialize, 387 initialize: initialize,
1002 isRTL: isRTL,
1003 leaveRearrangeMode: leaveRearrangeMode,
1004 saveAppPageName: saveAppPageName,
1005 setAppToBeHighlighted: setAppToBeHighlighted,
1006 setBookmarksData: setBookmarksData, 388 setBookmarksData: setBookmarksData,
1007 setMostVisitedPages: setMostVisitedPages, 389 setMostVisitedPages: setMostVisitedPages,
1008 setRecentlyClosedTabs: setRecentlyClosedTabs, 390 setRecentlyClosedTabs: setRecentlyClosedTabs,
1009 setStripeColor: setStripeColor, 391 setStripeColor: setStripeColor,
1010 showNotification: showNotification, 392 showNotification: showNotification,
1011 themeChanged: themeChanged, 393 themeChanged: themeChanged,
1012 updateLogin: updateLogin, 394 updateLogin: updateLogin
1013 updateOfflineEnabledApps: updateOfflineEnabledApps
1014 }; 395 };
1015 }); 396 });
1016 397
1017 // publish ntp globals 398 // publish ntp globals
1018 // TODO(estade): update the content handlers to use ntp namespace instead of 399 // TODO(estade): update the content handlers to use ntp namespace instead of
1019 // making these global. 400 // making these global.
1020 var assert = ntp4.assert;
1021 var getAppsCallback = ntp4.getAppsCallback;
1022 var appsPrefChangeCallback = ntp4.appsPrefChangeCallback;
1023 var themeChanged = ntp4.themeChanged; 401 var themeChanged = ntp4.themeChanged;
1024 var recentlyClosedTabs = ntp4.setRecentlyClosedTabs; 402 var recentlyClosedTabs = ntp4.setRecentlyClosedTabs;
1025 var setMostVisitedPages = ntp4.setMostVisitedPages; 403 var setMostVisitedPages = ntp4.setMostVisitedPages;
1026 var updateLogin = ntp4.updateLogin; 404 var updateLogin = ntp4.updateLogin;
1027 405
1028 document.addEventListener('DOMContentLoaded', ntp4.initialize); 406 document.addEventListener('DOMContentLoaded', ntp4.initialize);
1029 window.addEventListener('online', ntp4.updateOfflineEnabledApps);
1030 window.addEventListener('offline', ntp4.updateOfflineEnabledApps);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698