1 // Copyright 2013 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.
7 * @fileoverview The local InstantExtended NTP.
12 * Controls rendering the new tab page for InstantExtended.
13 * @return {Object} A limited interface for testing the local NTP.
16 <include src
="../../../../ui/webui/resources/js/assert.js">
17 <include src
="window_disposition_util.js">
21 * Enum for classnames.
26 ALTERNATE_LOGO
: 'alternate-logo', // Shows white logo if required by theme
27 BLACKLIST
: 'mv-blacklist', // triggers tile blacklist animation
28 BLACKLIST_BUTTON
: 'mv-x',
29 DELAYED_HIDE_NOTIFICATION
: 'mv-notice-delayed-hide',
30 FAKEBOX_DISABLE
: 'fakebox-disable', // Makes fakebox non-interactive
31 FAKEBOX_FOCUS
: 'fakebox-focused', // Applies focus styles to the fakebox
32 // Applies drag focus style to the fakebox
33 FAKEBOX_DRAG_FOCUS
: 'fakebox-drag-focused',
34 FAVICON
: 'mv-favicon',
35 HIDE_BLACKLIST_BUTTON
: 'mv-x-hide', // hides blacklist button during animation
36 HIDE_FAKEBOX_AND_LOGO
: 'hide-fakebox-logo',
37 HIDE_NOTIFICATION
: 'mv-notice-hide',
38 // Vertically centers the most visited section for a non-Google provided page.
39 NON_GOOGLE_PAGE
: 'non-google-page',
40 PAGE
: 'mv-page', // page tiles
41 PAGE_READY
: 'mv-page-ready', // page tile when ready
42 ROW
: 'mv-row', // tile row
43 RTL
: 'rtl', // Right-to-left language text.
44 THUMBNAIL
: 'mv-thumb',
45 THUMBNAIL_MASK
: 'mv-mask',
52 * Enum for HTML element ids.
57 ATTRIBUTION
: 'attribution',
58 ATTRIBUTION_TEXT
: 'attribution-text',
59 CUSTOM_THEME_STYLE
: 'ct-style',
61 FAKEBOX_INPUT
: 'fakebox-input',
63 NOTIFICATION
: 'mv-notice',
64 NOTIFICATION_CLOSE_BUTTON
: 'mv-notice-x',
65 NOTIFICATION_MESSAGE
: 'mv-msg',
66 NTP_CONTENTS
: 'ntp-contents',
67 RESTORE_ALL_LINK
: 'mv-restore',
85 * Enum for the state of the NTP when it is disposed.
89 var NTP_DISPOSE_STATE
= {
90 NONE
: 0, // Preserve the NTP appearance and functionality
92 HIDE_FAKEBOX_AND_LOGO
: 2
97 * The JavaScript button event value for a middle click.
101 var MIDDLE_MOUSE_BUTTON
= 1;
105 * The container for the tile elements.
112 * The notification displayed when a page is blacklisted.
119 * The container for the theme attribution.
126 * The "fakebox" - an input field that looks like a regular searchbox. When it
127 * is focused, any text the user types goes directly into the omnibox.
134 * The container for NTP elements.
141 * The array of rendered tiles, ordered by appearance.
142 * @type {!Array.<Tile>}
148 * The last blacklisted tile if any, which by definition should not be filler.
151 var lastBlacklistedTile
= null;
155 * True if a page has been blacklisted and we're waiting on the
156 * onmostvisitedchange callback. See onMostVisitedChange() for how this
160 var isBlacklisting
= false;
164 * Current number of tiles columns shown based on the window width, including
165 * those that just contain filler.
168 var numColumnsShown
= 0;
172 * True if the user initiated the current most visited change and false
176 var userInitiatedMostVisitedChange
= false;
180 * The browser embeddedSearch.newTabPage object.
187 * The browser embeddedSearch.searchBox object.
190 var searchboxApiHandle
;
194 * The state of the NTP when a query is entered into the Omnibox.
195 * @type {NTP_DISPOSE_STATE}
197 var omniboxInputBehavior
= NTP_DISPOSE_STATE
.NONE
;
201 * The state of the NTP when a query is entered into the Fakebox.
202 * @type {NTP_DISPOSE_STATE}
204 var fakeboxInputBehavior
= NTP_DISPOSE_STATE
.HIDE_FAKEBOX_AND_LOGO
;
208 * Total tile width. Should be equal to mv-tile's width + 2 * border-width.
212 var TILE_WIDTH
= 140;
216 * Margin between tiles. Should be equal to mv-tile's -webkit-margin-start.
220 var TILE_MARGIN_START
= 20;
223 /** @type {number} @const */
224 var MAX_NUM_TILES_TO_SHOW
= 8;
227 /** @type {number} @const */
228 var MIN_NUM_COLUMNS
= 2;
231 /** @type {number} @const */
232 var MAX_NUM_COLUMNS
= 4;
235 /** @type {number} @const */
240 * Minimum total padding to give to the left and right of the most visited
241 * section. Used to determine how many tiles to show.
245 var MIN_TOTAL_HORIZONTAL_PADDING
= 200;
249 * The filename for a most visited iframe src which shows a page title.
253 var MOST_VISITED_TITLE_IFRAME
= 'title.html';
257 * The filename for a most visited iframe src which shows a thumbnail image.
261 var MOST_VISITED_THUMBNAIL_IFRAME
= 'thumbnail.html';
265 * The hex color for most visited tile elements.
269 var MOST_VISITED_COLOR
= '777777';
273 * The font family for most visited tile elements.
277 var MOST_VISITED_FONT_FAMILY
= 'arial, sans-serif';
281 * The font size for most visited tile elements.
285 var MOST_VISITED_FONT_SIZE
= 11;
289 * Hide most visited tiles for at most this many milliseconds while painting.
293 var MOST_VISITED_PAINT_TIMEOUT_MSEC
= 500;
297 * A Tile is either a rendering of a Most Visited page or "filler" used to
298 * pad out the section when not enough pages exist.
300 * @param {Element} elem The element for rendering the tile.
301 * @param {number=} opt_rid The RID for the corresponding Most Visited page.
302 * Should only be left unspecified when creating a filler tile.
305 function Tile(elem
, opt_rid
) {
306 /** @type {Element} */
309 /** @type {number|undefined} */
315 * Updates the NTP based on the current theme.
318 function onThemeChange() {
319 var info
= ntpApiHandle
.themeBackgroundInfo
;
323 var background
= [convertToRGBAColor(info
.backgroundColorRgba
),
326 info
.imageHorizontalAlignment
,
327 info
.imageVerticalAlignment
].join(' ').trim();
328 document
.body
.style
.background
= background
;
329 document
.body
.classList
.toggle(CLASSES
.ALTERNATE_LOGO
, info
.alternateLogo
);
330 updateThemeAttribution(info
.attributionUrl
);
331 setCustomThemeStyle(info
);
337 * Updates the NTP style according to theme.
338 * @param {Object=} opt_themeInfo The information about the theme. If it is
339 * omitted the style will be reverted to the default.
342 function setCustomThemeStyle(opt_themeInfo
) {
343 var customStyleElement
= $(IDS
.CUSTOM_THEME_STYLE
);
344 var head
= document
.head
;
346 if (opt_themeInfo
&& !opt_themeInfo
.usingDefaultTheme
) {
349 ' color: ' + convertToRGBAColor(opt_themeInfo
.textColorLightRgba
) + ';' +
352 ' color: ' + convertToRGBAColor(opt_themeInfo
.textColorRgba
) + ';' +
354 '#mv-notice-links span {' +
355 ' color: ' + convertToRGBAColor(opt_themeInfo
.textColorLightRgba
) + ';' +
358 ' -webkit-filter: drop-shadow(0 0 0 ' +
359 convertToRGBAColor(opt_themeInfo
.textColorRgba
) + ');' +
362 ' border: 1px solid ' +
363 convertToRGBAColor(opt_themeInfo
.sectionBorderColorRgba
) + ';' +
365 '.mv-page-ready:hover, .mv-page-ready:focus {' +
367 convertToRGBAColor(opt_themeInfo
.headerColorRgba
) + ';' +
370 if (customStyleElement
) {
371 customStyleElement
.textContent
= themeStyle
;
373 customStyleElement
= document
.createElement('style');
374 customStyleElement
.type
= 'text/css';
375 customStyleElement
.id
= IDS
.CUSTOM_THEME_STYLE
;
376 customStyleElement
.textContent
= themeStyle
;
377 head
.appendChild(customStyleElement
);
380 } else if (customStyleElement
) {
381 head
.removeChild(customStyleElement
);
387 * Renders the attribution if the URL is present, otherwise hides it.
388 * @param {string} url The URL of the attribution image, if any.
391 function updateThemeAttribution(url
) {
393 setAttributionVisibility_(false);
397 var attributionImage
= attribution
.querySelector('img');
398 if (!attributionImage
) {
399 attributionImage
= new Image();
400 attribution
.appendChild(attributionImage
);
402 attributionImage
.style
.content
= url
;
403 setAttributionVisibility_(true);
408 * Sets the visibility of the theme attribution.
409 * @param {boolean} show True to show the attribution.
412 function setAttributionVisibility_(show
) {
414 attribution
.style
.display
= show
? '' : 'none';
420 * Converts an Array of color components into RGBA format "rgba(R,G,B,A)".
421 * @param {Array.<number>} color Array of rgba color components.
422 * @return {string} CSS color in RGBA format.
425 function convertToRGBAColor(color
) {
426 return 'rgba(' + color
[0] + ',' + color
[1] + ',' + color
[2] + ',' +
427 color
[3] / 255 + ')';
432 * Handles a new set of Most Visited page data.
434 function onMostVisitedChange() {
435 var pages
= ntpApiHandle
.mostVisited
;
437 if (isBlacklisting
) {
438 // Trigger the blacklist animation and re-render the tiles when it
440 var lastBlacklistedTileElement
= lastBlacklistedTile
.elem
;
441 lastBlacklistedTileElement
.addEventListener(
442 'webkitTransitionEnd', blacklistAnimationDone
);
443 lastBlacklistedTileElement
.classList
.add(CLASSES
.BLACKLIST
);
446 // Otherwise render the tiles using the new data without animation.
448 for (var i
= 0; i
< MAX_NUM_TILES_TO_SHOW
; ++i
) {
449 tiles
.push(createTile(pages
[i
], i
));
451 if (!userInitiatedMostVisitedChange
) {
452 tilesContainer
.hidden
= true;
453 window
.setTimeout(function() {
454 if (tilesContainer
) {
455 tilesContainer
.hidden
= false;
457 }, MOST_VISITED_PAINT_TIMEOUT_MSEC
);
465 * Renders the current set of tiles.
467 function renderTiles() {
468 var rows
= tilesContainer
.children
;
469 for (var i
= 0; i
< rows
.length
; ++i
) {
470 removeChildren(rows
[i
]);
473 for (var i
= 0, length
= tiles
.length
;
474 i
< Math
.min(length
, numColumnsShown
* NUM_ROWS
); ++i
) {
475 rows
[Math
.floor(i
/ numColumnsShown
)].appendChild(tiles
[i
].elem
);
481 * Shows most visited tiles if all child iframes are loaded, and hides them
484 function updateMostVisitedVisibility() {
485 var iframes
= tilesContainer
.querySelectorAll('iframe');
487 for (var i
= 0, numIframes
= iframes
.length
; i
< numIframes
; i
++) {
488 if (iframes
[i
].hidden
) {
494 tilesContainer
.hidden
= false;
495 userInitiatedMostVisitedChange
= false;
501 * Builds a URL to display a most visited tile component in an iframe.
502 * @param {string} filename The desired most visited component filename.
503 * @param {number} rid The restricted ID.
504 * @param {string} color The text color for text in the iframe.
505 * @param {string} fontFamily The font family for text in the iframe.
506 * @param {number} fontSize The font size for text in the iframe.
507 * @param {number} position The position of the iframe in the UI.
508 * @return {string} An URL to display the most visited component in an iframe.
510 function getMostVisitedIframeUrl(filename
, rid
, color
, fontFamily
, fontSize
,
512 return 'chrome-search://most-visited/' + encodeURIComponent(filename
) + '?' +
513 ['rid=' + encodeURIComponent(rid
),
514 'c=' + encodeURIComponent(color
),
515 'f=' + encodeURIComponent(fontFamily
),
516 'fs=' + encodeURIComponent(fontSize
),
517 'pos=' + encodeURIComponent(position
)].join('&');
522 * Creates a Tile with the specified page data. If no data is provided, a
523 * filler Tile is created.
524 * @param {Object} page The page data.
525 * @param {number} position The position of the tile.
526 * @return {Tile} The new Tile.
528 function createTile(page
, position
) {
529 var tileElement
= document
.createElement('div');
530 tileElement
.classList
.add(CLASSES
.TILE
);
534 tileElement
.classList
.add(CLASSES
.PAGE
);
536 var navigateFunction = function(e
) {
538 ntpApiHandle
.navigateContentWindow(rid
, getDispositionFromEvent(e
));
541 // The click handler for navigating to the page identified by the RID.
542 tileElement
.addEventListener('click', navigateFunction
);
544 // Make thumbnails tab-accessible.
545 tileElement
.setAttribute('tabindex', '1');
546 registerKeyHandler(tileElement
, KEYCODE
.ENTER
, navigateFunction
);
548 // The iframe which renders the page title.
549 var titleElement
= document
.createElement('iframe');
550 titleElement
.tabIndex
= '-1';
552 // Why iframes have IDs:
554 // On navigating back to the NTP we see several onmostvisitedchange() events
555 // in series with incrementing RIDs. After the first event, a set of iframes
556 // begins loading RIDs n, n+1, ..., n+k-1; after the second event, these get
557 // destroyed and a new set begins loading RIDs n+k, n+k+1, ..., n+2k-1.
558 // Now due to crbug.com/68841, Chrome incorrectly loads the content for the
559 // first set of iframes into the most recent set of iframes.
561 // Giving iframes distinct ids seems to cause some invalidation and prevent
562 // associating the incorrect data.
564 // TODO(jered): Find and fix the root (probably Blink) bug.
566 titleElement
.src
= getMostVisitedIframeUrl(
567 MOST_VISITED_TITLE_IFRAME
, rid
, MOST_VISITED_COLOR
,
568 MOST_VISITED_FONT_FAMILY
, MOST_VISITED_FONT_SIZE
, position
);
570 // Keep this id here. See comment above.
571 titleElement
.id
= 'title-' + rid
;
572 titleElement
.hidden
= true;
573 titleElement
.onload = function() {
574 titleElement
.hidden
= false;
575 updateMostVisitedVisibility();
577 titleElement
.className
= CLASSES
.TITLE
;
578 tileElement
.appendChild(titleElement
);
580 // The iframe which renders either a thumbnail or domain element.
581 var thumbnailElement
= document
.createElement('iframe');
582 thumbnailElement
.tabIndex
= '-1';
583 thumbnailElement
.src
= getMostVisitedIframeUrl(
584 MOST_VISITED_THUMBNAIL_IFRAME
, rid
, MOST_VISITED_COLOR
,
585 MOST_VISITED_FONT_FAMILY
, MOST_VISITED_FONT_SIZE
, position
);
587 // Keep this id here. See comment above.
588 thumbnailElement
.id
= 'thumb-' + rid
;
589 thumbnailElement
.hidden
= true;
590 thumbnailElement
.onload = function() {
591 thumbnailElement
.hidden
= false;
592 tileElement
.classList
.add(CLASSES
.PAGE_READY
);
593 updateMostVisitedVisibility();
595 thumbnailElement
.className
= CLASSES
.THUMBNAIL
;
596 tileElement
.appendChild(thumbnailElement
);
598 // A mask to darken the thumbnail on focus.
599 var maskElement
= createAndAppendElement(
600 tileElement
, 'div', CLASSES
.THUMBNAIL_MASK
);
602 // The button used to blacklist this page.
603 var blacklistButton
= createAndAppendElement(
604 tileElement
, 'div', CLASSES
.BLACKLIST_BUTTON
);
605 var blacklistFunction
= generateBlacklistFunction(rid
);
606 blacklistButton
.addEventListener('click', blacklistFunction
);
607 blacklistButton
.title
= configData
.translatedStrings
.removeThumbnailTooltip
;
609 // When a tile is focused, have delete also blacklist the page.
610 registerKeyHandler(tileElement
, KEYCODE
.DELETE
, blacklistFunction
);
612 // The page favicon, if any.
613 var faviconUrl
= page
.faviconUrl
;
615 var favicon
= createAndAppendElement(
616 tileElement
, 'div', CLASSES
.FAVICON
);
617 favicon
.style
.backgroundImage
= 'url(' + faviconUrl
+ ')';
619 return new Tile(tileElement
, rid
);
621 return new Tile(tileElement
);
627 * Generates a function to be called when the page with the corresponding RID
629 * @param {number} rid The RID of the page being blacklisted.
630 * @return {function(Event)} A function which handles the blacklisting of the
631 * page by updating state variables and notifying Chrome.
633 function generateBlacklistFunction(rid
) {
635 // Prevent navigation when the page is being blacklisted.
638 userInitiatedMostVisitedChange
= true;
639 isBlacklisting
= true;
640 tilesContainer
.classList
.add(CLASSES
.HIDE_BLACKLIST_BUTTON
);
641 lastBlacklistedTile
= getTileByRid(rid
);
642 ntpApiHandle
.deleteMostVisitedItem(rid
);
648 * Shows the blacklist notification and triggers a delay to hide it.
650 function showNotification() {
651 notification
.classList
.remove(CLASSES
.HIDE_NOTIFICATION
);
652 notification
.classList
.remove(CLASSES
.DELAYED_HIDE_NOTIFICATION
);
653 notification
.scrollTop
;
654 notification
.classList
.add(CLASSES
.DELAYED_HIDE_NOTIFICATION
);
659 * Hides the blacklist notification.
661 function hideNotification() {
662 notification
.classList
.add(CLASSES
.HIDE_NOTIFICATION
);
667 * Handles the end of the blacklist animation by showing the notification and
668 * re-rendering the new set of tiles.
670 function blacklistAnimationDone() {
672 isBlacklisting
= false;
673 tilesContainer
.classList
.remove(CLASSES
.HIDE_BLACKLIST_BUTTON
);
674 lastBlacklistedTile
.elem
.removeEventListener(
675 'webkitTransitionEnd', blacklistAnimationDone
);
676 // Need to call explicitly to re-render the tiles, since the initial
677 // onmostvisitedchange issued by the blacklist function only triggered
679 onMostVisitedChange();
684 * Handles a click on the notification undo link by hiding the notification and
688 userInitiatedMostVisitedChange
= true;
690 var lastBlacklistedRID
= lastBlacklistedTile
.rid
;
691 if (typeof lastBlacklistedRID
!= 'undefined')
692 ntpApiHandle
.undoMostVisitedDeletion(lastBlacklistedRID
);
697 * Handles a click on the restore all notification link by hiding the
698 * notification and informing Chrome.
700 function onRestoreAll() {
701 userInitiatedMostVisitedChange
= true;
703 ntpApiHandle
.undoAllMostVisitedDeletions();
708 * Re-renders the tiles if the number of columns has changed. As a temporary
709 * fix for crbug/240510, updates the width of the fakebox and most visited tiles
712 function onResize() {
713 // If innerWidth is zero, then use the maximum snap size.
714 var innerWidth
= window
.innerWidth
|| 820;
716 // These values should remain in sync with local_ntp.css.
717 // TODO(jeremycho): Delete once the root cause of crbug/240510 is resolved.
718 var setWidths = function(tilesContainerWidth
) {
719 tilesContainer
.style
.width
= tilesContainerWidth
+ 'px';
721 fakebox
.style
.width
= (tilesContainerWidth
- 2) + 'px';
723 if (innerWidth
>= 820)
725 else if (innerWidth
>= 660)
730 var tileRequiredWidth
= TILE_WIDTH
+ TILE_MARGIN_START
;
731 // Adds margin-start to the available width to compensate the extra margin
732 // counted above for the first tile (which does not have a margin-start).
733 var availableWidth
= innerWidth
+ TILE_MARGIN_START
-
734 MIN_TOTAL_HORIZONTAL_PADDING
;
735 var numColumnsToShow
= Math
.floor(availableWidth
/ tileRequiredWidth
);
736 numColumnsToShow
= Math
.max(MIN_NUM_COLUMNS
,
737 Math
.min(MAX_NUM_COLUMNS
, numColumnsToShow
));
738 if (numColumnsToShow
!= numColumnsShown
) {
739 numColumnsShown
= numColumnsToShow
;
746 * Returns the tile corresponding to the specified page RID.
747 * @param {number} rid The page RID being looked up.
748 * @return {Tile} The corresponding tile.
750 function getTileByRid(rid
) {
751 for (var i
= 0, length
= tiles
.length
; i
< length
; ++i
) {
761 * Handles new input by disposing the NTP, according to where the input was
764 function onInputStart() {
765 if (fakebox
&& isFakeboxFocused()) {
766 setFakeboxFocus(false);
767 setFakeboxDragFocus(false);
769 } else if (!isFakeboxFocused()) {
776 * Disposes the NTP, according to where the input was entered.
777 * @param {boolean} wasFakeboxInput True if the input was in the fakebox.
779 function disposeNtp(wasFakeboxInput
) {
780 var behavior
= wasFakeboxInput
? fakeboxInputBehavior
: omniboxInputBehavior
;
781 if (behavior
== NTP_DISPOSE_STATE
.DISABLE_FAKEBOX
)
782 setFakeboxActive(false);
783 else if (behavior
== NTP_DISPOSE_STATE
.HIDE_FAKEBOX_AND_LOGO
)
784 setFakeboxAndLogoVisibility(false);
789 * Restores the NTP (re-enables the fakebox and unhides the logo.)
791 function restoreNtp() {
792 setFakeboxActive(true);
793 setFakeboxAndLogoVisibility(true);
798 * @param {boolean} focus True to focus the fakebox.
800 function setFakeboxFocus(focus
) {
801 document
.body
.classList
.toggle(CLASSES
.FAKEBOX_FOCUS
, focus
);
805 * @param {boolean} focus True to show a dragging focus to the fakebox.
807 function setFakeboxDragFocus(focus
) {
808 document
.body
.classList
.toggle(CLASSES
.FAKEBOX_DRAG_FOCUS
, focus
);
812 * @return {boolean} True if the fakebox has focus.
814 function isFakeboxFocused() {
815 return document
.body
.classList
.contains(CLASSES
.FAKEBOX_FOCUS
) ||
816 document
.body
.classList
.contains(CLASSES
.FAKEBOX_DRAG_FOCUS
);
821 * @param {boolean} enable True to enable the fakebox.
823 function setFakeboxActive(enable
) {
824 document
.body
.classList
.toggle(CLASSES
.FAKEBOX_DISABLE
, !enable
);
829 * @param {!Event} event The click event.
830 * @return {boolean} True if the click occurred in an enabled fakebox.
832 function isFakeboxClick(event
) {
833 return fakebox
.contains(event
.target
) &&
834 !document
.body
.classList
.contains(CLASSES
.FAKEBOX_DISABLE
);
839 * @param {boolean} show True to show the fakebox and logo.
841 function setFakeboxAndLogoVisibility(show
) {
842 document
.body
.classList
.toggle(CLASSES
.HIDE_FAKEBOX_AND_LOGO
, !show
);
847 * Shortcut for document.getElementById.
848 * @param {string} id of the element.
849 * @return {HTMLElement} with the id.
852 return document
.getElementById(id
);
857 * Utility function which creates an element with an optional classname and
858 * appends it to the specified parent.
859 * @param {Element} parent The parent to append the new element.
860 * @param {string} name The name of the new element.
861 * @param {string=} opt_class The optional classname of the new element.
862 * @return {Element} The new element.
864 function createAndAppendElement(parent
, name
, opt_class
) {
865 var child
= document
.createElement(name
);
867 child
.classList
.add(opt_class
);
868 parent
.appendChild(child
);
874 * Removes a node from its parent.
875 * @param {Node} node The node to remove.
877 function removeNode(node
) {
878 node
.parentNode
.removeChild(node
);
883 * Removes all the child nodes on a DOM node.
884 * @param {Node} node Node to remove children from.
886 function removeChildren(node
) {
892 * @param {!Element} element The element to register the handler for.
893 * @param {number} keycode The keycode of the key to register.
894 * @param {!Function} handler The key handler to register.
896 function registerKeyHandler(element
, keycode
, handler
) {
897 element
.addEventListener('keydown', function(event
) {
898 if (event
.keyCode
== keycode
)
905 * @return {Object} the handle to the embeddedSearch API.
907 function getEmbeddedSearchApiHandle() {
910 if (window
.chrome
&& window
.chrome
.embeddedSearch
)
911 return window
.chrome
.embeddedSearch
;
917 * Prepares the New Tab Page by adding listeners, rendering the current
918 * theme, the most visited pages section, and Google-specific elements for a
919 * Google-provided page.
922 tilesContainer
= $(IDS
.TILES
);
923 notification
= $(IDS
.NOTIFICATION
);
924 attribution
= $(IDS
.ATTRIBUTION
);
925 ntpContents
= $(IDS
.NTP_CONTENTS
);
927 for (var i
= 0; i
< NUM_ROWS
; i
++) {
928 var row
= document
.createElement('div');
929 row
.classList
.add(CLASSES
.ROW
);
930 tilesContainer
.appendChild(row
);
933 if (configData
.isGooglePage
) {
934 var logo
= document
.createElement('div');
937 fakebox
= document
.createElement('div');
938 fakebox
.id
= IDS
.FAKEBOX
;
940 '<input id="' + IDS
.FAKEBOX_INPUT
+
941 '" autocomplete="off" tabindex="-1" aria-hidden="true">' +
942 '<div id=cursor></div>';
944 ntpContents
.insertBefore(fakebox
, ntpContents
.firstChild
);
945 ntpContents
.insertBefore(logo
, ntpContents
.firstChild
);
947 document
.body
.classList
.add(CLASSES
.NON_GOOGLE_PAGE
);
950 var notificationMessage
= $(IDS
.NOTIFICATION_MESSAGE
);
951 notificationMessage
.textContent
=
952 configData
.translatedStrings
.thumbnailRemovedNotification
;
953 var undoLink
= $(IDS
.UNDO_LINK
);
954 undoLink
.addEventListener('click', onUndo
);
955 registerKeyHandler(undoLink
, KEYCODE
.ENTER
, onUndo
);
956 undoLink
.textContent
= configData
.translatedStrings
.undoThumbnailRemove
;
957 var restoreAllLink
= $(IDS
.RESTORE_ALL_LINK
);
958 restoreAllLink
.addEventListener('click', onRestoreAll
);
959 registerKeyHandler(restoreAllLink
, KEYCODE
.ENTER
, onUndo
);
960 restoreAllLink
.textContent
=
961 configData
.translatedStrings
.restoreThumbnailsShort
;
962 $(IDS
.ATTRIBUTION_TEXT
).textContent
=
963 configData
.translatedStrings
.attributionIntro
;
965 var notificationCloseButton
= $(IDS
.NOTIFICATION_CLOSE_BUTTON
);
966 notificationCloseButton
.addEventListener('click', hideNotification
);
968 userInitiatedMostVisitedChange
= false;
969 window
.addEventListener('resize', onResize
);
972 var topLevelHandle
= getEmbeddedSearchApiHandle();
974 ntpApiHandle
= topLevelHandle
.newTabPage
;
975 ntpApiHandle
.onthemechange
= onThemeChange
;
976 ntpApiHandle
.onmostvisitedchange
= onMostVisitedChange
;
978 ntpApiHandle
.oninputstart
= onInputStart
;
979 ntpApiHandle
.oninputcancel
= restoreNtp
;
981 if (ntpApiHandle
.isInputInProgress
)
985 onMostVisitedChange();
987 searchboxApiHandle
= topLevelHandle
.searchBox
;
990 // Listener for updating the key capture state.
991 document
.body
.onmousedown = function(event
) {
992 if (isFakeboxClick(event
))
993 searchboxApiHandle
.startCapturingKeyStrokes();
994 else if (isFakeboxFocused())
995 searchboxApiHandle
.stopCapturingKeyStrokes();
997 searchboxApiHandle
.onkeycapturechange = function() {
998 setFakeboxFocus(searchboxApiHandle
.isKeyCaptureEnabled
);
1000 var inputbox
= $(IDS
.FAKEBOX_INPUT
);
1002 inputbox
.onpaste = function(event
) {
1003 event
.preventDefault();
1004 searchboxApiHandle
.paste();
1006 inputbox
.ondrop = function(event
) {
1007 event
.preventDefault();
1008 var text
= event
.dataTransfer
.getData('text/plain');
1010 searchboxApiHandle
.paste(text
);
1013 inputbox
.ondragenter = function() {
1014 setFakeboxDragFocus(true);
1016 inputbox
.ondragleave = function() {
1017 setFakeboxDragFocus(false);
1021 // Update the fakebox style to match the current key capturing state.
1022 setFakeboxFocus(searchboxApiHandle
.isKeyCaptureEnabled
);
1025 if (searchboxApiHandle
.rtl
) {
1026 $(IDS
.NOTIFICATION
).dir
= 'rtl';
1027 // Add class for setting alignments based on language directionality.
1028 document
.body
.classList
.add(CLASSES
.RTL
);
1029 $(IDS
.TILES
).dir
= 'rtl';
1035 * Binds event listeners.
1038 document
.addEventListener('DOMContentLoaded', init
);
1047 if (!window
.localNTPUnitTest
) {
1048 LocalNTP().listen();