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

Side by Side Diff: chrome/browser/resources/local_ntp/local_ntp.js

Issue 2939273002: DO NOT SUBMIT: what chrome/browser/resources/ could eventually look like with clang-format (Closed)
Patch Set: Created 3 years, 6 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
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 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 // NOTE: If you modify this file, you also have to change its hash in 6 // NOTE: If you modify this file, you also have to change its hash in
7 // local_ntp.html and in LocalNtpSource::GetContentSecurityPolicyScriptSrc. 7 // local_ntp.html and in LocalNtpSource::GetContentSecurityPolicyScriptSrc.
8 // To calculate the sum execute the following command 8 // To calculate the sum execute the following command
9 // sha256sum local_ntp.js | cut -d " " -f 1 | xxd -r -p | base64 9 // sha256sum local_ntp.js | cut -d " " -f 1 | xxd -r -p | base64
10 10
11 11
12 /** 12 /**
13 * @fileoverview The local InstantExtended NTP. 13 * @fileoverview The local InstantExtended NTP.
14 */ 14 */
15 15
16 16
17 /** 17 /**
18 * Controls rendering the new tab page for InstantExtended. 18 * Controls rendering the new tab page for InstantExtended.
19 * @return {Object} A limited interface for testing the local NTP. 19 * @return {Object} A limited interface for testing the local NTP.
20 */ 20 */
21 function LocalNTP() { 21 function LocalNTP() {
22 'use strict'; 22 'use strict';
23 23
24 24
25 /** 25 /**
26 * Alias for document.getElementById. 26 * Alias for document.getElementById.
27 * @param {string} id The ID of the element to find. 27 * @param {string} id The ID of the element to find.
28 * @return {HTMLElement} The found element or null if not found. 28 * @return {HTMLElement} The found element or null if not found.
29 */ 29 */
30 function $(id) { 30 function $(id) {
31 return document.getElementById(id); 31 return document.getElementById(id);
32 } 32 }
33 33
34 34
35 /** 35 /**
36 * Specifications for an NTP design (not comprehensive). 36 * Specifications for an NTP design (not comprehensive).
37 * 37 *
38 * numTitleLines: Number of lines to display in titles. 38 * numTitleLines: Number of lines to display in titles.
39 * tileWidth: The width of each suggestion tile, in px. 39 * tileWidth: The width of each suggestion tile, in px.
40 * tileMargin: Spacing between successive tiles, in px. 40 * tileMargin: Spacing between successive tiles, in px.
41 * titleColor: The 4-component color of title text. 41 * titleColor: The 4-component color of title text.
42 * titleColorAgainstDark: The 4-component color of title text against a dark 42 * titleColorAgainstDark: The 4-component color of title text against a dark
43 * theme. 43 * theme.
44 * 44 *
45 * @type {{ 45 * @type {{
46 * numTitleLines: number, 46 * numTitleLines: number,
47 * tileWidth: number, 47 * tileWidth: number,
48 * tileMargin: number, 48 * tileMargin: number,
49 * titleColor: string, 49 * titleColor: string,
50 * titleColorAgainstDark: string, 50 * titleColorAgainstDark: string,
51 * }} 51 * }}
52 */ 52 */
53 var NTP_DESIGN = { 53 var NTP_DESIGN = {
54 numTitleLines: 1, 54 numTitleLines: 1,
55 tileWidth: 154, 55 tileWidth: 154,
56 tileMargin: 16, 56 tileMargin: 16,
57 titleColor: [50, 50, 50, 255], 57 titleColor: [50, 50, 50, 255],
58 titleColorAgainstDark: [210, 210, 210, 255], 58 titleColorAgainstDark: [210, 210, 210, 255],
59 }; 59 };
60 60
61 61
62 /** 62 /**
63 * Enum for classnames. 63 * Enum for classnames.
64 * @enum {string} 64 * @enum {string}
65 * @const 65 * @const
66 */ 66 */
67 var CLASSES = { 67 var CLASSES = {
68 ALTERNATE_LOGO: 'alternate-logo', // Shows white logo if required by theme 68 ALTERNATE_LOGO: 'alternate-logo', // Shows white logo if required by theme
69 DARK: 'dark', 69 DARK: 'dark',
70 DEFAULT_THEME: 'default-theme', 70 DEFAULT_THEME: 'default-theme',
71 DELAYED_HIDE_NOTIFICATION: 'mv-notice-delayed-hide', 71 DELAYED_HIDE_NOTIFICATION: 'mv-notice-delayed-hide',
72 FAKEBOX_FOCUS: 'fakebox-focused', // Applies focus styles to the fakebox 72 FAKEBOX_FOCUS: 'fakebox-focused', // Applies focus styles to the fakebox
73 // Applies drag focus style to the fakebox 73 // Applies drag focus style to the fakebox
74 FAKEBOX_DRAG_FOCUS: 'fakebox-drag-focused', 74 FAKEBOX_DRAG_FOCUS: 'fakebox-drag-focused',
75 HIDE_FAKEBOX_AND_LOGO: 'hide-fakebox-logo', 75 HIDE_FAKEBOX_AND_LOGO: 'hide-fakebox-logo',
76 HIDE_NOTIFICATION: 'mv-notice-hide', 76 HIDE_NOTIFICATION: 'mv-notice-hide',
77 LEFT_ALIGN_ATTRIBUTION: 'left-align-attribution', 77 LEFT_ALIGN_ATTRIBUTION: 'left-align-attribution',
78 // Vertically centers the most visited section for a non-Google provided page. 78 // Vertically centers the most visited section for a non-Google provided
79 NON_GOOGLE_PAGE: 'non-google-page', 79 // page.
80 RTL: 'rtl' // Right-to-left language text. 80 NON_GOOGLE_PAGE: 'non-google-page',
81 }; 81 RTL: 'rtl' // Right-to-left language text.
82 82 };
83 83
84 /** 84
85 * Enum for HTML element ids. 85 /**
86 * @enum {string} 86 * Enum for HTML element ids.
87 * @const 87 * @enum {string}
88 */ 88 * @const
89 var IDS = { 89 */
90 ATTRIBUTION: 'attribution', 90 var IDS = {
91 ATTRIBUTION_TEXT: 'attribution-text', 91 ATTRIBUTION: 'attribution',
92 CUSTOM_THEME_STYLE: 'ct-style', 92 ATTRIBUTION_TEXT: 'attribution-text',
93 FAKEBOX: 'fakebox', 93 CUSTOM_THEME_STYLE: 'ct-style',
94 FAKEBOX_INPUT: 'fakebox-input', 94 FAKEBOX: 'fakebox',
95 FAKEBOX_TEXT: 'fakebox-text', 95 FAKEBOX_INPUT: 'fakebox-input',
96 LOGO: 'logo', 96 FAKEBOX_TEXT: 'fakebox-text',
97 NOTIFICATION: 'mv-notice', 97 LOGO: 'logo',
98 NOTIFICATION_CLOSE_BUTTON: 'mv-notice-x', 98 NOTIFICATION: 'mv-notice',
99 NOTIFICATION_MESSAGE: 'mv-msg', 99 NOTIFICATION_CLOSE_BUTTON: 'mv-notice-x',
100 NTP_CONTENTS: 'ntp-contents', 100 NOTIFICATION_MESSAGE: 'mv-msg',
101 RESTORE_ALL_LINK: 'mv-restore', 101 NTP_CONTENTS: 'ntp-contents',
102 TILES: 'mv-tiles', 102 RESTORE_ALL_LINK: 'mv-restore',
103 TILES_IFRAME: 'mv-single', 103 TILES: 'mv-tiles',
104 UNDO_LINK: 'mv-undo' 104 TILES_IFRAME: 'mv-single',
105 }; 105 UNDO_LINK: 'mv-undo'
106 106 };
107 107
108 /** 108
109 * Enum for keycodes. 109 /**
110 * @enum {number} 110 * Enum for keycodes.
111 * @const 111 * @enum {number}
112 */ 112 * @const
113 var KEYCODE = { 113 */
114 ENTER: 13 114 var KEYCODE = {ENTER: 13};
115 }; 115
116 116
117 117 /**
118 /** 118 * The last blacklisted tile rid if any, which by definition should not be
119 * The last blacklisted tile rid if any, which by definition should not be 119 * filler.
120 * filler. 120 * @type {?number}
121 * @type {?number} 121 */
122 */ 122 var lastBlacklistedTile = null;
123 var lastBlacklistedTile = null; 123
124 124
125 125 /**
126 /** 126 * Current number of tiles columns shown based on the window width, including
127 * Current number of tiles columns shown based on the window width, including 127 * those that just contain filler.
128 * those that just contain filler. 128 * @type {number}
129 * @type {number} 129 */
130 */ 130 var numColumnsShown = 0;
131 var numColumnsShown = 0; 131
132 132
133 133 /**
134 /** 134 * The browser embeddedSearch.newTabPage object.
135 * The browser embeddedSearch.newTabPage object. 135 * @type {Object}
136 * @type {Object} 136 */
137 */ 137 var ntpApiHandle;
138 var ntpApiHandle; 138
139 139
140 140 /** @type {number} @const */
141 /** @type {number} @const */ 141 var MAX_NUM_TILES_TO_SHOW = 8;
142 var MAX_NUM_TILES_TO_SHOW = 8; 142
143 143
144 144 /** @type {number} @const */
145 /** @type {number} @const */ 145 var MIN_NUM_COLUMNS = 2;
146 var MIN_NUM_COLUMNS = 2; 146
147 147
148 148 /** @type {number} @const */
149 /** @type {number} @const */ 149 var MAX_NUM_COLUMNS = 4;
150 var MAX_NUM_COLUMNS = 4; 150
151 151
152 152 /** @type {number} @const */
153 /** @type {number} @const */ 153 var NUM_ROWS = 2;
154 var NUM_ROWS = 2; 154
155 155
156 156 /**
157 /** 157 * Minimum total padding to give to the left and right of the most visited
158 * Minimum total padding to give to the left and right of the most visited 158 * section. Used to determine how many tiles to show.
159 * section. Used to determine how many tiles to show. 159 * @type {number}
160 * @type {number} 160 * @const
161 * @const 161 */
162 */ 162 var MIN_TOTAL_HORIZONTAL_PADDING = 200;
163 var MIN_TOTAL_HORIZONTAL_PADDING = 200; 163
164 164
165 165 /**
166 /** 166 * Heuristic to determine whether a theme should be considered to be dark, so
167 * Heuristic to determine whether a theme should be considered to be dark, so 167 * the colors of various UI elements can be adjusted.
168 * the colors of various UI elements can be adjusted. 168 * @param {ThemeBackgroundInfo|undefined} info Theme background information.
169 * @param {ThemeBackgroundInfo|undefined} info Theme background information. 169 * @return {boolean} Whether the theme is dark.
170 * @return {boolean} Whether the theme is dark. 170 * @private
171 * @private 171 */
172 */ 172 function getIsThemeDark(info) {
173 function getIsThemeDark(info) { 173 if (!info)
174 if (!info) 174 return false;
175 return false; 175 // Heuristic: light text implies dark theme.
176 // Heuristic: light text implies dark theme. 176 var rgba = info.textColorRgba;
177 var rgba = info.textColorRgba; 177 var luminance = 0.3 * rgba[0] + 0.59 * rgba[1] + 0.11 * rgba[2];
178 var luminance = 0.3 * rgba[0] + 0.59 * rgba[1] + 0.11 * rgba[2]; 178 return luminance >= 128;
179 return luminance >= 128; 179 }
180 } 180
181 181
182 182 /**
183 /** 183 * Updates the NTP based on the current theme.
184 * Updates the NTP based on the current theme. 184 * @private
185 * @private 185 */
186 */ 186 function renderTheme() {
187 function renderTheme() { 187 var info = ntpApiHandle.themeBackgroundInfo;
188 var info = ntpApiHandle.themeBackgroundInfo; 188 var isThemeDark = getIsThemeDark(info);
189 var isThemeDark = getIsThemeDark(info); 189 $(IDS.NTP_CONTENTS).classList.toggle(CLASSES.DARK, isThemeDark);
190 $(IDS.NTP_CONTENTS).classList.toggle(CLASSES.DARK, isThemeDark); 190 if (!info) {
191 if (!info) { 191 return;
192 return; 192 }
193 } 193
194 194 var background = [
195 var background = [convertToRGBAColor(info.backgroundColorRgba), 195 convertToRGBAColor(info.backgroundColorRgba), info.imageUrl,
196 info.imageUrl, 196 info.imageTiling, info.imageHorizontalAlignment,
197 info.imageTiling, 197 info.imageVerticalAlignment
198 info.imageHorizontalAlignment, 198 ].join(' ').trim();
199 info.imageVerticalAlignment].join(' ').trim(); 199
200 200 document.body.style.background = background;
201 document.body.style.background = background; 201 document.body.classList.toggle(CLASSES.ALTERNATE_LOGO, info.alternateLogo);
202 document.body.classList.toggle(CLASSES.ALTERNATE_LOGO, info.alternateLogo); 202 updateThemeAttribution(info.attributionUrl, info.imageHorizontalAlignment);
203 updateThemeAttribution(info.attributionUrl, info.imageHorizontalAlignment); 203 setCustomThemeStyle(info);
204 setCustomThemeStyle(info); 204
205 205 // Inform the most visited iframe of the new theme.
206 // Inform the most visited iframe of the new theme. 206 var themeinfo = {cmd: 'updateTheme'};
207 var themeinfo = {cmd: 'updateTheme'}; 207 themeinfo.tileBorderColor = convertToRGBAColor(info.sectionBorderColorRgba);
208 themeinfo.tileBorderColor = convertToRGBAColor(info.sectionBorderColorRgba); 208 themeinfo.tileHoverBorderColor = convertToRGBAColor(info.headerColorRgba);
209 themeinfo.tileHoverBorderColor = convertToRGBAColor(info.headerColorRgba); 209 themeinfo.isThemeDark = isThemeDark;
210 themeinfo.isThemeDark = isThemeDark; 210
211 211 var titleColor = NTP_DESIGN.titleColor;
212 var titleColor = NTP_DESIGN.titleColor; 212 if (!info.usingDefaultTheme && info.textColorRgba) {
213 if (!info.usingDefaultTheme && info.textColorRgba) { 213 titleColor = info.textColorRgba;
214 titleColor = info.textColorRgba; 214 } else if (isThemeDark) {
215 } else if (isThemeDark) { 215 titleColor = NTP_DESIGN.titleColorAgainstDark;
216 titleColor = NTP_DESIGN.titleColorAgainstDark; 216 }
217 } 217 themeinfo.tileTitleColor = convertToRGBAColor(titleColor);
218 themeinfo.tileTitleColor = convertToRGBAColor(titleColor); 218
219 219 $(IDS.TILES_IFRAME).contentWindow.postMessage(themeinfo, '*');
220 $(IDS.TILES_IFRAME).contentWindow.postMessage(themeinfo, '*'); 220 }
221 } 221
222 222
223 223 /**
224 /** 224 * Callback for embeddedSearch.newTabPage.onthemechange.
225 * Callback for embeddedSearch.newTabPage.onthemechange. 225 * @private
226 * @private 226 */
227 */ 227 function onThemeChange() {
228 function onThemeChange() { 228 renderTheme();
229 renderTheme(); 229 }
230 } 230
231 231
232 232 /**
233 /** 233 * Updates the NTP style according to theme.
234 * Updates the NTP style according to theme. 234 * @param {Object=} opt_themeInfo The information about the theme. If it is
235 * @param {Object=} opt_themeInfo The information about the theme. If it is 235 * omitted the style will be reverted to the default.
236 * omitted the style will be reverted to the default. 236 * @private
237 * @private 237 */
238 */ 238 // TODO(treib): We actually never call this without a themeInfo. Should we?
239 // TODO(treib): We actually never call this without a themeInfo. Should we? 239 function setCustomThemeStyle(opt_themeInfo) {
240 function setCustomThemeStyle(opt_themeInfo) { 240 var customStyleElement = $(IDS.CUSTOM_THEME_STYLE);
241 var customStyleElement = $(IDS.CUSTOM_THEME_STYLE); 241 var head = document.head;
242 var head = document.head; 242 if (opt_themeInfo && !opt_themeInfo.usingDefaultTheme) {
243 if (opt_themeInfo && !opt_themeInfo.usingDefaultTheme) { 243 $(IDS.NTP_CONTENTS).classList.remove(CLASSES.DEFAULT_THEME);
244 $(IDS.NTP_CONTENTS).classList.remove(CLASSES.DEFAULT_THEME); 244 var themeStyle = '#attribution {' +
245 var themeStyle = 245 ' color: ' + convertToRGBAColor(opt_themeInfo.textColorLightRgba) +
246 '#attribution {' + 246 ';' +
247 ' color: ' + convertToRGBAColor(opt_themeInfo.textColorLightRgba) + ';' + 247 '}' +
248 '}' + 248 '#mv-msg {' +
249 '#mv-msg {' + 249 ' color: ' + convertToRGBAColor(opt_themeInfo.textColorRgba) + ';' +
250 ' color: ' + convertToRGBAColor(opt_themeInfo.textColorRgba) + ';' + 250 '}' +
251 '}' + 251 '#mv-notice-links span {' +
252 '#mv-notice-links span {' + 252 ' color: ' + convertToRGBAColor(opt_themeInfo.textColorLightRgba) +
253 ' color: ' + convertToRGBAColor(opt_themeInfo.textColorLightRgba) + ';' + 253 ';' +
254 '}' + 254 '}' +
255 '#mv-notice-x {' + 255 '#mv-notice-x {' +
256 ' -webkit-filter: drop-shadow(0 0 0 ' + 256 ' -webkit-filter: drop-shadow(0 0 0 ' +
257 convertToRGBAColor(opt_themeInfo.textColorRgba) + ');' + 257 convertToRGBAColor(opt_themeInfo.textColorRgba) + ');' +
258 '}' + 258 '}' +
259 '.mv-page-ready .mv-mask {' + 259 '.mv-page-ready .mv-mask {' +
260 ' border: 1px solid ' + 260 ' border: 1px solid ' +
261 convertToRGBAColor(opt_themeInfo.sectionBorderColorRgba) + ';' + 261 convertToRGBAColor(opt_themeInfo.sectionBorderColorRgba) + ';' +
262 '}' + 262 '}' +
263 '.mv-page-ready:hover .mv-mask, .mv-page-ready .mv-focused ~ .mv-mask {' + 263 '.mv-page-ready:hover .mv-mask, .mv-page-ready .mv-focused ~ .mv-mask {' +
264 ' border-color: ' + 264 ' border-color: ' +
265 convertToRGBAColor(opt_themeInfo.headerColorRgba) + ';' + 265 convertToRGBAColor(opt_themeInfo.headerColorRgba) + ';' +
266 '}'; 266 '}';
267 267
268 if (customStyleElement) { 268 if (customStyleElement) {
269 customStyleElement.textContent = themeStyle; 269 customStyleElement.textContent = themeStyle;
270 } else {
271 customStyleElement = document.createElement('style');
272 customStyleElement.type = 'text/css';
273 customStyleElement.id = IDS.CUSTOM_THEME_STYLE;
274 customStyleElement.textContent = themeStyle;
275 head.appendChild(customStyleElement);
276 }
277
270 } else { 278 } else {
271 customStyleElement = document.createElement('style'); 279 $(IDS.NTP_CONTENTS).classList.add(CLASSES.DEFAULT_THEME);
272 customStyleElement.type = 'text/css'; 280 if (customStyleElement)
273 customStyleElement.id = IDS.CUSTOM_THEME_STYLE; 281 head.removeChild(customStyleElement);
274 customStyleElement.textContent = themeStyle; 282 }
275 head.appendChild(customStyleElement); 283 }
276 } 284
277 285
278 } else { 286 /**
279 $(IDS.NTP_CONTENTS).classList.add(CLASSES.DEFAULT_THEME); 287 * Renders the attribution if the URL is present, otherwise hides it.
280 if (customStyleElement) 288 * @param {string} url The URL of the attribution image, if any.
281 head.removeChild(customStyleElement); 289 * @param {string} themeBackgroundAlignment The alignment of the theme
282 } 290 * background image. This is used to compute the attribution's alignment.
283 } 291 * @private
284 292 */
285 293 function updateThemeAttribution(url, themeBackgroundAlignment) {
286 /** 294 if (!url) {
287 * Renders the attribution if the URL is present, otherwise hides it. 295 setAttributionVisibility_(false);
288 * @param {string} url The URL of the attribution image, if any. 296 return;
289 * @param {string} themeBackgroundAlignment The alignment of the theme 297 }
290 * background image. This is used to compute the attribution's alignment. 298
291 * @private 299 var attribution = $(IDS.ATTRIBUTION);
292 */ 300 var attributionImage = attribution.querySelector('img');
293 function updateThemeAttribution(url, themeBackgroundAlignment) { 301 if (!attributionImage) {
294 if (!url) { 302 attributionImage = new Image();
295 setAttributionVisibility_(false); 303 attribution.appendChild(attributionImage);
296 return; 304 }
297 } 305 attributionImage.style.content = url;
298 306
299 var attribution = $(IDS.ATTRIBUTION); 307 // To avoid conflicts, place the attribution on the left for themes that
300 var attributionImage = attribution.querySelector('img'); 308 // right align their background images.
301 if (!attributionImage) { 309 attribution.classList.toggle(
302 attributionImage = new Image(); 310 CLASSES.LEFT_ALIGN_ATTRIBUTION, themeBackgroundAlignment == 'right');
303 attribution.appendChild(attributionImage); 311 setAttributionVisibility_(true);
304 } 312 }
305 attributionImage.style.content = url; 313
306 314
307 // To avoid conflicts, place the attribution on the left for themes that 315 /**
308 // right align their background images. 316 * Sets the visibility of the theme attribution.
309 attribution.classList.toggle(CLASSES.LEFT_ALIGN_ATTRIBUTION, 317 * @param {boolean} show True to show the attribution.
310 themeBackgroundAlignment == 'right'); 318 * @private
311 setAttributionVisibility_(true); 319 */
312 } 320 function setAttributionVisibility_(show) {
313 321 $(IDS.ATTRIBUTION).style.display = show ? '' : 'none';
314 322 }
315 /** 323
316 * Sets the visibility of the theme attribution. 324
317 * @param {boolean} show True to show the attribution. 325 /**
318 * @private 326 * Converts an Array of color components into RGBA format "rgba(R,G,B,A)".
319 */ 327 * @param {Array<number>} color Array of rgba color components.
320 function setAttributionVisibility_(show) { 328 * @return {string} CSS color in RGBA format.
321 $(IDS.ATTRIBUTION).style.display = show ? '' : 'none'; 329 * @private
322 } 330 */
323 331 function convertToRGBAColor(color) {
324 332 return 'rgba(' + color[0] + ',' + color[1] + ',' + color[2] + ',' +
325 /** 333 color[3] / 255 + ')';
326 * Converts an Array of color components into RGBA format "rgba(R,G,B,A)". 334 }
327 * @param {Array<number>} color Array of rgba color components. 335
328 * @return {string} CSS color in RGBA format. 336
329 * @private 337 /**
330 */ 338 * Callback for embeddedSearch.newTabPage.onmostvisitedchange. Called when the
331 function convertToRGBAColor(color) { 339 * NTP tiles are updated.
332 return 'rgba(' + color[0] + ',' + color[1] + ',' + color[2] + ',' + 340 */
333 color[3] / 255 + ')'; 341 function onMostVisitedChange() {
334 } 342 reloadTiles();
335 343 }
336 344
337 /** 345
338 * Callback for embeddedSearch.newTabPage.onmostvisitedchange. Called when the 346 /**
339 * NTP tiles are updated. 347 * Fetches new data (RIDs) from the embeddedSearch.newTabPage API and passes
340 */ 348 * them to the iframe.
341 function onMostVisitedChange() { 349 */
342 reloadTiles(); 350 function reloadTiles() {
343 } 351 var pages = ntpApiHandle.mostVisited;
344 352 var cmds = [];
345 353 for (var i = 0; i < Math.min(MAX_NUM_TILES_TO_SHOW, pages.length); ++i) {
346 /** 354 cmds.push({cmd: 'tile', rid: pages[i].rid});
347 * Fetches new data (RIDs) from the embeddedSearch.newTabPage API and passes 355 }
348 * them to the iframe. 356 cmds.push({cmd: 'show', maxVisible: numColumnsShown * NUM_ROWS});
349 */ 357
350 function reloadTiles() { 358 $(IDS.TILES_IFRAME).contentWindow.postMessage(cmds, '*');
351 var pages = ntpApiHandle.mostVisited; 359 }
352 var cmds = []; 360
353 for (var i = 0; i < Math.min(MAX_NUM_TILES_TO_SHOW, pages.length); ++i) { 361
354 cmds.push({cmd: 'tile', rid: pages[i].rid}); 362 /**
355 } 363 * Shows the blacklist notification and triggers a delay to hide it.
356 cmds.push({cmd: 'show', maxVisible: numColumnsShown * NUM_ROWS}); 364 */
357 365 function showNotification() {
358 $(IDS.TILES_IFRAME).contentWindow.postMessage(cmds, '*'); 366 var notification = $(IDS.NOTIFICATION);
359 } 367 notification.classList.remove(CLASSES.HIDE_NOTIFICATION);
360 368 notification.classList.remove(CLASSES.DELAYED_HIDE_NOTIFICATION);
361 369 notification.scrollTop;
362 /** 370 notification.classList.add(CLASSES.DELAYED_HIDE_NOTIFICATION);
363 * Shows the blacklist notification and triggers a delay to hide it. 371 }
364 */ 372
365 function showNotification() { 373
366 var notification = $(IDS.NOTIFICATION); 374 /**
367 notification.classList.remove(CLASSES.HIDE_NOTIFICATION); 375 * Hides the blacklist notification.
368 notification.classList.remove(CLASSES.DELAYED_HIDE_NOTIFICATION); 376 */
369 notification.scrollTop; 377 function hideNotification() {
370 notification.classList.add(CLASSES.DELAYED_HIDE_NOTIFICATION); 378 var notification = $(IDS.NOTIFICATION);
371 } 379 notification.classList.add(CLASSES.HIDE_NOTIFICATION);
372 380 notification.classList.remove(CLASSES.DELAYED_HIDE_NOTIFICATION);
373 381 }
374 /** 382
375 * Hides the blacklist notification. 383
376 */ 384 /**
377 function hideNotification() { 385 * Handles a click on the notification undo link by hiding the notification
378 var notification = $(IDS.NOTIFICATION); 386 * and informing Chrome.
379 notification.classList.add(CLASSES.HIDE_NOTIFICATION); 387 */
380 notification.classList.remove(CLASSES.DELAYED_HIDE_NOTIFICATION); 388 function onUndo() {
381 } 389 hideNotification();
382 390 if (lastBlacklistedTile != null) {
383 391 ntpApiHandle.undoMostVisitedDeletion(lastBlacklistedTile);
384 /** 392 }
385 * Handles a click on the notification undo link by hiding the notification and 393 }
386 * informing Chrome. 394
387 */ 395
388 function onUndo() { 396 /**
389 hideNotification(); 397 * Handles a click on the restore all notification link by hiding the
390 if (lastBlacklistedTile != null) { 398 * notification and informing Chrome.
391 ntpApiHandle.undoMostVisitedDeletion(lastBlacklistedTile); 399 */
392 } 400 function onRestoreAll() {
393 } 401 hideNotification();
394 402 ntpApiHandle.undoAllMostVisitedDeletions();
395 403 }
396 /** 404
397 * Handles a click on the restore all notification link by hiding the 405
398 * notification and informing Chrome. 406 /**
399 */ 407 * Recomputes the number of tile columns, and width of various contents based
400 function onRestoreAll() { 408 * on the width of the window.
401 hideNotification(); 409 * @return {boolean} Whether the number of tile columns has changed.
402 ntpApiHandle.undoAllMostVisitedDeletions(); 410 */
403 } 411 function updateContentWidth() {
404 412 var tileRequiredWidth = NTP_DESIGN.tileWidth + NTP_DESIGN.tileMargin;
405 413 // If innerWidth is zero, then use the maximum snap size.
406 /** 414 var maxSnapSize = MAX_NUM_COLUMNS * tileRequiredWidth -
407 * Recomputes the number of tile columns, and width of various contents based 415 NTP_DESIGN.tileMargin + MIN_TOTAL_HORIZONTAL_PADDING;
408 * on the width of the window. 416 var innerWidth = window.innerWidth || maxSnapSize;
409 * @return {boolean} Whether the number of tile columns has changed. 417 // Each tile has left and right margins that sum to NTP_DESIGN.tileMargin.
410 */ 418 var availableWidth =
411 function updateContentWidth() { 419 innerWidth + NTP_DESIGN.tileMargin - MIN_TOTAL_HORIZONTAL_PADDING;
412 var tileRequiredWidth = NTP_DESIGN.tileWidth + NTP_DESIGN.tileMargin; 420 var newNumColumns = Math.floor(availableWidth / tileRequiredWidth);
413 // If innerWidth is zero, then use the maximum snap size. 421 newNumColumns =
414 var maxSnapSize = MAX_NUM_COLUMNS * tileRequiredWidth - 422 Math.max(MIN_NUM_COLUMNS, Math.min(newNumColumns, MAX_NUM_COLUMNS));
415 NTP_DESIGN.tileMargin + MIN_TOTAL_HORIZONTAL_PADDING; 423
416 var innerWidth = window.innerWidth || maxSnapSize; 424 if (numColumnsShown === newNumColumns)
417 // Each tile has left and right margins that sum to NTP_DESIGN.tileMargin. 425 return false;
418 var availableWidth = innerWidth + NTP_DESIGN.tileMargin - 426
419 MIN_TOTAL_HORIZONTAL_PADDING; 427 numColumnsShown = newNumColumns;
420 var newNumColumns = Math.floor(availableWidth / tileRequiredWidth); 428 // We add an extra pixel because rounding errors on different zooms can
421 newNumColumns = 429 // make the width shorter than it should be.
422 Math.max(MIN_NUM_COLUMNS, Math.min(newNumColumns, MAX_NUM_COLUMNS)); 430 var tilesContainerWidth =
423 431 Math.ceil(numColumnsShown * tileRequiredWidth) + 1;
424 if (numColumnsShown === newNumColumns) 432 $(IDS.TILES).style.width = tilesContainerWidth + 'px';
425 return false; 433 // -2 to account for border.
426 434 var fakeboxWidth = (tilesContainerWidth - NTP_DESIGN.tileMargin - 2);
427 numColumnsShown = newNumColumns; 435 $(IDS.FAKEBOX).style.width = fakeboxWidth + 'px';
428 // We add an extra pixel because rounding errors on different zooms can 436 return true;
429 // make the width shorter than it should be. 437 }
430 var tilesContainerWidth = Math.ceil(numColumnsShown * tileRequiredWidth) + 1; 438
431 $(IDS.TILES).style.width = tilesContainerWidth + 'px'; 439
432 // -2 to account for border. 440 /**
433 var fakeboxWidth = (tilesContainerWidth - NTP_DESIGN.tileMargin - 2); 441 * Resizes elements because the number of tile columns may need to change in
434 $(IDS.FAKEBOX).style.width = fakeboxWidth + 'px'; 442 * response to resizing. Also shows or hides extra tiles tiles according to
435 return true; 443 * the new width of the page.
436 } 444 */
437 445 function onResize() {
438 446 if (updateContentWidth()) {
439 /** 447 // If the number of tile columns changes, inform the iframe.
440 * Resizes elements because the number of tile columns may need to change in 448 $(IDS.TILES_IFRAME)
441 * response to resizing. Also shows or hides extra tiles tiles according to the 449 .contentWindow.postMessage(
442 * new width of the page. 450 {cmd: 'tilesVisible', maxVisible: numColumnsShown * NUM_ROWS},
443 */ 451 '*');
444 function onResize() { 452 }
445 if (updateContentWidth()) { 453 }
446 // If the number of tile columns changes, inform the iframe. 454
447 $(IDS.TILES_IFRAME).contentWindow.postMessage( 455
448 {cmd: 'tilesVisible', maxVisible: numColumnsShown * NUM_ROWS}, '*'); 456 /**
449 } 457 * Callback for embeddedSearch.newTabPage.oninputstart. Handles new input by
450 } 458 * disposing the NTP, according to where the input was entered.
451 459 */
452 460 function onInputStart() {
453 /** 461 if (isFakeboxFocused()) {
454 * Callback for embeddedSearch.newTabPage.oninputstart. Handles new input by 462 setFakeboxFocus(false);
455 * disposing the NTP, according to where the input was entered. 463 setFakeboxDragFocus(false);
456 */ 464 setFakeboxAndLogoVisibility(false);
457 function onInputStart() { 465 }
458 if (isFakeboxFocused()) { 466 }
459 setFakeboxFocus(false); 467
460 setFakeboxDragFocus(false); 468
461 setFakeboxAndLogoVisibility(false); 469 /**
462 } 470 * Callback for embeddedSearch.newTabPage.oninputcancel. Restores the NTP
463 } 471 * (re-enables the fakebox and unhides the logo.)
464 472 */
465 473 function onInputCancel() {
466 /** 474 setFakeboxAndLogoVisibility(true);
467 * Callback for embeddedSearch.newTabPage.oninputcancel. Restores the NTP 475 }
468 * (re-enables the fakebox and unhides the logo.) 476
469 */ 477
470 function onInputCancel() { 478 /**
471 setFakeboxAndLogoVisibility(true); 479 * @param {boolean} focus True to focus the fakebox.
472 } 480 */
473 481 function setFakeboxFocus(focus) {
474 482 document.body.classList.toggle(CLASSES.FAKEBOX_FOCUS, focus);
475 /** 483 }
476 * @param {boolean} focus True to focus the fakebox. 484
477 */ 485 /**
478 function setFakeboxFocus(focus) { 486 * @param {boolean} focus True to show a dragging focus on the fakebox.
479 document.body.classList.toggle(CLASSES.FAKEBOX_FOCUS, focus); 487 */
480 } 488 function setFakeboxDragFocus(focus) {
481 489 document.body.classList.toggle(CLASSES.FAKEBOX_DRAG_FOCUS, focus);
482 /** 490 }
483 * @param {boolean} focus True to show a dragging focus on the fakebox. 491
484 */ 492 /**
485 function setFakeboxDragFocus(focus) { 493 * @return {boolean} True if the fakebox has focus.
486 document.body.classList.toggle(CLASSES.FAKEBOX_DRAG_FOCUS, focus); 494 */
487 } 495 function isFakeboxFocused() {
488 496 return document.body.classList.contains(CLASSES.FAKEBOX_FOCUS) ||
489 /** 497 document.body.classList.contains(CLASSES.FAKEBOX_DRAG_FOCUS);
490 * @return {boolean} True if the fakebox has focus. 498 }
491 */ 499
492 function isFakeboxFocused() { 500
493 return document.body.classList.contains(CLASSES.FAKEBOX_FOCUS) || 501 /**
494 document.body.classList.contains(CLASSES.FAKEBOX_DRAG_FOCUS); 502 * @param {!Event} event The click event.
495 } 503 * @return {boolean} True if the click occurred in an enabled fakebox.
496 504 */
497 505 function isFakeboxClick(event) {
498 /** 506 return $(IDS.FAKEBOX).contains(event.target);
499 * @param {!Event} event The click event. 507 }
500 * @return {boolean} True if the click occurred in an enabled fakebox. 508
501 */ 509
502 function isFakeboxClick(event) { 510 /**
503 return $(IDS.FAKEBOX).contains(event.target); 511 * @param {boolean} show True to show the fakebox and logo.
504 } 512 */
505 513 function setFakeboxAndLogoVisibility(show) {
506 514 document.body.classList.toggle(CLASSES.HIDE_FAKEBOX_AND_LOGO, !show);
507 /** 515 }
508 * @param {boolean} show True to show the fakebox and logo. 516
509 */ 517
510 function setFakeboxAndLogoVisibility(show) { 518 /**
511 document.body.classList.toggle(CLASSES.HIDE_FAKEBOX_AND_LOGO, !show); 519 * @param {!Element} element The element to register the handler for.
512 } 520 * @param {number} keycode The keycode of the key to register.
513 521 * @param {!Function} handler The key handler to register.
514 522 */
515 /** 523 function registerKeyHandler(element, keycode, handler) {
516 * @param {!Element} element The element to register the handler for. 524 element.addEventListener('keydown', function(event) {
517 * @param {number} keycode The keycode of the key to register. 525 if (event.keyCode == keycode)
518 * @param {!Function} handler The key handler to register. 526 handler(event);
519 */ 527 });
520 function registerKeyHandler(element, keycode, handler) { 528 }
521 element.addEventListener('keydown', function(event) { 529
522 if (event.keyCode == keycode) 530
523 handler(event); 531 /**
524 }); 532 * Event handler for the focus changed and blacklist messages on link
525 } 533 * elements. Used to toggle visual treatment on the tiles (depending on the
526 534 * message).
527 535 * @param {Event} event Event received.
528 /** 536 */
529 * Event handler for the focus changed and blacklist messages on link elements. 537 function handlePostMessage(event) {
530 * Used to toggle visual treatment on the tiles (depending on the message). 538 var cmd = event.data.cmd;
531 * @param {Event} event Event received. 539 var args = event.data;
532 */ 540 if (cmd == 'tileBlacklisted') {
533 function handlePostMessage(event) { 541 showNotification();
534 var cmd = event.data.cmd; 542 lastBlacklistedTile = args.tid;
535 var args = event.data; 543
536 if (cmd == 'tileBlacklisted') { 544 ntpApiHandle.deleteMostVisitedItem(args.tid);
537 showNotification(); 545 }
538 lastBlacklistedTile = args.tid; 546 // TODO(treib): Should we also handle the 'loaded' message from the iframe
539 547 // here? We could hide the page until it arrives, to avoid flicker.
540 ntpApiHandle.deleteMostVisitedItem(args.tid); 548 }
541 } 549
542 // TODO(treib): Should we also handle the 'loaded' message from the iframe 550
543 // here? We could hide the page until it arrives, to avoid flicker. 551 /**
544 } 552 * Prepares the New Tab Page by adding listeners, the most visited pages
545 553 * section, and Google-specific elements for a Google-provided page.
546 554 */
547 /** 555 function init() {
548 * Prepares the New Tab Page by adding listeners, the most visited pages 556 // Hide notifications after fade out, so we can't focus on links via
549 * section, and Google-specific elements for a Google-provided page. 557 // keyboard.
550 */ 558 $(IDS.NOTIFICATION).addEventListener('transitionend', hideNotification);
551 function init() { 559
552 // Hide notifications after fade out, so we can't focus on links via keyboard. 560 $(IDS.NOTIFICATION_MESSAGE).textContent =
553 $(IDS.NOTIFICATION).addEventListener('transitionend', hideNotification); 561 configData.translatedStrings.thumbnailRemovedNotification;
554 562
555 $(IDS.NOTIFICATION_MESSAGE).textContent = 563 var undoLink = $(IDS.UNDO_LINK);
556 configData.translatedStrings.thumbnailRemovedNotification; 564 undoLink.addEventListener('click', onUndo);
557 565 registerKeyHandler(undoLink, KEYCODE.ENTER, onUndo);
558 var undoLink = $(IDS.UNDO_LINK); 566 undoLink.textContent = configData.translatedStrings.undoThumbnailRemove;
559 undoLink.addEventListener('click', onUndo); 567
560 registerKeyHandler(undoLink, KEYCODE.ENTER, onUndo); 568 var restoreAllLink = $(IDS.RESTORE_ALL_LINK);
561 undoLink.textContent = configData.translatedStrings.undoThumbnailRemove; 569 restoreAllLink.addEventListener('click', onRestoreAll);
562 570 registerKeyHandler(restoreAllLink, KEYCODE.ENTER, onRestoreAll);
563 var restoreAllLink = $(IDS.RESTORE_ALL_LINK); 571 restoreAllLink.textContent =
564 restoreAllLink.addEventListener('click', onRestoreAll); 572 configData.translatedStrings.restoreThumbnailsShort;
565 registerKeyHandler(restoreAllLink, KEYCODE.ENTER, onRestoreAll); 573
566 restoreAllLink.textContent = 574 $(IDS.ATTRIBUTION_TEXT).textContent =
567 configData.translatedStrings.restoreThumbnailsShort; 575 configData.translatedStrings.attributionIntro;
568 576
569 $(IDS.ATTRIBUTION_TEXT).textContent = 577 $(IDS.NOTIFICATION_CLOSE_BUTTON)
570 configData.translatedStrings.attributionIntro; 578 .addEventListener('click', hideNotification);
571 579
572 $(IDS.NOTIFICATION_CLOSE_BUTTON).addEventListener('click', hideNotification); 580 window.addEventListener('resize', onResize);
573 581 updateContentWidth();
574 window.addEventListener('resize', onResize); 582
575 updateContentWidth(); 583 var embeddedSearchApiHandle = window.chrome.embeddedSearch;
576 584
577 var embeddedSearchApiHandle = window.chrome.embeddedSearch; 585 ntpApiHandle = embeddedSearchApiHandle.newTabPage;
578 586 ntpApiHandle.onthemechange = onThemeChange;
579 ntpApiHandle = embeddedSearchApiHandle.newTabPage; 587 ntpApiHandle.onmostvisitedchange = onMostVisitedChange;
580 ntpApiHandle.onthemechange = onThemeChange; 588
581 ntpApiHandle.onmostvisitedchange = onMostVisitedChange; 589 var searchboxApiHandle = embeddedSearchApiHandle.searchBox;
582 590
583 var searchboxApiHandle = embeddedSearchApiHandle.searchBox; 591 if (configData.isGooglePage) {
584 592 // Set up the fakebox (which only exists on the Google NTP).
585 if (configData.isGooglePage) { 593 ntpApiHandle.oninputstart = onInputStart;
586 // Set up the fakebox (which only exists on the Google NTP). 594 ntpApiHandle.oninputcancel = onInputCancel;
587 ntpApiHandle.oninputstart = onInputStart; 595
588 ntpApiHandle.oninputcancel = onInputCancel; 596 if (ntpApiHandle.isInputInProgress) {
589 597 onInputStart();
590 if (ntpApiHandle.isInputInProgress) { 598 }
591 onInputStart(); 599
592 } 600 $(IDS.FAKEBOX_TEXT).textContent =
593 601 configData.translatedStrings.searchboxPlaceholder;
594 $(IDS.FAKEBOX_TEXT).textContent = 602
595 configData.translatedStrings.searchboxPlaceholder; 603 // Listener for updating the key capture state.
596 604 document.body.onmousedown = function(event) {
597 // Listener for updating the key capture state. 605 if (isFakeboxClick(event))
598 document.body.onmousedown = function(event) { 606 searchboxApiHandle.startCapturingKeyStrokes();
599 if (isFakeboxClick(event)) 607 else if (isFakeboxFocused())
600 searchboxApiHandle.startCapturingKeyStrokes(); 608 searchboxApiHandle.stopCapturingKeyStrokes();
601 else if (isFakeboxFocused()) 609 };
602 searchboxApiHandle.stopCapturingKeyStrokes(); 610 searchboxApiHandle.onkeycapturechange = function() {
611 setFakeboxFocus(searchboxApiHandle.isKeyCaptureEnabled);
612 };
613 var inputbox = $(IDS.FAKEBOX_INPUT);
614 inputbox.onpaste = function(event) {
615 event.preventDefault();
616 // Send pasted text to Omnibox.
617 var text = event.clipboardData.getData('text/plain');
618 if (text)
619 searchboxApiHandle.paste(text);
620 };
621 inputbox.ondrop = function(event) {
622 event.preventDefault();
623 var text = event.dataTransfer.getData('text/plain');
624 if (text) {
625 searchboxApiHandle.paste(text);
626 }
627 setFakeboxDragFocus(false);
628 };
629 inputbox.ondragenter = function() {
630 setFakeboxDragFocus(true);
631 };
632 inputbox.ondragleave = function() {
633 setFakeboxDragFocus(false);
634 };
635
636 // Update the fakebox style to match the current key capturing state.
637 setFakeboxFocus(searchboxApiHandle.isKeyCaptureEnabled);
638
639 // Inject the OneGoogleBar loader script. It'll create a global variable
640 // named "og" with the following fields:
641 // .html - the main bar HTML.
642 // .end_of_body_html - HTML to be inserted at the end of the body.
643 var ogScript = document.createElement('script');
644 ogScript.src = 'chrome-search://local-ntp/one-google.js';
645 document.body.appendChild(ogScript);
646 ogScript.onload = function() {
647 injectOneGoogleBar(og.html, og.end_of_body_html);
648 };
649 } else {
650 document.body.classList.add(CLASSES.NON_GOOGLE_PAGE);
651 }
652
653 if (searchboxApiHandle.rtl) {
654 $(IDS.NOTIFICATION).dir = 'rtl';
655 // Grabbing the root HTML element.
656 document.documentElement.setAttribute('dir', 'rtl');
657 // Add class for setting alignments based on language directionality.
658 document.documentElement.classList.add(CLASSES.RTL);
659 }
660
661 // Collect arguments for the most visited iframe.
662 var args = [];
663
664 if (searchboxApiHandle.rtl)
665 args.push('rtl=1');
666 if (NTP_DESIGN.numTitleLines > 1)
667 args.push('ntl=' + NTP_DESIGN.numTitleLines);
668
669 args.push(
670 'removeTooltip=' +
671 encodeURIComponent(
672 configData.translatedStrings.removeThumbnailTooltip));
673
674 // Create the most visited iframe.
675 var iframe = document.createElement('iframe');
676 iframe.id = IDS.TILES_IFRAME;
677 iframe.tabIndex = 1;
678 iframe.src = 'chrome-search://most-visited/single.html?' + args.join('&');
679 $(IDS.TILES).appendChild(iframe);
680
681 iframe.onload = function() {
682 reloadTiles();
683 renderTheme();
603 }; 684 };
604 searchboxApiHandle.onkeycapturechange = function() { 685
605 setFakeboxFocus(searchboxApiHandle.isKeyCaptureEnabled); 686 window.addEventListener('message', handlePostMessage);
606 }; 687 }
607 var inputbox = $(IDS.FAKEBOX_INPUT); 688
608 inputbox.onpaste = function(event) { 689
609 event.preventDefault(); 690 /**
610 // Send pasted text to Omnibox. 691 * Binds event listeners.
611 var text = event.clipboardData.getData('text/plain'); 692 */
612 if (text) 693 function listen() {
613 searchboxApiHandle.paste(text); 694 document.addEventListener('DOMContentLoaded', init);
614 }; 695 }
615 inputbox.ondrop = function(event) { 696
616 event.preventDefault(); 697
617 var text = event.dataTransfer.getData('text/plain'); 698 /**
618 if (text) { 699 * Injects the One Google Bar into the page. Called asynchronously, so that it
619 searchboxApiHandle.paste(text); 700 * doesn't block the main page load.
620 } 701 */
621 setFakeboxDragFocus(false); 702 function injectOneGoogleBar(barHtml, endOfBodyHtml) {
622 }; 703 var inHeadStyle = document.createElement('link');
623 inputbox.ondragenter = function() { 704 inHeadStyle.rel = 'stylesheet';
624 setFakeboxDragFocus(true); 705 inHeadStyle.href = 'chrome-search://local-ntp/one-google/in-head.css';
625 }; 706 document.head.appendChild(inHeadStyle);
626 inputbox.ondragleave = function() { 707
627 setFakeboxDragFocus(false); 708 inHeadStyle.onload = function() {
628 }; 709 var inHeadScript = document.createElement('script');
629 710 inHeadScript.src = 'chrome-search://local-ntp/one-google/in-head.js';
630 // Update the fakebox style to match the current key capturing state. 711 document.head.appendChild(inHeadScript);
631 setFakeboxFocus(searchboxApiHandle.isKeyCaptureEnabled); 712
632 713 inHeadScript.onload = function() {
633 // Inject the OneGoogleBar loader script. It'll create a global variable 714 var ogElem = $('one-google');
634 // named "og" with the following fields: 715 ogElem.innerHTML = barHtml;
635 // .html - the main bar HTML. 716 ogElem.classList.remove('hidden');
636 // .end_of_body_html - HTML to be inserted at the end of the body. 717
637 var ogScript = document.createElement('script'); 718 var afterBarScript = document.createElement('script');
638 ogScript.src = 'chrome-search://local-ntp/one-google.js'; 719 afterBarScript.src =
639 document.body.appendChild(ogScript); 720 'chrome-search://local-ntp/one-google/after-bar.js';
640 ogScript.onload = function() { 721 ogElem.parentNode.insertBefore(afterBarScript, ogElem.nextSibling);
641 injectOneGoogleBar(og.html, og.end_of_body_html); 722
642 }; 723 afterBarScript.onload = function() {
643 } else { 724 $('one-google-end-of-body').innerHTML = endOfBodyHtml;
644 document.body.classList.add(CLASSES.NON_GOOGLE_PAGE); 725
645 } 726 var endOfBodyScript = document.createElement('script');
646 727 endOfBodyScript.src =
647 if (searchboxApiHandle.rtl) { 728 'chrome-search://local-ntp/one-google/end-of-body.js';
648 $(IDS.NOTIFICATION).dir = 'rtl'; 729 document.body.appendChild(endOfBodyScript);
649 // Grabbing the root HTML element. 730 };
650 document.documentElement.setAttribute('dir', 'rtl');
651 // Add class for setting alignments based on language directionality.
652 document.documentElement.classList.add(CLASSES.RTL);
653 }
654
655 // Collect arguments for the most visited iframe.
656 var args = [];
657
658 if (searchboxApiHandle.rtl)
659 args.push('rtl=1');
660 if (NTP_DESIGN.numTitleLines > 1)
661 args.push('ntl=' + NTP_DESIGN.numTitleLines);
662
663 args.push('removeTooltip=' +
664 encodeURIComponent(configData.translatedStrings.removeThumbnailTooltip));
665
666 // Create the most visited iframe.
667 var iframe = document.createElement('iframe');
668 iframe.id = IDS.TILES_IFRAME;
669 iframe.tabIndex = 1;
670 iframe.src = 'chrome-search://most-visited/single.html?' + args.join('&');
671 $(IDS.TILES).appendChild(iframe);
672
673 iframe.onload = function() {
674 reloadTiles();
675 renderTheme();
676 };
677
678 window.addEventListener('message', handlePostMessage);
679 }
680
681
682 /**
683 * Binds event listeners.
684 */
685 function listen() {
686 document.addEventListener('DOMContentLoaded', init);
687 }
688
689
690 /**
691 * Injects the One Google Bar into the page. Called asynchronously, so that it
692 * doesn't block the main page load.
693 */
694 function injectOneGoogleBar(barHtml, endOfBodyHtml) {
695 var inHeadStyle = document.createElement('link');
696 inHeadStyle.rel = "stylesheet";
697 inHeadStyle.href = 'chrome-search://local-ntp/one-google/in-head.css';
698 document.head.appendChild(inHeadStyle);
699
700 inHeadStyle.onload = function() {
701 var inHeadScript = document.createElement('script');
702 inHeadScript.src = 'chrome-search://local-ntp/one-google/in-head.js';
703 document.head.appendChild(inHeadScript);
704
705 inHeadScript.onload = function() {
706 var ogElem = $('one-google');
707 ogElem.innerHTML = barHtml;
708 ogElem.classList.remove('hidden');
709
710 var afterBarScript = document.createElement('script');
711 afterBarScript.src =
712 'chrome-search://local-ntp/one-google/after-bar.js';
713 ogElem.parentNode.insertBefore(afterBarScript, ogElem.nextSibling);
714
715 afterBarScript.onload = function() {
716 $('one-google-end-of-body').innerHTML = endOfBodyHtml;
717
718 var endOfBodyScript = document.createElement('script');
719 endOfBodyScript.src =
720 'chrome-search://local-ntp/one-google/end-of-body.js';
721 document.body.appendChild(endOfBodyScript);
722 }; 731 };
723 }; 732 };
733 }
734
735
736 return {
737 init: init, // Exposed for testing.
738 listen: listen
724 }; 739 };
725 } 740 }
726 741
727
728 return {
729 init: init, // Exposed for testing.
730 listen: listen
731 };
732
733 }
734
735 if (!window.localNTPUnitTest) { 742 if (!window.localNTPUnitTest) {
736 LocalNTP().listen(); 743 LocalNTP().listen();
737 } 744 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698