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

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: sync and address comments in patch 7 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. NewTabView extends PageListView with
66 * new tab UI specific logics.
67 * @constructor
68 * @extends {PageListView}
69 */
70 function NewTabView() {
71 this.initialize(getRequiredElement('page-list'),
72 getRequiredElement('dot-list'),
73 getRequiredElement('card-slider-frame'),
74 getRequiredElement('trash'),
75 $('page-switcher-start'),
Evan Stade 2011/11/10 00:02:21 getRequiredElement?
xiyuan 2011/11/10 18:25:07 Done.
76 $('page-switcher-end'));
77 }
78
79 NewTabView.prototype = {
80 __proto__: ntp4.PageListView.prototype,
81
82 /** @inheritDoc */
83 appendTilePage: function(page, title, titleIsEditable, opt_refNode) {
84 ntp4.PageListView.prototype.appendTilePage.apply(this, arguments);
85
86 if (infoBubble)
87 window.setTimeout(infoBubble.reposition.bind(infoBubble), 0);
88 }
89 };
90
91 /**
149 * Invoked at startup once the DOM is available to initialize the app. 92 * Invoked at startup once the DOM is available to initialize the app.
150 */ 93 */
151 function initialize() { 94 function load() {
152 cr.enablePlatformSpecificCSSRules(); 95 cr.enablePlatformSpecificCSSRules();
153 96
154 // Load the current theme colors. 97 // Load the current theme colors.
155 themeChanged(); 98 themeChanged();
156 99
157 dotList = getRequiredElement('dot-list'); 100 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 101
187 notificationContainer = getRequiredElement('notification-container'); 102 notificationContainer = getRequiredElement('notification-container');
188 notificationContainer.addEventListener( 103 notificationContainer.addEventListener(
189 'webkitTransitionEnd', onNotificationTransitionEnd); 104 'webkitTransitionEnd', onNotificationTransitionEnd);
190 105
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); 106 cr.ui.decorate($('recently-closed-menu-button'), ntp4.RecentMenuButton);
208 chrome.send('getRecentlyClosedTabs'); 107 chrome.send('getRecentlyClosedTabs');
209 108
210 mostVisitedPage = new ntp4.MostVisitedPage(); 109 newTabView.appendTilePage(new ntp4.MostVisitedPage(),
211 appendTilePage(mostVisitedPage, localStrings.getString('mostvisited'), 110 localStrings.getString('mostvisited'),
212 false); 111 false);
213 chrome.send('getMostVisited'); 112 chrome.send('getMostVisited');
214 113
215 if (localStrings.getString('login_status_message')) { 114 if (localStrings.getString('login_status_message')) {
216 loginBubble = new cr.ui.Bubble; 115 loginBubble = new cr.ui.Bubble;
217 loginBubble.anchorNode = $('login-container'); 116 loginBubble.anchorNode = $('login-container');
218 loginBubble.setArrowLocation(cr.ui.ArrowLocation.TOP_END); 117 loginBubble.setArrowLocation(cr.ui.ArrowLocation.TOP_END);
219 loginBubble.bubbleAlignment = 118 loginBubble.bubbleAlignment =
220 cr.ui.BubbleAlignment.BUBBLE_EDGE_TO_ANCHOR_EDGE; 119 cr.ui.BubbleAlignment.BUBBLE_EDGE_TO_ANCHOR_EDGE;
221 loginBubble.deactivateToDismissDelay = 2000; 120 loginBubble.deactivateToDismissDelay = 2000;
222 loginBubble.setCloseButtonVisible(false); 121 loginBubble.setCloseButtonVisible(false);
(...skipping 12 matching lines...) Expand all
235 } 134 }
236 135
237 var dismissButton = loginBubble.querySelector('#login-status-dismiss'); 136 var dismissButton = loginBubble.querySelector('#login-status-dismiss');
238 dismissButton.onclick = loginBubble.hide.bind(loginBubble); 137 dismissButton.onclick = loginBubble.hide.bind(loginBubble);
239 138
240 // The anchor node won't be updated until updateLogin is called so don't 139 // The anchor node won't be updated until updateLogin is called so don't
241 // show the bubble yet. 140 // show the bubble yet.
242 shouldShowLoginBubble = true; 141 shouldShowLoginBubble = true;
243 } else if (localStrings.getString('ntp4_intro_message')) { 142 } else if (localStrings.getString('ntp4_intro_message')) {
244 infoBubble = new cr.ui.Bubble; 143 infoBubble = new cr.ui.Bubble;
245 infoBubble.anchorNode = mostVisitedPage.navigationDot; 144 infoBubble.anchorNode = newTabView.mostVisitedPage.navigationDot;
246 infoBubble.setArrowLocation(cr.ui.ArrowLocation.BOTTOM_START); 145 infoBubble.setArrowLocation(cr.ui.ArrowLocation.BOTTOM_START);
247 infoBubble.handleCloseEvent = function() { 146 infoBubble.handleCloseEvent = function() {
248 this.hide(); 147 this.hide();
249 chrome.send('introMessageDismissed'); 148 chrome.send('introMessageDismissed');
250 } 149 }
251 150
252 var bubbleContent = $('ntp4-intro-bubble-contents'); 151 var bubbleContent = $('ntp4-intro-bubble-contents');
253 infoBubble.content = bubbleContent; 152 infoBubble.content = bubbleContent;
254 bubbleContent.hidden = false; 153 bubbleContent.hidden = false;
255 154
256 var learnMoreLink = infoBubble.querySelector('a'); 155 var learnMoreLink = infoBubble.querySelector('a');
257 learnMoreLink.href = localStrings.getString('ntp4_intro_url'); 156 learnMoreLink.href = localStrings.getString('ntp4_intro_url');
258 learnMoreLink.onclick = infoBubble.hide.bind(infoBubble); 157 learnMoreLink.onclick = infoBubble.hide.bind(infoBubble);
259 158
260 infoBubble.show(); 159 infoBubble.show();
261 } 160 }
262 161
263 var bookmarkFeatures = localStrings.getString('bookmark_features'); 162 var bookmarkFeatures = localStrings.getString('bookmark_features');
264 if (bookmarkFeatures == 'true') { 163 if (bookmarkFeatures == 'true') {
265 bookmarksPage = new ntp4.BookmarksPage(); 164 newTabView.appendTilePage(new ntp4.BookmarksPage(),
266 appendTilePage(bookmarksPage, localStrings.getString('bookmarksPage'), 165 localStrings.getString('bookmarksPage'),
267 false); 166 false);
268 chrome.send('getBookmarksData'); 167 chrome.send('getBookmarksData');
269 } 168 }
270 169
271 var serverpromo = localStrings.getString('serverpromo'); 170 var serverpromo = localStrings.getString('serverpromo');
272 if (serverpromo) { 171 if (serverpromo) {
273 showNotification(parseHtmlSubset(serverpromo), [], function() { 172 showNotification(parseHtmlSubset(serverpromo), [], function() {
274 chrome.send('closeNotificationPromo'); 173 chrome.send('closeNotificationPromo');
275 }, 60000); 174 }, 60000);
276 chrome.send('notificationPromoViewed'); 175 chrome.send('notificationPromoViewed');
277 } 176 }
278 177
279 var loginContainer = getRequiredElement('login-container'); 178 var loginContainer = getRequiredElement('login-container');
280 loginContainer.addEventListener('click', function() { 179 loginContainer.addEventListener('click', function() {
281 var rect = loginContainer.getBoundingClientRect(); 180 var rect = loginContainer.getBoundingClientRect();
282 chrome.send('showSyncLoginUI', 181 chrome.send('showSyncLoginUI',
283 [rect.left, rect.top, rect.width, rect.height]); 182 [rect.left, rect.top, rect.width, rect.height]);
284 }); 183 });
285 chrome.send('initializeSyncLogin'); 184 chrome.send('initializeSyncLogin');
286 } 185 }
287 186
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 187 // TODO(estade): rename newtab.css to new_tab_theme.css
750 function themeChanged(hasAttribution) { 188 function themeChanged(hasAttribution) {
751 $('themecss').href = 'chrome://theme/css/newtab.css?' + Date.now(); 189 $('themecss').href = 'chrome://theme/css/newtab.css?' + Date.now();
752 if (typeof hasAttribution != 'undefined') 190 if (typeof hasAttribution != 'undefined')
753 document.documentElement.setAttribute('hasattribution', hasAttribution); 191 document.documentElement.setAttribute('hasattribution', hasAttribution);
754 updateLogo(); 192 updateLogo();
755 updateAttribution(); 193 updateAttribution();
756 } 194 }
757 195
758 /** 196 /**
(...skipping 15 matching lines...) Expand all
774 if (document.documentElement.getAttribute('hasattribution') == 'true') { 212 if (document.documentElement.getAttribute('hasattribution') == 'true') {
775 $('attribution-img').src = 213 $('attribution-img').src =
776 'chrome://theme/IDR_THEME_NTP_ATTRIBUTION?' + Date.now(); 214 'chrome://theme/IDR_THEME_NTP_ATTRIBUTION?' + Date.now();
777 attribution.hidden = false; 215 attribution.hidden = false;
778 } else { 216 } else {
779 attribution.hidden = true; 217 attribution.hidden = true;
780 } 218 }
781 } 219 }
782 220
783 /** 221 /**
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. 222 * Timeout ID.
818 * @type {number} 223 * @type {number}
819 */ 224 */
820 var notificationTimeout_ = 0; 225 var notificationTimeout_ = 0;
821 226
822 /** 227 /**
823 * Shows the notification bubble. 228 * Shows the notification bubble.
824 * @param {string|Node} message The notification message or node to use as 229 * @param {string|Node} message The notification message or node to use as
825 * message. 230 * message.
826 * @param {Array.<{text: string, action: function()}>} links An array of 231 * @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) { 288 function onNotificationTransitionEnd(e) {
884 if (notificationContainer.classList.contains('inactive')); 289 if (notificationContainer.classList.contains('inactive'));
885 notificationContainer.hidden = true; 290 notificationContainer.hidden = true;
886 } 291 }
887 292
888 function setRecentlyClosedTabs(dataItems) { 293 function setRecentlyClosedTabs(dataItems) {
889 $('recently-closed-menu-button').dataItems = dataItems; 294 $('recently-closed-menu-button').dataItems = dataItems;
890 } 295 }
891 296
892 function setMostVisitedPages(data, hasBlacklistedUrls) { 297 function setMostVisitedPages(data, hasBlacklistedUrls) {
893 mostVisitedPage.data = data; 298 newTabView.mostVisitedPage.data = data;
894 } 299 }
895 300
896 function setBookmarksData(data) { 301 function setBookmarksData(data) {
897 bookmarksPage.data = data; 302 newTabView.bookmarksPage.data = data;
303 }
304
305 function bookmarkImportBegan() {
306 newTabView.bookmarksPage.bookmarkImportBegan.apply(
307 newTabView.bookmarksPage,
308 arguments);
309 }
310
311 function bookmarkImportEnded() {
312 newTabView.bookmarksPage.bookmarkImportEnded.apply(
313 newTabView.bookmarksPage,
314 arguments);
315 }
316
317 function bookmarkNodeAdded() {
318 newTabView.bookmarksPage.bookmarkNodeAdded.apply(
319 newTabView.bookmarksPage,
320 arguments);
321 }
322
323 function bookmarkNodeChanged() {
324 newTabView.bookmarksPage.bookmarkNodeChanged.apply(
325 newTabView.bookmarksPage,
326 arguments);
327 }
328
329 function bookmarkNodeChildrenReordered() {
330 newTabView.bookmarksPage.bookmarkNodeChildrenReordered.apply(
331 newTabView.bookmarksPage,
332 arguments);
333 }
334
335 function bookmarkNodeMoved() {
336 newTabView.bookmarksPage.bookmarkNodeMoved.apply(
337 newTabView.bookmarksPage,
338 arguments);
339 }
340
341 function bookmarkNodeRemoved() {
342 newTabView.bookmarksPage.bookmarkNodeRemoved.apply(
343 newTabView.bookmarksPage,
344 arguments);
898 } 345 }
899 346
900 /** 347 /**
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 348 * 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 349 * getFaviconDominantColor. The node represented by |id| better have a setter
951 * for stripeColor. 350 * for stripeColor.
952 * @param {string} id The ID of a node. 351 * @param {string} id The ID of a node.
953 * @param {string} color The color represented as a CSS string. 352 * @param {string} color The color represented as a CSS string.
954 */ 353 */
955 function setStripeColor(id, color) { 354 function setStripeColor(id, color) {
956 var node = $(id); 355 var node = $(id);
957 if (node) 356 if (node)
958 node.stripeColor = color; 357 node.stripeColor = color;
(...skipping 15 matching lines...) Expand all
974 $('login-container').hidden = true; 373 $('login-container').hidden = true;
975 $('card-slider-frame').classList.remove('showing-login-area'); 374 $('card-slider-frame').classList.remove('showing-login-area');
976 } 375 }
977 if (shouldShowLoginBubble) { 376 if (shouldShowLoginBubble) {
978 window.setTimeout(loginBubble.show.bind(loginBubble), 0); 377 window.setTimeout(loginBubble.show.bind(loginBubble), 0);
979 chrome.send('loginMessageSeen'); 378 chrome.send('loginMessageSeen');
980 shouldShowLoginBubble = false; 379 shouldShowLoginBubble = false;
981 } 380 }
982 } 381 }
983 382
383 /**
384 * Wrappers to forward the callback to corresponding PageListView member.
385 */
386 function appAdded(appData, opt_highlight) {
387 newTabView.appAdded(appData, opt_highlight);
388 }
389
390 function appRemoved(appData, isUninstall) {
391 newTabView.appRemoved(appData, isUninstall);
392 }
393
394 function appsPrefChangeCallback(data) {
395 newTabView.appsPrefChangedCallback(data);
396 }
397
398 function enterRearrangeMode() {
399 newTabView.enterRearrangeMode();
400 }
401
402 function getAppsCallback(data) {
403 newTabView.getAppsCallback(data);
404 }
405
406 function getAppsPageIndex(page) {
407 newTabView.getAppsPageIndex(page);
408 }
409
410 function getCardSlider() {
411 return newTabView.cardSlider;
412 }
413
414 function leaveRearrangeMode(e) {
415 newTabView.leaveRearrangeMode(e);
416 }
417
418 function saveAppPageName(appPage, name) {
419 newTabView.saveAppPageName(appPage, name);
420 }
421
422 function setAppToBeHighlighted(appId) {
423 newTabView.highlightAppId = appId;
424 }
425
984 // Return an object with all the exports 426 // Return an object with all the exports
985 return { 427 return {
986 appAdded: appAdded, 428 appAdded: appAdded,
987 appRemoved: appRemoved, 429 appRemoved: appRemoved,
988 appsPrefChangeCallback: appsPrefChangeCallback, 430 appsPrefChangeCallback: appsPrefChangeCallback,
989 assert: assert,
990 bookmarkImportBegan: bookmarkImportBegan, 431 bookmarkImportBegan: bookmarkImportBegan,
991 bookmarkImportEnded: bookmarkImportEnded, 432 bookmarkImportEnded: bookmarkImportEnded,
992 bookmarkNodeAdded: bookmarkNodeAdded, 433 bookmarkNodeAdded: bookmarkNodeAdded,
993 bookmarkNodeChanged: bookmarkNodeChanged, 434 bookmarkNodeChanged: bookmarkNodeChanged,
994 bookmarkNodeChildrenReordered: bookmarkNodeChildrenReordered, 435 bookmarkNodeChildrenReordered: bookmarkNodeChildrenReordered,
995 bookmarkNodeMoved: bookmarkNodeMoved, 436 bookmarkNodeMoved: bookmarkNodeMoved,
996 bookmarkNodeRemoved: bookmarkNodeRemoved, 437 bookmarkNodeRemoved: bookmarkNodeRemoved,
997 enterRearrangeMode: enterRearrangeMode, 438 enterRearrangeMode: enterRearrangeMode,
998 getAppsCallback: getAppsCallback, 439 getAppsCallback: getAppsCallback,
999 getAppsPageIndex: getAppsPageIndex, 440 getAppsPageIndex: getAppsPageIndex,
1000 getCardSlider: getCardSlider, 441 getCardSlider: getCardSlider,
1001 initialize: initialize,
1002 isRTL: isRTL,
1003 leaveRearrangeMode: leaveRearrangeMode, 442 leaveRearrangeMode: leaveRearrangeMode,
443 load: load,
1004 saveAppPageName: saveAppPageName, 444 saveAppPageName: saveAppPageName,
1005 setAppToBeHighlighted: setAppToBeHighlighted, 445 setAppToBeHighlighted: setAppToBeHighlighted,
1006 setBookmarksData: setBookmarksData, 446 setBookmarksData: setBookmarksData,
1007 setMostVisitedPages: setMostVisitedPages, 447 setMostVisitedPages: setMostVisitedPages,
1008 setRecentlyClosedTabs: setRecentlyClosedTabs, 448 setRecentlyClosedTabs: setRecentlyClosedTabs,
1009 setStripeColor: setStripeColor, 449 setStripeColor: setStripeColor,
1010 showNotification: showNotification, 450 showNotification: showNotification,
1011 themeChanged: themeChanged, 451 themeChanged: themeChanged,
1012 updateLogin: updateLogin, 452 updateLogin: updateLogin
1013 updateOfflineEnabledApps: updateOfflineEnabledApps
1014 }; 453 };
1015 }); 454 });
1016 455
1017 // publish ntp globals 456 // publish ntp globals
1018 // TODO(estade): update the content handlers to use ntp namespace instead of 457 // TODO(estade): update the content handlers to use ntp namespace instead of
1019 // making these global. 458 // making these global.
1020 var assert = ntp4.assert;
1021 var getAppsCallback = ntp4.getAppsCallback; 459 var getAppsCallback = ntp4.getAppsCallback;
1022 var appsPrefChangeCallback = ntp4.appsPrefChangeCallback; 460 var appsPrefChangeCallback = ntp4.appsPrefChangeCallback;
1023 var themeChanged = ntp4.themeChanged; 461 var themeChanged = ntp4.themeChanged;
1024 var recentlyClosedTabs = ntp4.setRecentlyClosedTabs; 462 var recentlyClosedTabs = ntp4.setRecentlyClosedTabs;
1025 var setMostVisitedPages = ntp4.setMostVisitedPages; 463 var setMostVisitedPages = ntp4.setMostVisitedPages;
1026 var updateLogin = ntp4.updateLogin; 464 var updateLogin = ntp4.updateLogin;
1027 465
1028 document.addEventListener('DOMContentLoaded', ntp4.initialize); 466 document.addEventListener('DOMContentLoaded', ntp4.load);
Evan Stade 2011/11/10 00:02:21 can you make it onLoad
xiyuan 2011/11/10 18:25:07 Done.
1029 window.addEventListener('online', ntp4.updateOfflineEnabledApps);
1030 window.addEventListener('offline', ntp4.updateOfflineEnabledApps);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698