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

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

Powered by Google App Engine
This is Rietveld 408576698