OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 cr.define('ntp4', function() { | |
6 'use strict'; | |
7 | |
8 var localStrings = new LocalStrings; | |
9 | |
10 /** | |
11 * A running count of bookmark tiles that we create so that each will have | |
12 * a unique ID. | |
13 */ | |
14 var tileId = 0; | |
15 | |
16 /** | |
17 * The maximum number of tiles that we will display on this page. If there | |
18 * are not enough spaces to show all bookmarks, we'll include a link to the | |
19 * Bookmarks manager. | |
20 * TODO(csilv): Eliminate the need for this restraint. | |
21 * @type {number} | |
22 * @const | |
23 */ | |
24 var MAX_BOOKMARK_TILES = 18; | |
25 | |
26 /** | |
27 * The root node's ID. We need this to determine removable items (direct | |
28 * children aren't removable). | |
29 * @type {number} | |
30 * @const | |
31 */ | |
32 var ROOT_NODE_ID = '0'; | |
33 | |
34 /** | |
35 * The bookmark bar's ID. We need this to allow combining the root and | |
36 * bookmarks bars at the top level of the folder hierarchy/bookmarks page. | |
37 * aren't removable). | |
38 * @type {number} | |
39 * @const | |
40 */ | |
41 var BOOKMARKS_BAR_ID = '1'; | |
42 | |
43 /** | |
44 * Creates a new bookmark object. | |
45 * @param {Object} data The url and title. | |
46 * @constructor | |
47 * @extends {HTMLDivElement} | |
48 */ | |
49 function Bookmark(data) { | |
50 var el = $('bookmark-template').cloneNode(true); | |
51 el.__proto__ = Bookmark.prototype; | |
52 el.initialize(data); | |
53 | |
54 return el; | |
55 } | |
56 | |
57 Bookmark.prototype = { | |
58 __proto__: HTMLDivElement.prototype, | |
59 | |
60 /** | |
61 * Initialize the bookmark object. | |
62 * @param {Object} data The bookmark data (url, title, etc). | |
63 */ | |
64 initialize: function(data) { | |
65 this.data = data; | |
66 this.hidden = false; | |
67 | |
68 var id = tileId++; | |
69 this.id = 'bookmark_tile_' + id; | |
70 | |
71 var title = this.querySelector('.title'); | |
72 title.textContent = this.data.title; | |
73 | |
74 // Sets the tooltip. | |
75 this.title = this.data.title; | |
76 | |
77 if (this.data.url) { | |
78 var button = this.querySelector('.button'); | |
79 button.href = title.href = this.data.url; | |
80 } | |
81 | |
82 var faviconDiv = this.querySelector('.favicon'); | |
83 var faviconUrl; | |
84 if (this.data.url) { | |
85 faviconUrl = 'chrome://favicon/size/16/' + this.data.url; | |
86 chrome.send('getFaviconDominantColor', [faviconUrl, this.id]); | |
87 } else { | |
88 faviconUrl = 'chrome://theme/IDR_BOOKMARK_BAR_FOLDER'; | |
89 // TODO(csilv): Should we vary this color by platform? | |
90 this.stripeColor = '#919191'; | |
91 } | |
92 faviconDiv.style.backgroundImage = url(faviconUrl); | |
93 | |
94 if (this.canBeRemoved()) | |
95 this.classList.add('removable'); | |
96 | |
97 this.addEventListener('click', this.handleClick_.bind(this)); | |
98 }, | |
99 | |
100 /** | |
101 * Sets the color of the favicon dominant color bar. | |
102 * @param {string} color The css-parsable value for the color. | |
103 */ | |
104 set stripeColor(color) { | |
105 this.querySelector('.color-stripe').style.backgroundColor = color; | |
106 }, | |
107 | |
108 /** | |
109 * Set the size and position of the bookmark tile. | |
110 * @param {number} size The total size of |this|. | |
111 * @param {number} x The x-position. | |
112 * @param {number} y The y-position. | |
113 * animate. | |
114 */ | |
115 setBounds: function(size, x, y) { | |
116 this.style.width = size + 'px'; | |
117 this.style.height = heightForWidth(size) + 'px'; | |
118 | |
119 this.style.left = x + 'px'; | |
120 this.style.right = x + 'px'; | |
121 this.style.top = y + 'px'; | |
122 }, | |
123 | |
124 /** | |
125 * Set some data on the drag when it starts. | |
126 * @param {DataTransfer} dataTransfer A data transfer object from the | |
127 * current drag. | |
128 */ | |
129 setDragData: function(dataTransfer) { | |
130 // OS X requires extra data drag data to consider a drag useful, so we're | |
131 // appending some semi-useful data at the end to force drags to work on | |
132 // this OS. Don't use this data for anything -- this is just a hack to | |
133 // ensure drag works the same as on other platforms. | |
134 dataTransfer.setData('Text', this.data.id); | |
135 }, | |
136 | |
137 /** | |
138 * Invoked when a bookmark is clicked | |
139 * @param {Event} e The click event. | |
140 * @private | |
141 */ | |
142 handleClick_: function(e) { | |
143 if (e.target.classList.contains('close-button')) { | |
144 e.preventDefault(); | |
145 this.removeFromChrome(); | |
146 } else if (!this.data.url) { | |
147 chrome.send('getBookmarksData', [this.data.id]); | |
148 e.preventDefault(); | |
149 } | |
150 }, | |
151 | |
152 /** @inheritDoc */ | |
153 removeFromChrome: function() { | |
154 chrome.send('removeBookmark', [this.data.id]); | |
155 }, | |
156 | |
157 /** | |
158 * All bookmarks except for children of the root node. | |
159 * @return {boolean} Whether or not the item can be removed. | |
160 */ | |
161 canBeRemoved: function() { | |
162 return this.data.parentId !== ROOT_NODE_ID; | |
163 }, | |
164 }; | |
165 | |
166 /** | |
167 * Creates a new bookmark title object. | |
168 * @param {Object} data The url and title. | |
169 * @constructor | |
170 * @extends {HTMLDivElement} | |
171 */ | |
172 function BookmarkTitle(data) { | |
173 var el = cr.doc.createElement('div'); | |
174 el.__proto__ = BookmarkTitle.prototype; | |
175 el.initialize(data); | |
176 | |
177 return el; | |
178 } | |
179 | |
180 BookmarkTitle.prototype = { | |
181 __proto__: HTMLDivElement.prototype, | |
182 | |
183 /** | |
184 * Initialize the bookmark title object. | |
185 */ | |
186 initialize: function(data) { | |
187 this.className = 'title-crumb'; | |
188 this.folderId = data.id; | |
189 this.textContent = data.parentId ? data.title : | |
190 localStrings.getString('bookmarksPage'); | |
191 | |
192 this.addEventListener('click', this.handleClick_); | |
193 }, | |
194 | |
195 /** | |
196 * Invoked when a bookmark title is clicked | |
197 * @param {Event} e The click event. | |
198 * @private | |
199 */ | |
200 handleClick_: function(e) { | |
201 chrome.send('getBookmarksData', [this.folderId]); | |
202 }, | |
203 }; | |
204 | |
205 var TilePage = ntp4.TilePage; | |
206 | |
207 var bookmarksPageGridValues = { | |
208 // The fewest tiles we will show in a row. | |
209 minColCount: 3, | |
210 // The most tiles we will show in a row. | |
211 maxColCount: 6, | |
212 | |
213 // The smallest a tile can be. | |
214 minTileWidth: 64, | |
215 // The biggest a tile can be. | |
216 maxTileWidth: 96, | |
217 | |
218 // The padding between tiles, as a fraction of the tile width. | |
219 tileSpacingFraction: 1 / 2, | |
220 }; | |
221 TilePage.initGridValues(bookmarksPageGridValues); | |
222 | |
223 /** | |
224 * Calculates the height for a bookmarks tile for a given width. The size | |
225 * is based on a desired size of 96x72 ratio. | |
226 * @return {number} The height. | |
227 */ | |
228 function heightForWidth(width) { | |
229 // The 2s are for borders, the 31 is for the title. | |
230 return (width - 2) * 72 / 96 + 2 + 31; | |
231 } | |
232 | |
233 /** | |
234 * Creates a new BookmarksPage object. | |
235 * @constructor | |
236 * @extends {TilePage} | |
237 */ | |
238 function BookmarksPage() { | |
239 var el = new TilePage(bookmarksPageGridValues); | |
240 el.__proto__ = BookmarksPage.prototype; | |
241 el.initialize(); | |
242 | |
243 return el; | |
244 } | |
245 | |
246 BookmarksPage.prototype = { | |
247 __proto__: TilePage.prototype, | |
248 | |
249 /** | |
250 * Initialize the bookmarks page object. | |
251 */ | |
252 initialize: function() { | |
253 this.classList.add('bookmarks-page'); | |
254 | |
255 // Insert the bookmark titles header which is unique to bookmark pages. | |
256 var titleWrapper = $('bookmarks-title-wrapper') | |
257 titleWrapper.hidden = false; | |
258 this.insertBefore(titleWrapper, this.firstChild); | |
259 | |
260 // Insert the top & bottom links for the Bookmarks Manager page. | |
261 var pageContent = this.querySelector('.tile-page-content'); | |
262 var topWrapper = $('bookmarks-top-link-wrapper'); | |
263 pageContent.insertBefore(topWrapper, pageContent.firstChild); | |
264 topWrapper.hidden = false; | |
265 pageContent.appendChild($('bookmarks-bottom-link-wrapper')); | |
266 }, | |
267 | |
268 /** | |
269 * Build the bookmark titles bar (ie, navigation hiearchy). | |
270 * @param {Array} items The parent hiearchy of the current folder. | |
271 * @private | |
272 */ | |
273 updateBookmarkTitles_: function(items) { | |
274 var wrapper = $('bookmarks-title-wrapper'); | |
275 var title = wrapper.querySelector('.section-title'); | |
276 title.innerHTML = ''; | |
277 | |
278 for (var i = items.length - 1; i > 0; i--) { | |
279 title.appendChild(new BookmarkTitle(items[i])); | |
280 | |
281 var separator = document.createElement('hr'); | |
282 separator.className = 'bookmark-separator'; | |
283 title.appendChild(separator); | |
284 } | |
285 | |
286 var titleCrumb = new BookmarkTitle(items[0]); | |
287 titleCrumb.classList.add('title-crumb-active'); | |
288 title.appendChild(titleCrumb); | |
289 }, | |
290 | |
291 /** | |
292 * Build the bookmark tiles. | |
293 * @param {Array} items The contents of the current folder. | |
294 * @private | |
295 */ | |
296 updateBookmarkTiles_: function(items) { | |
297 this.removeAllTiles(); | |
298 var tile_count = Math.min(items.length, MAX_BOOKMARK_TILES); | |
299 for (var i = 0; i < tile_count; i++) | |
300 this.appendTile(new Bookmark(items[i]), false); | |
301 | |
302 var folder_id = this.id == ROOT_NODE_ID ? BOOKMARKS_BAR_ID : this.id; | |
303 var top_link = $('bookmarks-top-link-wrapper').querySelector('a'); | |
304 top_link.href = 'chrome://bookmarks/#' + folder_id; | |
305 | |
306 var wrapper = $('bookmarks-bottom-link-wrapper'); | |
307 if (items.length > MAX_BOOKMARK_TILES) { | |
308 var bottom_link = wrapper.querySelector('a'); | |
309 bottom_link.href = 'chrome://bookmarks/#' + folder_id; | |
310 wrapper.hidden = false; | |
311 } else { | |
312 wrapper.hidden = true; | |
313 } | |
314 | |
315 if (this.id === ROOT_NODE_ID && !tile_count && !cr.isChromeOS) | |
316 this.showImportPromo_(); | |
317 }, | |
318 | |
319 /** | |
320 * Determine whether a bookmark ID matches a folder in the current | |
321 * hierarchy. | |
322 * @param {string} id The bookmark ID to search for. | |
323 * @private | |
324 */ | |
325 isBookmarkInParentHierarchy_: function(id) { | |
326 var titlesWrapper = $('bookmarks-title-wrapper'); | |
327 var titles = titlesWrapper.querySelectorAll('.title-crumb'); | |
328 for (var i = 0; i < titles.length; i++) { | |
329 var bookmarkTitle = titles[i]; | |
330 if (bookmarkTitle.folderId == id) | |
331 return true; | |
332 } | |
333 return false; | |
334 }, | |
335 | |
336 /** | |
337 * Tells if we're in currently in the given folder. | |
338 * @param {String} id The folder node's ID. | |
339 * @returns {boolean} If it's in that folder (visually). | |
340 */ | |
341 currentlyInFolder_: function(id) { | |
342 return id === this.id || (this.id === ROOT_NODE_ID && | |
343 id === BOOKMARKS_BAR_ID); | |
344 }, | |
345 | |
346 /** @inheritDoc */ | |
347 shouldAcceptDrag: function(e) { | |
348 var tile = ntp4.getCurrentlyDraggingTile(); | |
349 if (tile) | |
350 return !!tile.querySelector('.most-visited, .bookmark'); | |
351 // If there was no dragging tile, look for a URL in the drag data. | |
352 return e.dataTransfer && e.dataTransfer.types && | |
353 e.dataTransfer.types.indexOf('url') != -1; | |
354 }, | |
355 | |
356 /** @inheritDoc */ | |
357 heightForWidth: heightForWidth, | |
358 | |
359 /** | |
360 * Invoked before a batch import begins. We will ignore added/changed | |
361 * notifications while the operation is in progress. | |
362 */ | |
363 bookmarkImportBegan: function() { | |
364 this.importing = true; | |
365 }, | |
366 | |
367 /** | |
368 * Invoked after a batch import finishes. We will reload the bookmarks | |
369 * page to reflect the new state. | |
370 */ | |
371 bookmarkImportEnded: function() { | |
372 this.importing = false; | |
373 chrome.send('getBookmarksData', []); | |
374 }, | |
375 | |
376 /** | |
377 * Invoked when a node has been added. | |
378 * @param {string} id The id of the newly created bookmark node. | |
379 * @param {Object} bookmark The new bookmark node. | |
380 * @param {boolean} fromCurrentPage True if the action was from this page. | |
381 */ | |
382 bookmarkNodeAdded: function(id, bookmark, fromCurrentPage) { | |
383 if (this.importing) return; | |
384 if (this.currentlyInFolder_(bookmark.parentId)) { | |
385 // Hide the import promo if it exists. | |
386 this.hideImportPromo_(); | |
387 // Only add the item if it should be visible. | |
388 if (bookmark.index < MAX_BOOKMARK_TILES) { | |
389 // If source of the add came from this page, show an animated | |
390 // insertion, otherwise just quietly do it. | |
391 this.addTileAt(new Bookmark(bookmark), bookmark.index, | |
392 fromCurrentPage); | |
393 // Delete extra tiles if they exist. | |
394 while (this.tiles.length > MAX_BOOKMARK_TILES) { | |
395 var tile = this.tiles[this.tiles.length - 1]; | |
396 this.removeTile(tile, false); | |
397 } | |
398 this.repositionTiles_(); | |
399 } | |
400 } | |
401 }, | |
402 | |
403 /** | |
404 * Invoked when the title or url of a node changes. | |
405 * @param {string} id The id of the changed node. | |
406 * @param {Object} changeInfo Details of the changed node. | |
407 */ | |
408 bookmarkNodeChanged: function(id, changeInfo) { | |
409 if (this.importing) return; | |
410 | |
411 // If the current folder or parent is being re-named, reload the page. | |
412 // TODO(csilv): Optimize this to reload just the titles. | |
413 if (this.isBookmarkInParentHierarchy_(id)) { | |
414 chrome.send('getBookmarksData', [this.id]); | |
415 return; | |
416 } | |
417 | |
418 // If the target item is contained in this folder, update just that item. | |
419 for (var i = 0; i < this.tiles.length; i++) { | |
420 var tile = this.tiles[i]; | |
421 var data = tile.firstChild.data; | |
422 | |
423 if (data.id == id) { | |
424 data.title = changeInfo.title; | |
425 var title = tile.querySelector('.title'); | |
426 title.textContent = data.title; | |
427 | |
428 if (changeInfo.url) { | |
429 data.url = changeInfo.url; | |
430 var button = tile.querySelector('.button'); | |
431 button.href = title.href = data.url; | |
432 } | |
433 break; | |
434 } | |
435 } | |
436 }, | |
437 | |
438 /** | |
439 * Invoked when the children (just direct children, not descendants) of | |
440 * a folder have been reordered in some way, such as sorted. | |
441 * @param {string} id The id of the reordered bookmark node. | |
442 * @param {!Object} reorderInfo Details of the reordered bookmark node. | |
443 */ | |
444 bookmarkNodeChildrenReordered: function(id, reorderInfo) { | |
445 if (this.currentlyInFolder_(id)) | |
446 chrome.send('getBookmarksData', [this.id]); | |
447 }, | |
448 | |
449 /** | |
450 * Invoked when a node has moved. | |
451 * @param {string} id The id of the moved bookmark node. | |
452 * @param {!Object} moveInfo Details of the moved bookmark. | |
453 */ | |
454 bookmarkNodeMoved: function(id, moveInfo) { | |
455 // TODO(csilv): Optimize this by doing less than reloading the folder. | |
456 // Reload the current page if the target item is the current folder | |
457 // or a parent of the current folder. | |
458 if (this.isBookmarkInParentHierarchy_(id)) { | |
459 chrome.send('getBookmarksData', [this.id]); | |
460 return; | |
461 } | |
462 | |
463 // Reload the current page if the target item is being moved to/from the | |
464 // current folder. | |
465 if (this.currentlyInFolder_(moveInfo.parentId) || | |
466 this.currentlyInFolder_(moveInfo.oldParentId)) { | |
467 chrome.send('getBookmarksData', [this.id]); | |
468 } | |
469 }, | |
470 | |
471 /** | |
472 * Invoked when a node has been removed from a folder. | |
473 * @param {string} id The id of the removed bookmark node. | |
474 * @param {!Object} removeInfo Details of the removed bookmark node. | |
475 * @param {boolearn} fromCurrentPage If the event was from this page. | |
476 */ | |
477 bookmarkNodeRemoved: function(id, removeInfo, fromCurrentPage) { | |
478 // If the target item is the visibile folder or a parent folder, load | |
479 // the parent of the removed item. | |
480 if (this.isBookmarkInParentHierarchy_(id)) { | |
481 chrome.send('getBookmarksData', [removeInfo.parentId]); | |
482 return; | |
483 } | |
484 | |
485 // If the target item is contained in the visible folder, find the | |
486 // matching tile and delete it. | |
487 if (this.currentlyInFolder_(removeInfo.parentId)) { | |
488 for (var i = 0; i < this.tiles.length; i++) { | |
489 var tile = this.tiles[i]; | |
490 if (tile.firstChild.data.id == id) { | |
491 this.removeTile(tile, fromCurrentPage); | |
492 break; | |
493 } | |
494 } | |
495 } | |
496 }, | |
497 | |
498 /** | |
499 * Set the bookmark data that should be displayed, replacing any existing | |
500 * data. | |
501 * @param {Object} data Data that shoudl be displayed. Contains arrays | |
502 * 'items' and 'navigationItems'. | |
503 */ | |
504 set data(data) { | |
505 this.id = data.navigationItems[0].id; | |
506 this.updateBookmarkTiles_(data.items); | |
507 this.updateBookmarkTitles_(data.navigationItems); | |
508 }, | |
509 | |
510 /** @inheritDoc */ | |
511 get extraBottomPadding() { | |
512 return 40; | |
513 }, | |
514 | |
515 /** @inheritDoc */ | |
516 setDropEffect: function(dataTransfer) { | |
517 var tile = ntp4.getCurrentlyDraggingTile(); | |
518 if (tile && tile.querySelector('.bookmark')) | |
519 ntp4.setCurrentDropEffect(dataTransfer, 'move'); | |
520 else | |
521 ntp4.setCurrentDropEffect(dataTransfer, 'copy'); | |
522 }, | |
523 | |
524 /** @inheritDoc */ | |
525 addDragData: function(dataTransfer, index) { | |
526 var parentId = ROOT_NODE_ID == this.id ? BOOKMARKS_BAR_ID : this.id; | |
527 var currentlyDraggingTile = ntp4.getCurrentlyDraggingTile(); | |
528 if (currentlyDraggingTile) { | |
529 var tileContents = currentlyDraggingTile.firstChild; | |
530 if (tileContents.classList.contains('most-visited')) { | |
531 this.generateBookmarkForLink(parentId, index, | |
532 tileContents.textContent, | |
533 tileContents.href); | |
534 } | |
535 } else { | |
536 this.addOutsideData_(dataTransfer, parentId, index); | |
537 } | |
538 }, | |
539 | |
540 /** | |
541 * If we got an outside drag from a page or the URL bar. | |
542 * @param {!DataTransfer} dataTransfer The drag data transfer object. | |
543 * @param {number} parentId The parent ID for the current bookmark level. | |
544 * @param {number} index Position it will be inserted relative to siblings. | |
545 */ | |
546 addOutsideData_: function(dataTransfer, parentId, index) { | |
547 var url = dataTransfer.getData('url'); | |
548 assert(url && url !== window.location.href); | |
549 // Look for any text/html types first (links, etc.). | |
550 var title, html = dataTransfer.getData('text/html'); | |
551 if (html) { | |
552 // NOTE: Don't insert this into the DOM! It could XSS the page! | |
553 var node = this.ownerDocument.createElement('div'); | |
554 node.innerHTML = html; | |
555 var text = node.textContent, img = node.firstChild; | |
556 // OS X prepends a <meta> tag to the html for some reason... | |
557 if ('META' == img.nodeName) | |
558 img = img.nextSibling; | |
559 // Use the combined text (if it exists), otherwise fall back to an | |
560 // image's alternate text (if they dragged an image onto this page). | |
561 title = text || ('IMG' == img.nodeName && img.alt); | |
562 } | |
563 // If we *still* don't have a title, just use the URL. | |
564 if (!title) | |
565 title = url; | |
566 // Create a bookmark for the dropped data. | |
567 this.generateBookmarkForLink(parentId, index, title, url); | |
568 }, | |
569 | |
570 /** | |
571 * Show the 'Import bookmarks' promo. | |
572 * @private | |
573 */ | |
574 showImportPromo_: function() { | |
575 var importTemplate = $('bookmarks-import-data-link-template'); | |
576 var importWrapper = importTemplate.cloneNode(true); | |
577 importWrapper.id = ''; | |
578 importWrapper.hidden = false; | |
579 this.querySelector('.tile-page-content').appendChild(importWrapper); | |
580 }, | |
581 | |
582 /** | |
583 * Hide the 'Import bookmarks' promo. | |
584 * @private | |
585 */ | |
586 hideImportPromo_: function() { | |
587 var wrapper = this.querySelector('.bookmarks-import-data-link-wrapper'); | |
588 if (wrapper) | |
589 wrapper.parentNode.removeChild(wrapper); | |
590 }, | |
591 | |
592 /** | |
593 * Create a bookmark from a title/url. | |
594 * @param {!string} parentId Stringified int64 of the parent node's ID. | |
595 * @param {number} index Sibling relative index, i.e. 3rd on this level. | |
596 * @param {string} title More human readable title of the bookmark. | |
597 * @param {string} url URL of the bookmark to be created. | |
598 */ | |
599 generateBookmarkForLink: function(parentId, index, title, url) { | |
600 // Bookmark creation actually only *requires* a parent ID, as if we just | |
601 // pass a parent ID it's created as a folder with blank title as a child | |
602 // of that parent. | |
603 assert(parentId); | |
604 chrome.send('createBookmark', [parentId, index, title, url]); | |
605 }, | |
606 | |
607 /** @inheritDoc */ | |
608 tileMoved: function(tile, prevIndex) { | |
609 var parentId = ROOT_NODE_ID == this.id ? BOOKMARKS_BAR_ID : this.id; | |
610 chrome.send('moveBookmark', [tile.firstChild.data.id, parentId, | |
611 tile.index + (prevIndex < tile.index)]); | |
612 }, | |
613 }; | |
614 | |
615 /** | |
616 * Initializes and renders the bookmark chevron canvas. This needs to be | |
617 * performed after the page has been loaded so that we have access to the | |
618 * style sheet values. | |
619 */ | |
620 function initBookmarkChevron() { | |
621 var wrapperStyle = window.getComputedStyle($('bookmarks-title-wrapper')); | |
622 var width = 10; | |
623 var height = parseInt(wrapperStyle.height, 10); | |
624 var ctx = document.getCSSCanvasContext('2d', 'bookmark-chevron', | |
625 width, height); | |
626 ctx.strokeStyle = wrapperStyle.borderBottomColor; | |
627 ctx.beginPath(); | |
628 ctx.moveTo(0, 0); | |
629 ctx.lineTo(width, height / 2); | |
630 ctx.lineTo(0, height); | |
631 ctx.stroke(); | |
632 } | |
633 | |
634 /** | |
635 * Set the dominant color for a bookmark tile. This is the callback method | |
636 * from a request made when the tile was created. | |
637 * @param {string} id The ID of the bookmark tile. | |
638 * @param {string} color The color represented as a CSS string. | |
639 */ | |
640 function setBookmarksFaviconDominantColor(id, color) { | |
641 var tile = $(id); | |
642 if (tile) | |
643 tile.stripeColor = color; | |
644 } | |
645 | |
646 return { | |
647 BookmarksPage: BookmarksPage, | |
648 initBookmarkChevron: initBookmarkChevron, | |
649 }; | |
650 }); | |
651 | |
652 window.addEventListener('load', ntp4.initBookmarkChevron); | |
OLD | NEW |