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

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

Issue 12207138: Remove unused ntp_search. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 10 months 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
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6 * @fileoverview New tab page
7 * This is the main code for the new tab page. NewTabView manages page list,
8 * dot list and handles apps pages callbacks from backend. It also handles
9 * the layout of the Bottom Panel and the global UI states of the New Tab Page.
10 */
11
12 // Use an anonymous function to enable strict mode just for this file (which
13 // will be concatenated with other files when embedded in Chrome
14 cr.define('ntp', function() {
15 'use strict';
16
17 var APP_LAUNCH = {
18 // The histogram buckets (keep in sync with extension_constants.h).
19 NTP_APPS_MAXIMIZED: 0,
20 NTP_APPS_COLLAPSED: 1,
21 NTP_APPS_MENU: 2,
22 NTP_MOST_VISITED: 3,
23 NTP_RECENTLY_CLOSED: 4,
24 NTP_APP_RE_ENABLE: 16,
25 NTP_WEBSTORE_FOOTER: 18,
26 NTP_WEBSTORE_PLUS_ICON: 19,
27 };
28
29 /**
30 * @type {number}
31 * @const
32 */
33 var BOTTOM_PANEL_HORIZONTAL_MARGIN = 100;
34
35 /**
36 * The height required to show the Bottom Panel.
37 * @type {number}
38 * @const
39 */
40 var HEIGHT_FOR_BOTTOM_PANEL = 531;
41
42 /**
43 * The Bottom Panel width required to show 6 cols of Tiles, which is used
44 * in the width computation.
45 * @type {number}
46 * @const
47 */
48 var MAX_BOTTOM_PANEL_WIDTH = 920;
49
50 /**
51 * The minimum width of the Bottom Panel's content.
52 * @type {number}
53 * @const
54 */
55 var MIN_BOTTOM_PANEL_CONTENT_WIDTH = 200;
56
57 /**
58 * The minimum Bottom Panel width. If the available width is smaller than
59 * this value, then the width of the Bottom Panel's content will be fixed to
60 * MIN_BOTTOM_PANEL_CONTENT_WIDTH.
61 * @type {number}
62 * @const
63 */
64 var MIN_BOTTOM_PANEL_WIDTH = 300;
65
66 /**
67 * The normal Bottom Panel width. If the window width is greater than or
68 * equal to this value, then the width of the Bottom Panel's content will be
69 * the available width minus side margin. If the available width is smaller
70 * than this value, then the width of the Bottom Panel's content will be an
71 * interpolation between the normal width, and the minimum width defined by
72 * the constant MIN_BOTTOM_PANEL_CONTENT_WIDTH.
73 * @type {number}
74 * @const
75 */
76 var NORMAL_BOTTOM_PANEL_WIDTH = 500;
77
78 /**
79 * @type {number}
80 * @const
81 */
82 var TILE_ROW_HEIGHT = 100;
83
84 //----------------------------------------------------------------------------
85
86 /**
87 * NewTabView instance.
88 * @type {!Object|undefined}
89 */
90 var newTabView;
91
92 /**
93 * The 'notification-container' element.
94 * @type {!Element|undefined}
95 */
96 var notificationContainer;
97
98 /**
99 * If non-null, an info bubble for showing messages to the user. It points at
100 * the Most Visited label, and is used to draw more attention to the
101 * navigation dot UI.
102 * @type {!Element|undefined}
103 */
104 var promoBubble;
105
106 /**
107 * The total number of thumbnails that were hovered over.
108 * @type {number}
109 * @private
110 */
111 var hoveredThumbnailCount = 0;
112
113 /**
114 * The time when all sections are ready.
115 * @type {number|undefined}
116 * @private
117 */
118 var startTime;
119
120 /**
121 * The top position of the Bottom Panel.
122 * @type {number|undefined}
123 * @private
124 */
125 var bottomPanelOffsetTop;
126
127 /**
128 * The height of the Bottom Panel Header, in pixels.
129 * @type {number|undefined}
130 * @private
131 */
132 var headerHeight;
133
134 /**
135 * The time in milliseconds for most transitions. This should match what's
136 * in new_tab.css. Unfortunately there's no better way to try to time
137 * something to occur until after a transition has completed.
138 * @type {number}
139 * @const
140 */
141 var DEFAULT_TRANSITION_TIME = 500;
142
143 /**
144 * See description for these values in ntp_stats.h.
145 * @enum {number}
146 */
147 var NtpFollowAction = {
148 CLICKED_TILE: 11,
149 CLICKED_OTHER_NTP_PANE: 12,
150 OTHER: 13,
151 };
152
153 /**
154 * Creates a NewTabView object.
155 * @constructor
156 */
157 function NewTabView() {
158 this.initialize(getRequiredElement('page-list'),
159 getRequiredElement('dot-list'),
160 getRequiredElement('card-slider-frame'));
161 }
162
163 NewTabView.prototype = {
164 /**
165 * The CardSlider object to use for changing app pages.
166 * @type {CardSlider|undefined}
167 */
168 cardSlider: undefined,
169
170 /**
171 * The frame div for this.cardSlider.
172 * @type {!Element|undefined}
173 */
174 sliderFrame: undefined,
175
176 /**
177 * The 'page-list' element.
178 * @type {!Element|undefined}
179 */
180 pageList: undefined,
181
182 /**
183 * A list of all 'tile-page' elements.
184 * @type {!NodeList|undefined}
185 */
186 tilePages: undefined,
187
188 /**
189 * The Apps page.
190 * @type {!Element|undefined}
191 */
192 appsPage: undefined,
193
194 /**
195 * The Most Visited page.
196 * @type {!Element|undefined}
197 */
198 mostVisitedPage: undefined,
199
200 /**
201 * The Recently Closed page.
202 * @type {!Element|undefined}
203 */
204 recentlyClosedPage: undefined,
205
206 /**
207 * The Devices page.
208 * @type {!Element|undefined}
209 */
210 otherDevicesPage: undefined,
211
212 /**
213 * The 'dots-list' element.
214 * @type {!Element|undefined}
215 */
216 dotList: undefined,
217
218 /**
219 * The type of page that is currently shown. The value is a numerical ID.
220 * @type {number}
221 */
222 shownPage: 0,
223
224 /**
225 * The index of the page that is currently shown, within the page type.
226 * For example if the third Apps page is showing, this will be 2.
227 * @type {number}
228 */
229 shownPageIndex: 0,
230
231 /**
232 * If non-null, this is the ID of the app to highlight to the user the next
233 * time getAppsCallback runs. "Highlight" in this case means to switch to
234 * the page and run the new tile animation.
235 * @type {?string}
236 */
237 highlightAppId: null,
238
239 /**
240 * Initializes new tab view.
241 * @param {!Element} pageList A DIV element to host all pages.
242 * @param {!Element} dotList An UL element to host nav dots. Each dot
243 * represents a page.
244 * @param {!Element} cardSliderFrame The card slider frame that hosts
245 * pageList.
246 */
247 initialize: function(pageList, dotList, cardSliderFrame) {
248 this.pageList = pageList;
249
250 this.dotList = dotList;
251 cr.ui.decorate(this.dotList, ntp.DotList);
252
253 this.shownPage = loadTimeData.getInteger('shown_page_type');
254 this.shownPageIndex = loadTimeData.getInteger('shown_page_index');
255
256 if (loadTimeData.getBoolean('showApps')) {
257 // When the Apps Page is available, then the dot list should be visible.
258 this.dotList.removeAttribute('hidden');
259 // Request data on the apps so we can fill them in.
260 // Note that this is kicked off asynchronously. 'getAppsCallback' will
261 // be invoked at some point after this function returns.
262 chrome.send('getApps');
263 } else if (this.shownPage == loadTimeData.getInteger('apps_page_id')) {
264 // No apps page.
265 this.setShownPage_(
266 loadTimeData.getInteger('most_visited_page_id'), 0);
267 }
268
269 this.tilePages = this.pageList.getElementsByClassName('tile-page');
270
271 // Initialize the cardSlider without any cards at the moment.
272 this.sliderFrame = cardSliderFrame;
273 this.cardSlider = new cr.ui.CardSlider(this.sliderFrame, this.pageList,
274 this.sliderFrame.offsetWidth);
275
276 var cardSlider = this.cardSlider;
277 this.cardSlider.initialize(
278 loadTimeData.getBoolean('isSwipeTrackingFromScrollEventsEnabled'));
279
280 // Prevent touch events from triggering any sort of native scrolling.
281 document.addEventListener('touchmove', function(e) {
282 e.preventDefault();
283 }, true);
284
285 // Handle events from the card slider.
286 this.pageList.addEventListener('cardSlider:card_changed',
287 this.onCardChanged_.bind(this));
288 this.pageList.addEventListener('cardSlider:card_added',
289 this.onCardAdded_.bind(this));
290 this.pageList.addEventListener('cardSlider:card_removed',
291 this.onCardRemoved_.bind(this));
292
293 $('bottom-panel').addEventListener('webkitTransitionEnd',
294 this.onBottomPanelTransitionEnd_.bind(this));
295
296 // Update apps when online state changes.
297 window.addEventListener('online',
298 this.updateOfflineEnabledApps_.bind(this));
299 window.addEventListener('offline',
300 this.updateOfflineEnabledApps_.bind(this));
301 },
302
303 /**
304 * Starts listening to user input events. The resize and keydown events
305 * must be added only when all NTP have finished loading because they
306 * will act in the current selected page.
307 */
308 onReady: function() {
309 window.addEventListener('resize', this.onWindowResize_.bind(this));
310 document.addEventListener('keydown', this.onDocKeyDown_.bind(this));
311 },
312
313 /**
314 * Appends a tile page.
315 *
316 * @param {TilePage} page The page element.
317 * @param {string} title The title of the tile page.
318 * @param {TilePage=} opt_refNode Optional reference node to insert in front
319 * of.
320 * When opt_refNode is falsey, |page| will just be appended to the end of
321 * the page list.
322 */
323 appendTilePage: function(page, title, opt_refNode) {
324 if (opt_refNode) {
325 var refIndex = this.getTilePageIndex(opt_refNode);
326 this.cardSlider.addCardAtIndex(page, refIndex);
327 } else {
328 this.cardSlider.appendCard(page);
329 }
330
331 // Remember special MostVisitedPage.
332 if (typeof ntp.MostVisitedPage != 'undefined' &&
333 page instanceof ntp.MostVisitedPage) {
334 assert(this.tilePages.length == 1,
335 'MostVisitedPage should be added as first tile page');
336 this.mostVisitedPage = page;
337 }
338
339 if (typeof ntp.AppsPage != 'undefined' &&
340 page instanceof ntp.AppsPage) {
341 this.appsPage = page;
342 }
343
344 if (typeof ntp.RecentlyClosedPage != 'undefined' &&
345 page instanceof ntp.RecentlyClosedPage) {
346 this.recentlyClosedPage = page;
347 }
348
349 // Remember special OtherDevicesPage.
350 if (typeof ntp.OtherDevicesPage != 'undefined' &&
351 page instanceof ntp.OtherDevicesPage) {
352 this.otherDevicesPage = page;
353 }
354
355 // Make a deep copy of the dot template to add a new one.
356 var newDot = new ntp.NavDot(page, title);
357 page.navigationDot = newDot;
358 this.dotList.insertBefore(newDot,
359 opt_refNode ? opt_refNode.navigationDot : null);
360 // Set a tab index on the first dot.
361 if (this.dotList.dots.length == 1)
362 newDot.tabIndex = 3;
363 },
364
365 /**
366 * Called by chrome when an app has changed positions.
367 * @param {Object} data The data for the app. This contains page and
368 * position indices.
369 */
370 appMoved: function(data) {
371 assert(loadTimeData.getBoolean('showApps'));
372
373 var app = $(data.id);
374 assert(app, 'trying to move an app that doesn\'t exist');
375 app.remove(false);
376
377 this.appsPage.insertApp(data, false);
378 },
379
380 /**
381 * Called by chrome when an existing app has been disabled or
382 * removed/uninstalled from chrome.
383 * @param {Object} data A data structure full of relevant information for
384 * the app.
385 * @param {boolean} isUninstall True if the app is being uninstalled;
386 * false if the app is being disabled.
387 * @param {boolean} fromPage True if the removal was from the current page.
388 */
389 appRemoved: function(data, isUninstall, fromPage) {
390 assert(loadTimeData.getBoolean('showApps'));
391
392 var app = $(data.id);
393 assert(app, 'trying to remove an app that doesn\'t exist');
394
395 if (!isUninstall)
396 app.replaceAppData(data);
397 else
398 app.remove(!!fromPage);
399 },
400
401 /**
402 * @return {boolean} If the page is still starting up.
403 * @private
404 */
405 isStartingUp_: function() {
406 return document.documentElement.classList.contains('starting-up');
407 },
408
409 /**
410 * Tracks whether apps have been loaded at least once.
411 * @type {boolean}
412 * @private
413 */
414 appsLoaded_: false,
415
416 /**
417 * Callback invoked by chrome with the apps available.
418 *
419 * Note that calls to this function can occur at any time, not just in
420 * response to a getApps request. For example, when a user
421 * installs/uninstalls an app on another synchronized devices.
422 * @param {Object} data An object with all the data on available
423 * applications.
424 */
425 getAppsCallback: function(data) {
426 assert(loadTimeData.getBoolean('showApps'));
427
428 var startTime = Date.now();
429
430 // Get the array of apps and add any special synthesized entries.
431 var apps = data.apps;
432
433 // Sort alphabetically.
434 apps.sort(function(a, b) {
435 return a.title.toLocaleLowerCase() > b.title.toLocaleLowerCase() ? 1 :
436 a.title.toLocaleLowerCase() < b.title.toLocaleLowerCase() ? -1 : 0;
437 });
438
439 // An app to animate (in case it was just installed).
440 var highlightApp;
441
442 if (this.appsPage) {
443 this.appsPage.removeAllTiles();
444 } else {
445 var page = new ntp.AppsPage();
446 page.setDataList(apps);
447 this.appendTilePage(page, loadTimeData.getString('appDefaultPageName'));
448 }
449
450 for (var i = 0; i < apps.length; i++) {
451 var app = apps[i];
452 if (app.id == this.highlightAppId)
453 highlightApp = app;
454 else
455 this.appsPage.insertApp(app, false);
456 }
457
458 if (highlightApp)
459 this.appAdded(highlightApp, true);
460
461 logEvent('apps.layout: ' + (Date.now() - startTime));
462
463 // Tell the slider about the pages and mark the current page.
464 this.updateSliderCards();
465
466 if (!this.appsLoaded_) {
467 this.appsLoaded_ = true;
468 cr.dispatchSimpleEvent(document, 'sectionready', true, true);
469 }
470 },
471
472 /**
473 * Called by chrome when a new app has been added to chrome or has been
474 * enabled if previously disabled.
475 * @param {Object} data A data structure full of relevant information for
476 * the app.
477 * @param {boolean=} opt_highlight Whether the app about to be added should
478 * be highlighted.
479 */
480 appAdded: function(data, opt_highlight) {
481 assert(loadTimeData.getBoolean('showApps'));
482
483 if (data.id == this.highlightAppId) {
484 opt_highlight = true;
485 this.highlightAppId = null;
486 }
487
488 if (!this.appsLoaded_)
489 opt_highlight = false;
490
491 var app = $(data.id);
492 if (app) {
493 app.replaceAppData(data);
494 } else if (opt_highlight) {
495 this.appsPage.insertAndHighlightApp(data);
496 this.setShownPage_(loadTimeData.getInteger('apps_page_id'),
497 data.page_index);
498 } else {
499 this.appsPage.insertApp(data, false);
500 }
501 },
502
503 /**
504 * Callback invoked by chrome whenever an app preference changes.
505 * @param {Object} data An object with all the data on available
506 * applications.
507 */
508 appsPrefChangedCallback: function(data) {
509 assert(loadTimeData.getBoolean('showApps'));
510
511 for (var i = 0; i < data.apps.length; ++i) {
512 var element = $(data.apps[i].id);
513 if (element)
514 element.data = data.apps[i];
515 }
516 },
517
518 /**
519 * Invoked whenever the pages in page-list have changed so that the
520 * CardSlider knows about the new elements.
521 */
522 updateSliderCards: function() {
523 var pageNo = Math.max(0, Math.min(this.cardSlider.currentCard,
524 this.tilePages.length - 1));
525 this.cardSlider.setCards(Array.prototype.slice.call(this.tilePages),
526 pageNo);
527 switch (this.shownPage) {
528 case loadTimeData.getInteger('apps_page_id'):
529 this.cardSlider.selectCardByValue(this.appsPage);
530 break;
531 case loadTimeData.getInteger('most_visited_page_id'):
532 if (this.mostVisitedPage)
533 this.cardSlider.selectCardByValue(this.mostVisitedPage);
534 break;
535 }
536 },
537
538 /**
539 * Handler for cardSlider:card_changed events from this.cardSlider.
540 * @param {Event} e The cardSlider:card_changed event.
541 * @private
542 */
543 onCardChanged_: function(e) {
544 var page = e.cardSlider.currentCardValue;
545
546 // Don't change shownPage until startup is done (and page changes actually
547 // reflect user actions).
548 if (!this.isStartingUp_()) {
549 if (page.classList.contains('apps-page')) {
550 this.setShownPage_(loadTimeData.getInteger('apps_page_id'), 0);
551 } else if (page.classList.contains('most-visited-page')) {
552 this.setShownPage_(
553 loadTimeData.getInteger('most_visited_page_id'), 0);
554 } else if (page.classList.contains('recently-closed-page')) {
555 this.setShownPage_(
556 loadTimeData.getInteger('recently_closed_page_id'), 0);
557 } else if (page.classList.contains('other-devices-page')) {
558 this.setShownPage_(
559 loadTimeData.getInteger('other_devices_page_id'), 0);
560 } else {
561 console.error('unknown page selected');
562 }
563 }
564
565 // Update the active dot
566 var curDot = this.dotList.getElementsByClassName('selected')[0];
567 if (curDot)
568 curDot.classList.remove('selected');
569 page.navigationDot.classList.add('selected');
570 },
571
572 /**
573 * Saves/updates the newly selected page to open when first loading the NTP.
574 * @type {number} shownPage The new shown page type.
575 * @type {number} shownPageIndex The new shown page index.
576 * @private
577 */
578 setShownPage_: function(shownPage, shownPageIndex) {
579 assert(shownPageIndex >= 0);
580 this.shownPage = shownPage;
581 this.shownPageIndex = shownPageIndex;
582 chrome.send('pageSelected', [this.shownPage, this.shownPageIndex]);
583 },
584
585 /**
586 * Listen for card additions to update the current card accordingly.
587 * @param {Event} e A card removed or added event.
588 */
589 onCardAdded_: function(e) {
590 var page = e.addedCard;
591 // When the second arg passed to insertBefore is falsey, it acts just like
592 // appendChild.
593 this.pageList.insertBefore(page, this.tilePages[e.addedIndex]);
594 this.layout(false, page);
595 this.onCardAddedOrRemoved_();
596 },
597
598 /**
599 * Listen for card removals to update the current card accordingly.
600 * @param {Event} e A card removed or added event.
601 */
602 onCardRemoved_: function(e) {
603 e.removedCard.remove();
604 this.onCardAddedOrRemoved_();
605 },
606
607 /**
608 * Called when a card is removed or added.
609 * @private
610 */
611 onCardAddedOrRemoved_: function() {
612 if (this.isStartingUp_())
613 return;
614
615 // Without repositioning there were issues - http://crbug.com/133457.
616 this.cardSlider.repositionFrame();
617 },
618
619 /**
620 * Window resize handler.
621 * @private
622 */
623 onWindowResize_: function(e) {
624 this.cardSlider.resize(this.sliderFrame.offsetWidth);
625 this.layout(true);
626 },
627
628 /**
629 * Handler for key events on the page. Ctrl-Arrow will switch the visible
630 * page.
631 * @param {Event} e The KeyboardEvent.
632 * @private
633 */
634 onDocKeyDown_: function(e) {
635 if (!e.ctrlKey || e.altKey || e.metaKey || e.shiftKey)
636 return;
637
638 var direction = 0;
639 if (e.keyIdentifier == 'Left')
640 direction = -1;
641 else if (e.keyIdentifier == 'Right')
642 direction = 1;
643 else
644 return;
645
646 var cardIndex =
647 (this.cardSlider.currentCard + direction +
648 this.cardSlider.cardCount) % this.cardSlider.cardCount;
649 this.cardSlider.selectCard(cardIndex, true);
650
651 e.stopPropagation();
652 },
653
654 /**
655 * Listener for offline status change events. Updates apps that are
656 * not offline-enabled to be grayscale if the browser is offline.
657 * @private
658 */
659 updateOfflineEnabledApps_: function() {
660 var apps = document.querySelectorAll('.app');
661 for (var i = 0; i < apps.length; ++i) {
662 if (apps[i].data.enabled && !apps[i].data.offline_enabled) {
663 apps[i].setIcon();
664 apps[i].loadIcon();
665 }
666 }
667 },
668
669 /**
670 * Returns the index of a given tile page.
671 * @param {TilePage} page The TilePage we wish to find.
672 * @return {number} The index of |page| or -1 if it is not in the
673 * collection.
674 */
675 getTilePageIndex: function(page) {
676 return Array.prototype.indexOf.call(this.tilePages, page);
677 },
678
679 /**
680 * Removes a page and navigation dot (if the navdot exists).
681 * @param {TilePage} page The page to be removed.
682 */
683 removeTilePageAndDot_: function(page) {
684 if (page.navigationDot)
685 page.navigationDot.remove();
686 this.cardSlider.removeCard(page);
687 },
688
689 /**
690 * The width of the Bottom Panel's content.
691 * @type {number}
692 */
693 contentWidth_: 0,
694
695 /**
696 * Calculates the layout of the NTP's Bottom Panel. This method will resize
697 * and position all container elements in the Bottom Panel. At the end of
698 * the layout process it will dispatch the layout method to the current
699 * selected TilePage. Alternatively, you can pass a specific TilePage in
700 * the |opt_page| parameter, which is useful for initializing the layout
701 * of a recently created TilePage.
702 *
703 * The |NewTabView.layout| deals with the global layout state while the
704 * |TilePage.layout| deals with the per-page layout state. A general rule
705 * would be: if you need to resize any element which is outside the
706 * card-slider-frame, it should be handled here in NewTabView. Otherwise,
707 * it should be handled in TilePage.
708 *
709 * @param {boolean=} opt_animate Whether the layout should be animated.
710 * @param {ntp.TilePage=} opt_page Alternative TilePage to calculate layout.
711 */
712 layout: function(opt_animate, opt_page) {
713 opt_animate = typeof opt_animate == 'undefined' ? false : opt_animate;
714
715 var viewHeight = cr.doc.documentElement.clientHeight;
716 var isBottomPanelVisible = viewHeight >= HEIGHT_FOR_BOTTOM_PANEL;
717 // Toggles the visibility of the Bottom Panel when there is (or there
718 // is not) space to show the entire panel.
719 this.showBottomPanel_(isBottomPanelVisible);
720
721 // The layout calculation can be skipped if Bottom Panel is not visible.
722 if (!isBottomPanelVisible && !opt_page)
723 return;
724
725 // Calculates the width of the Bottom Panel's Content.
726 var width = this.calculateContentWidth_();
727 if (width != this.contentWidth_) {
728 this.contentWidth_ = width;
729 $('bottom-panel-footer').style.width = width + 'px';
730 }
731
732 // Finally, dispatch the layout method to the current page.
733 var currentPage = opt_page || this.cardSlider.currentCardValue;
734
735 var contentHeight = TILE_ROW_HEIGHT;
736 if (!opt_page && currentPage.config.scrollable) {
737 contentHeight = viewHeight - bottomPanelOffsetTop -
738 headerHeight - $('bottom-panel-footer').offsetHeight;
739 contentHeight = Math.max(TILE_ROW_HEIGHT, contentHeight);
740 }
741 this.contentHeight_ = contentHeight;
742
743 $('card-slider-frame').style.height = contentHeight + 'px';
744
745 currentPage.layout(opt_animate);
746 },
747
748 /**
749 * @return {number} The height of the Bottom Panel's content.
750 */
751 get contentHeight() {
752 return this.contentHeight_;
753 },
754
755 /**
756 * @return {number} The width of the Bottom Panel's content.
757 */
758 get contentWidth() {
759 return this.contentWidth_;
760 },
761
762 /**
763 * @return {number} The width of the Bottom Panel's content.
764 * @private
765 */
766 calculateContentWidth_: function() {
767 var windowWidth = cr.doc.documentElement.clientWidth;
768 var margin = 2 * BOTTOM_PANEL_HORIZONTAL_MARGIN;
769
770 var width;
771 if (windowWidth >= MAX_BOTTOM_PANEL_WIDTH) {
772 width = MAX_BOTTOM_PANEL_WIDTH - margin;
773 } else if (windowWidth >= NORMAL_BOTTOM_PANEL_WIDTH) {
774 width = windowWidth - margin;
775 } else if (windowWidth >= MIN_BOTTOM_PANEL_WIDTH) {
776 // Interpolation between the previous and next states.
777 var minMargin = MIN_BOTTOM_PANEL_WIDTH - MIN_BOTTOM_PANEL_CONTENT_WIDTH;
778 var factor = (windowWidth - MIN_BOTTOM_PANEL_WIDTH) /
779 (NORMAL_BOTTOM_PANEL_WIDTH - MIN_BOTTOM_PANEL_WIDTH);
780 var interpolatedMargin = minMargin + factor * (margin - minMargin);
781 width = windowWidth - interpolatedMargin;
782 } else {
783 width = MIN_BOTTOM_PANEL_CONTENT_WIDTH;
784 }
785
786 return width;
787 },
788
789 /**
790 * Animates the display of the Bottom Panel.
791 * @param {boolean} show Whether or not to show the Bottom Panel.
792 */
793 showBottomPanel_: function(show) {
794 var bottomPanel = $('bottom-panel');
795
796 if (show) {
797 bottomPanel.hidden = false;
798 // Forces the reflow.
799 bottomPanel.offsetHeight;
800 }
801
802 bottomPanel.classList.toggle('hide-bottom-panel', !show);
803 },
804
805 /**
806 * Handles the end of the bottom panel transition.
807 * @param {Event} e The bottom panel webkitTransitionEnd event.
808 * @private
809 */
810 onBottomPanelTransitionEnd_: function(e) {
811 var bottomPanel = $('bottom-panel');
812 if (e.target == bottomPanel && e.propertyName == 'opacity' &&
813 bottomPanel.classList.contains('hide-bottom-panel')) {
814 bottomPanel.hidden = true;
815 }
816 },
817 };
818
819 /**
820 * Invoked at startup once the DOM is available to initialize the app.
821 */
822 function onLoad() {
823
824 if (!loadTimeData.getBoolean('showApps'))
825 cr.dispatchSimpleEvent(document, 'sectionready', true, true);
826
827 // Load the current theme colors.
828 themeChanged();
829
830 newTabView = new NewTabView();
831
832 bottomPanelOffsetTop = $('bottom-panel').offsetTop;
833 headerHeight = $('bottom-panel-header').offsetHeight;
834
835 notificationContainer = getRequiredElement('notification-container');
836
837 var mostVisited = new ntp.MostVisitedPage();
838 newTabView.appendTilePage(mostVisited,
839 loadTimeData.getString('mostvisited'));
840 chrome.send('getMostVisited');
841
842 if (loadTimeData.valueExists('bubblePromoText')) {
843 promoBubble = new cr.ui.Bubble;
844 promoBubble.anchorNode = getRequiredElement('promo-bubble-anchor');
845 promoBubble.arrowLocation = cr.ui.ArrowLocation.BOTTOM_START;
846 promoBubble.bubbleAlignment = cr.ui.BubbleAlignment.ENTIRELY_VISIBLE;
847 promoBubble.deactivateToDismissDelay = 2000;
848 promoBubble.content = parseHtmlSubset(
849 loadTimeData.getString('bubblePromoText'), ['BR']);
850
851 var bubbleLink = promoBubble.querySelector('a');
852 if (bubbleLink) {
853 bubbleLink.addEventListener('click', function(e) {
854 chrome.send('bubblePromoLinkClicked');
855 });
856 }
857
858 promoBubble.handleCloseEvent = function() {
859 promoBubble.hide();
860 chrome.send('bubblePromoClosed');
861 };
862 promoBubble.show();
863 chrome.send('bubblePromoViewed');
864 }
865
866 doWhenAllSectionsReady(function() {
867 // Tell the slider about the pages.
868 newTabView.updateSliderCards();
869 newTabView.onReady();
870
871 // Restore the visibility only after calling updateSliderCards to avoid
872 // flickering, otherwise for a small fraction of a second the Page List is
873 // partially rendered.
874 $('bottom-panel').style.visibility = 'visible';
875
876 if (loadTimeData.valueExists('notificationPromoText')) {
877 var promoText = loadTimeData.getString('notificationPromoText');
878 var tags = ['IMG'];
879 var attrs = {
880 src: function(node, value) {
881 return node.tagName == 'IMG' &&
882 /^data\:image\/(?:png|gif|jpe?g)/.test(value);
883 },
884 };
885
886 var promo = parseHtmlSubset(promoText, tags, attrs);
887 var promoLink = promo.querySelector('a');
888 if (promoLink) {
889 promoLink.addEventListener('click', function(e) {
890 chrome.send('notificationPromoLinkClicked');
891 });
892 }
893
894 showNotification(promo, [], function() {
895 chrome.send('notificationPromoClosed');
896 }, 60000);
897 chrome.send('notificationPromoViewed');
898 }
899
900 cr.dispatchSimpleEvent(document, 'ntpLoaded', true, true);
901 document.documentElement.classList.remove('starting-up');
902
903 startTime = Date.now();
904 });
905 }
906
907 /*
908 * The number of sections to wait on.
909 * @type {number}
910 */
911 var sectionsToWaitFor = 2;
912
913 /**
914 * Queued callbacks which lie in wait for all sections to be ready.
915 * @type {!Array}
916 */
917 var readyCallbacks = [];
918
919 /**
920 * Fired as each section of pages becomes ready.
921 * @param {Event} e Each page's synthetic DOM event.
922 */
923 document.addEventListener('sectionready', function(e) {
924 if (--sectionsToWaitFor <= 0) {
925 while (readyCallbacks.length) {
926 readyCallbacks.shift()();
927 }
928 }
929 });
930
931 /**
932 * This is used to simulate a fire-once event (i.e. $(document).ready() in
933 * jQuery or Y.on('domready') in YUI. If all sections are ready, the callback
934 * is fired right away. If all pages are not ready yet, the function is queued
935 * for later execution.
936 * @param {function} callback The work to be done when ready.
937 */
938 function doWhenAllSectionsReady(callback) {
939 assert(typeof callback == 'function');
940 if (sectionsToWaitFor > 0)
941 readyCallbacks.push(callback);
942 else
943 window.setTimeout(callback, 0); // Do soon after, but asynchronously.
944 }
945
946 function themeChanged(opt_hasAttribution) {
947 $('themecss').href = 'chrome://theme/css/new_tab_theme.css?' + Date.now();
948
949 if (typeof opt_hasAttribution != 'undefined') {
950 document.documentElement.setAttribute('hasattribution',
951 opt_hasAttribution);
952 }
953
954 updateAttribution();
955 }
956
957 function setBookmarkBarAttached(attached) {
958 document.documentElement.setAttribute('bookmarkbarattached', attached);
959 }
960
961 /**
962 * Attributes the attribution image at the bottom left.
963 */
964 function updateAttribution() {
965 var attribution = $('attribution');
966 if (document.documentElement.getAttribute('hasattribution') == 'true') {
967 $('attribution-img').src =
968 'chrome://theme/IDR_THEME_NTP_ATTRIBUTION?' + Date.now();
969 attribution.hidden = false;
970 } else {
971 attribution.hidden = true;
972 }
973 }
974
975 /**
976 * Timeout ID.
977 * @type {number}
978 */
979 var notificationTimeout = 0;
980
981 /**
982 * Shows the notification bubble.
983 * @param {string|Node} message The notification message or node to use as
984 * message.
985 * @param {Array.<{text: string, action: function()}>} links An array of
986 * records describing the links in the notification. Each record should
987 * have a 'text' attribute (the display string) and an 'action' attribute
988 * (a function to run when the link is activated).
989 * @param {Function=} opt_closeHandler The callback invoked if the user
990 * manually dismisses the notification.
991 */
992 function showNotification(message, links, opt_closeHandler, opt_timeout) {
993 window.clearTimeout(notificationTimeout);
994
995 var span = document.querySelector('#notification > span');
996 if (typeof message == 'string') {
997 span.textContent = message;
998 } else {
999 span.textContent = ''; // Remove all children.
1000 span.appendChild(message);
1001 }
1002
1003 var linksBin = $('notificationLinks');
1004 linksBin.textContent = '';
1005 for (var i = 0; i < links.length; i++) {
1006 var link = linksBin.ownerDocument.createElement('div');
1007 link.textContent = links[i].text;
1008 link.action = links[i].action;
1009 link.onclick = function() {
1010 this.action();
1011 hideNotification();
1012 };
1013 link.setAttribute('role', 'button');
1014 link.setAttribute('tabindex', 0);
1015 link.className = 'link-button';
1016 linksBin.appendChild(link);
1017 }
1018
1019 function closeFunc(e) {
1020 if (opt_closeHandler)
1021 opt_closeHandler();
1022 hideNotification();
1023 }
1024
1025 document.querySelector('#notification button').onclick = closeFunc;
1026 document.addEventListener('dragstart', closeFunc);
1027
1028 notificationContainer.hidden = false;
1029
1030 var timeout = opt_timeout || 10000;
1031 notificationTimeout = window.setTimeout(hideNotification, timeout);
1032
1033 layout();
1034 }
1035
1036 /**
1037 * Hide the notification bubble.
1038 */
1039 function hideNotification() {
1040 notificationContainer.hidden = true;
1041
1042 layout();
1043 }
1044
1045 function setMostVisitedPages(dataList, hasBlacklistedUrls) {
1046 var page = newTabView.mostVisitedPage;
1047 var state = page.getTileRepositioningState();
1048 if (state) {
1049 if (state.isRemoving)
1050 page.animateTileRemoval(state.index, dataList);
1051 else
1052 page.animateTileRestoration(state.index, dataList);
1053
1054 page.resetTileRepositioningState();
1055 } else {
1056 page.setDataList(dataList);
1057 cr.dispatchSimpleEvent(document, 'sectionready', true, true);
1058 }
1059 }
1060
1061 /**
1062 * Set the dominant color for a node. This will be called in response to
1063 * getFaviconDominantColor. The node represented by |id| better have a setter
1064 * for stripeColor.
1065 * @param {string} id The ID of a node.
1066 * @param {string} color The color represented as a CSS string.
1067 */
1068 function setFaviconDominantColor(id, color) {
1069 var node = $(id);
1070 var prop = Object.getOwnPropertyDescriptor(node.__proto__, 'stripeColor');
1071 assert(prop && prop.set, 'Node doesn\'t have a stripeColor setter');
1072 if (node)
1073 node.stripeColor = color;
1074 }
1075
1076 function getThumbnailUrl(url) {
1077 return 'chrome://thumb/' + url;
1078 }
1079
1080 /**
1081 * Increments the parameter used to log the total number of thumbnail hovered
1082 * over.
1083 */
1084 function incrementHoveredThumbnailCount() {
1085 hoveredThumbnailCount++;
1086 }
1087
1088 /**
1089 * Logs the time to click for the specified item and the total number of
1090 * thumbnails hovered over.
1091 * @param {string} item The item to log the time-to-click.
1092 */
1093 function logTimeToClickAndHoverCount(item) {
1094 var timeToClick = Date.now() - startTime;
1095 chrome.send('logTimeToClick',
1096 ['ExtendedNewTabPage.TimeToClick' + item, timeToClick]);
1097 chrome.send('metricsHandler:recordInHistogram',
1098 ['ExtendedNewTabPage.hoveredThumbnailCount',
1099 hoveredThumbnailCount, 40]);
1100 }
1101
1102 /**
1103 * Wrappers to forward the callback to corresponding NewTabView member.
1104 */
1105 function appAdded() {
1106 return newTabView.appAdded.apply(newTabView, arguments);
1107 }
1108
1109 function appMoved() {
1110 return newTabView.appMoved.apply(newTabView, arguments);
1111 }
1112
1113 function appRemoved() {
1114 return newTabView.appRemoved.apply(newTabView, arguments);
1115 }
1116
1117 function appsPrefChangeCallback() {
1118 return newTabView.appsPrefChangedCallback.apply(newTabView, arguments);
1119 }
1120
1121 function getAppsCallback() {
1122 return newTabView.getAppsCallback.apply(newTabView, arguments);
1123 }
1124
1125 function getCardSlider() {
1126 return newTabView.cardSlider;
1127 }
1128
1129 function setAppToBeHighlighted(appId) {
1130 newTabView.highlightAppId = appId;
1131 }
1132
1133 function layout() {
1134 newTabView.layout.apply(newTabView, arguments);
1135 }
1136
1137 function getContentHeight() {
1138 return newTabView.contentHeight;
1139 }
1140
1141 function getContentWidth() {
1142 return newTabView.contentWidth;
1143 }
1144
1145 function noop() {
1146 // Ignore some NTP4 callbacks for backwards compatibility purposes.
1147 }
1148
1149 // Return an object with all the exports
1150 return {
1151 APP_LAUNCH: APP_LAUNCH,
1152 TILE_ROW_HEIGHT: TILE_ROW_HEIGHT,
1153 appAdded: appAdded,
1154 appMoved: appMoved,
1155 appRemoved: appRemoved,
1156 appsPrefChangeCallback: appsPrefChangeCallback,
1157 getAppsCallback: getAppsCallback,
1158 getCardSlider: getCardSlider,
1159 getContentHeight: getContentHeight,
1160 getContentWidth: getContentWidth,
1161 getThumbnailUrl: getThumbnailUrl,
1162 incrementHoveredThumbnailCount: incrementHoveredThumbnailCount,
1163 layout: layout,
1164 logTimeToClickAndHoverCount: logTimeToClickAndHoverCount,
1165 NtpFollowAction: NtpFollowAction,
1166 onLoad: onLoad,
1167 setAppToBeHighlighted: setAppToBeHighlighted,
1168 setBookmarkBarAttached: setBookmarkBarAttached,
1169 setFaviconDominantColor: setFaviconDominantColor,
1170 setForeignSessions: noop,
1171 setMostVisitedPages: setMostVisitedPages,
1172 setRecentlyClosedTabs: noop,
1173 showNotification: showNotification,
1174 themeChanged: themeChanged,
1175 updateLogin: noop,
1176 };
1177 });
1178
1179 document.addEventListener('DOMContentLoaded', ntp.onLoad);
1180
1181 var toCssPx = cr.ui.toCssPx;
OLDNEW
« no previous file with comments | « chrome/browser/resources/ntp_search/new_tab.html ('k') | chrome/browser/resources/ntp_search/new_tab_theme.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698