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 // Dependencies that we should remove/formalize: | |
6 // util.js | |
7 // | |
8 // afterTransition | |
9 // chrome.send | |
10 // hideNotification | |
11 // isRtl | |
12 // localStrings | |
13 // logEvent | |
14 // showNotification | |
15 | |
16 | |
17 var MostVisited = (function() { | |
18 | |
19 function addPinnedUrl(item, index) { | |
20 chrome.send('addPinnedURL', [item.url, item.title, item.faviconUrl || '', | |
21 item.thumbnailUrl || '', String(index)]); | |
22 } | |
23 | |
24 function getItem(el) { | |
25 return findAncestorByClass(el, 'thumbnail-container'); | |
26 } | |
27 | |
28 function updatePinnedDom(el, pinned) { | |
29 el.querySelector('.pin').title = localStrings.getString(pinned ? | |
30 'unpinthumbnailtooltip' : 'pinthumbnailtooltip'); | |
31 if (pinned) { | |
32 el.classList.add('pinned'); | |
33 } else { | |
34 el.classList.remove('pinned'); | |
35 } | |
36 } | |
37 | |
38 function getThumbnailIndex(el) { | |
39 var nodes = el.parentNode.querySelectorAll('.thumbnail-container'); | |
40 return Array.prototype.indexOf.call(nodes, el); | |
41 } | |
42 | |
43 function MostVisited(el, miniview, menu, useSmallGrid, visible) { | |
44 this.element = el; | |
45 this.miniview = miniview; | |
46 this.menu = menu; | |
47 this.useSmallGrid_ = useSmallGrid; | |
48 this.visible_ = visible; | |
49 | |
50 this.createThumbnails_(); | |
51 this.applyMostVisitedRects_(); | |
52 | |
53 el.addEventListener('click', this.handleClick_.bind(this)); | |
54 el.addEventListener('keydown', this.handleKeyDown_.bind(this)); | |
55 | |
56 document.addEventListener('DOMContentLoaded', | |
57 this.ensureSmallGridCorrect.bind(this)); | |
58 | |
59 // Commands | |
60 document.addEventListener('command', this.handleCommand_.bind(this)); | |
61 document.addEventListener('canExecute', this.handleCanExecute_.bind(this)); | |
62 | |
63 // DND | |
64 el.addEventListener('dragstart', this.handleDragStart_.bind(this)); | |
65 el.addEventListener('dragenter', this.handleDragEnter_.bind(this)); | |
66 el.addEventListener('dragover', this.handleDragOver_.bind(this)); | |
67 el.addEventListener('dragleave', this.handleDragLeave_.bind(this)); | |
68 el.addEventListener('drop', this.handleDrop_.bind(this)); | |
69 el.addEventListener('dragend', this.handleDragEnd_.bind(this)); | |
70 el.addEventListener('drag', this.handleDrag_.bind(this)); | |
71 el.addEventListener('mousedown', this.handleMouseDown_.bind(this)); | |
72 } | |
73 | |
74 MostVisited.prototype = { | |
75 togglePinned_: function(el) { | |
76 var index = getThumbnailIndex(el); | |
77 var item = this.data[index]; | |
78 item.pinned = !item.pinned; | |
79 if (item.pinned) { | |
80 addPinnedUrl(item, index); | |
81 } else { | |
82 chrome.send('removePinnedURL', [item.url]); | |
83 } | |
84 updatePinnedDom(el, item.pinned); | |
85 }, | |
86 | |
87 swapPosition_: function(source, destination) { | |
88 var nodes = source.parentNode.querySelectorAll('.thumbnail-container'); | |
89 var sourceIndex = getThumbnailIndex(source); | |
90 var destinationIndex = getThumbnailIndex(destination); | |
91 swapDomNodes(source, destination); | |
92 | |
93 var sourceData = this.data[sourceIndex]; | |
94 addPinnedUrl(sourceData, destinationIndex); | |
95 sourceData.pinned = true; | |
96 updatePinnedDom(source, true); | |
97 | |
98 var destinationData = this.data[destinationIndex]; | |
99 // Only update the destination if it was pinned before. | |
100 if (destinationData.pinned) { | |
101 addPinnedUrl(destinationData, sourceIndex); | |
102 } | |
103 this.data[destinationIndex] = sourceData; | |
104 this.data[sourceIndex] = destinationData; | |
105 | |
106 chrome.send('recordAction', ['MostVisitedReordered']); | |
107 }, | |
108 | |
109 updateSettingsLink: function(hasBlacklistedUrls) { | |
110 if (hasBlacklistedUrls) | |
111 $('most-visited-settings').classList.add('has-blacklist'); | |
112 else | |
113 $('most-visited-settings').classList.remove('has-blacklist'); | |
114 }, | |
115 | |
116 blacklist: function(el) { | |
117 var self = this; | |
118 var url = el.href; | |
119 chrome.send('blacklistURLFromMostVisited', [url]); | |
120 | |
121 el.classList.add('hide'); | |
122 | |
123 // Find the old item. | |
124 var oldUrls = {}; | |
125 var oldIndex = -1; | |
126 var oldItem; | |
127 var data = this.data; | |
128 for (var i = 0; i < data.length; i++) { | |
129 if (data[i].url == url) { | |
130 oldItem = data[i]; | |
131 oldIndex = i; | |
132 } | |
133 oldUrls[data[i].url] = true; | |
134 } | |
135 | |
136 // Send 'getMostVisitedPages' with a callback since we want to find the | |
137 // new page and add that in the place of the removed page. | |
138 chromeSend('getMostVisited', [], 'setMostVisitedPages', | |
139 function(data, hasBlacklistedUrls) { | |
140 // Update settings link. | |
141 self.updateSettingsLink(hasBlacklistedUrls); | |
142 | |
143 // Find new item. | |
144 var newItem; | |
145 for (var i = 0; i < data.length; i++) { | |
146 if (!(data[i].url in oldUrls)) { | |
147 newItem = data[i]; | |
148 break; | |
149 } | |
150 } | |
151 | |
152 if (!newItem) { | |
153 // If no other page is available to replace the blacklisted item, | |
154 // we need to reorder items s.t. all filler items are in the rightmost | |
155 // indices. | |
156 self.data = data; | |
157 | |
158 // Replace old item with new item in the most visited data array. | |
159 } else if (oldIndex != -1) { | |
160 var oldData = self.data.concat(); | |
161 oldData.splice(oldIndex, 1, newItem); | |
162 self.data = oldData; | |
163 el.classList.add('fade-in'); | |
164 } | |
165 | |
166 // We wrap the title in a <span class=blacklisted-title>. We pass an | |
167 // empty string to the notifier function and use DOM to insert the real | |
168 // string. | |
169 var actionText = localStrings.getString('undothumbnailremove'); | |
170 | |
171 // Show notification and add undo callback function. | |
172 var wasPinned = oldItem.pinned; | |
173 showNotification('', actionText, function() { | |
174 self.removeFromBlackList(url); | |
175 if (wasPinned) { | |
176 addPinnedUrl(oldItem, oldIndex); | |
177 } | |
178 chrome.send('getMostVisited'); | |
179 }); | |
180 | |
181 // Now change the DOM. | |
182 var removeText = localStrings.getString('thumbnailremovednotification'); | |
183 var notifyMessageEl = document.querySelector('#notification > *'); | |
184 notifyMessageEl.textContent = removeText; | |
185 | |
186 // Focus the undo link. | |
187 var undoLink = document.querySelector( | |
188 '#notification > .link > [tabindex]'); | |
189 undoLink.focus(); | |
190 }); | |
191 }, | |
192 | |
193 removeFromBlackList: function(url) { | |
194 chrome.send('removeURLsFromMostVisitedBlacklist', [url]); | |
195 }, | |
196 | |
197 clearAllBlacklisted: function() { | |
198 chrome.send('clearMostVisitedURLsBlacklist', []); | |
199 hideNotification(); | |
200 }, | |
201 | |
202 dirty_: false, | |
203 invalidate_: function() { | |
204 this.dirty_ = true; | |
205 }, | |
206 | |
207 visible_: true, | |
208 get visible() { | |
209 return this.visible_; | |
210 }, | |
211 set visible(visible) { | |
212 if (this.visible_ != visible) { | |
213 this.visible_ = visible; | |
214 this.invalidate_(); | |
215 } | |
216 }, | |
217 | |
218 useSmallGrid_: false, | |
219 get useSmallGrid() { | |
220 return this.useSmallGrid_; | |
221 }, | |
222 set useSmallGrid(b) { | |
223 if (this.useSmallGrid_ != b) { | |
224 this.useSmallGrid_ = b; | |
225 this.invalidate_(); | |
226 } | |
227 }, | |
228 | |
229 layout: function() { | |
230 if (!this.dirty_) | |
231 return; | |
232 var d0 = Date.now(); | |
233 this.applyMostVisitedRects_(); | |
234 this.dirty_ = false; | |
235 logEvent('mostVisited.layout: ' + (Date.now() - d0)); | |
236 }, | |
237 | |
238 createThumbnails_: function() { | |
239 var singleHtml = | |
240 '<a class="thumbnail-container filler" tabindex="1">' + | |
241 '<div class="edit-mode-border">' + | |
242 '<div class="edit-bar">' + | |
243 '<div class="pin"></div>' + | |
244 '<div class="spacer"></div>' + | |
245 '<div class="remove"></div>' + | |
246 '</div>' + | |
247 '<span class="thumbnail-wrapper">' + | |
248 '<span class="thumbnail"></span>' + | |
249 '</span>' + | |
250 '</div>' + | |
251 '<div class="title">' + | |
252 '<div></div>' + | |
253 '</div>' + | |
254 '</a>'; | |
255 this.element.innerHTML = Array(8 + 1).join(singleHtml); | |
256 var children = this.element.children; | |
257 for (var i = 0; i < 8; i++) { | |
258 children[i].id = 't' + i; | |
259 children[i].onmouseover = this.handleMouseOver_.bind(this); | |
260 children[i].onmouseout = this.handleMouseOut_.bind(this); | |
261 } | |
262 }, | |
263 | |
264 getMostVisitedLayoutRects_: function() { | |
265 var small = this.useSmallGrid; | |
266 | |
267 var cols = 4; | |
268 var rows = 2; | |
269 var marginWidth = 10; | |
270 var marginHeight = 7; | |
271 var borderWidth = 4; | |
272 var thumbWidth = small ? 150 : 207; | |
273 var thumbHeight = small ? 93 : 129; | |
274 var w = thumbWidth + 2 * borderWidth + 2 * marginWidth; | |
275 var h = thumbHeight + 40 + 2 * marginHeight; | |
276 var sumWidth = cols * w - 2 * marginWidth; | |
277 var topSpacing = 10; | |
278 | |
279 var rtl = isRtl(); | |
280 var rects = []; | |
281 | |
282 if (this.visible) { | |
283 for (var i = 0; i < rows * cols; i++) { | |
284 var row = Math.floor(i / cols); | |
285 var col = i % cols; | |
286 var left = rtl ? sumWidth - col * w - thumbWidth - 2 * borderWidth : | |
287 col * w; | |
288 | |
289 var top = row * h + topSpacing; | |
290 | |
291 rects[i] = {left: left, top: top}; | |
292 } | |
293 } | |
294 return rects; | |
295 }, | |
296 | |
297 applyMostVisitedRects_: function() { | |
298 if (this.visible) { | |
299 var rects = this.getMostVisitedLayoutRects_(); | |
300 var children = this.element.children; | |
301 for (var i = 0; i < 8; i++) { | |
302 var t = children[i]; | |
303 t.style.left = rects[i].left + 'px'; | |
304 t.style.top = rects[i].top + 'px'; | |
305 t.style.right = ''; | |
306 var innerStyle = t.firstElementChild.style; | |
307 innerStyle.left = innerStyle.top = ''; | |
308 } | |
309 } | |
310 }, | |
311 | |
312 // Work around for http://crbug.com/25329 | |
313 ensureSmallGridCorrect: function(expected) { | |
314 if (expected != this.useSmallGrid) | |
315 this.applyMostVisitedRects_(); | |
316 }, | |
317 | |
318 getRectByIndex_: function(index) { | |
319 return this.getMostVisitedLayoutRects_()[index]; | |
320 }, | |
321 | |
322 // Commands | |
323 | |
324 handleCommand_: function(e) { | |
325 var commandId = e.command.id; | |
326 switch (commandId) { | |
327 case 'clear-all-blacklisted': | |
328 this.clearAllBlacklisted(); | |
329 chrome.send('getMostVisited'); | |
330 break; | |
331 } | |
332 }, | |
333 | |
334 handleCanExecute_: function(e) { | |
335 if (e.command.id == 'clear-all-blacklisted') | |
336 e.canExecute = true; | |
337 }, | |
338 | |
339 // DND | |
340 | |
341 currentOverItem_: null, | |
342 get currentOverItem() { | |
343 return this.currentOverItem_; | |
344 }, | |
345 set currentOverItem(item) { | |
346 var style; | |
347 if (item != this.currentOverItem_) { | |
348 if (this.currentOverItem_) { | |
349 style = this.currentOverItem_.firstElementChild.style; | |
350 style.left = style.top = ''; | |
351 } | |
352 this.currentOverItem_ = item; | |
353 | |
354 if (item) { | |
355 // Make the drag over item move 15px towards the source. The movement | |
356 // is done by only moving the edit-mode-border (as in the mocks) and | |
357 // it is done with relative positioning so that the movement does not | |
358 // change the drop target. | |
359 var dragIndex = getThumbnailIndex(this.dragItem_); | |
360 var overIndex = getThumbnailIndex(item); | |
361 if (dragIndex == -1 || overIndex == -1) { | |
362 return; | |
363 } | |
364 | |
365 var dragRect = this.getRectByIndex_(dragIndex); | |
366 var overRect = this.getRectByIndex_(overIndex); | |
367 | |
368 var x = dragRect.left - overRect.left; | |
369 var y = dragRect.top - overRect.top; | |
370 var z = Math.sqrt(x * x + y * y); | |
371 var z2 = 15; | |
372 var x2 = x * z2 / z; | |
373 var y2 = y * z2 / z; | |
374 | |
375 style = this.currentOverItem_.firstElementChild.style; | |
376 style.left = x2 + 'px'; | |
377 style.top = y2 + 'px'; | |
378 } | |
379 } | |
380 }, | |
381 dragItem_: null, | |
382 startX_: 0, | |
383 startY_: 0, | |
384 startScreenX_: 0, | |
385 startScreenY_: 0, | |
386 dragEndTimer_: null, | |
387 hoverStartTime_: null, | |
388 | |
389 isDragging: function() { | |
390 return !!this.dragItem_; | |
391 }, | |
392 | |
393 handleDragStart_: function(e) { | |
394 // For the purpose of recording histograms, treat this as the end of | |
395 // hovering over the thumbnail. | |
396 this.RecordHoverTime_(false); | |
397 | |
398 var thumbnail = getItem(e.target); | |
399 if (thumbnail) { | |
400 // Don't set data since HTML5 does not allow setting the name for | |
401 // url-list. Instead, we just rely on the dragging of link behavior. | |
402 this.dragItem_ = thumbnail; | |
403 this.dragItem_.classList.add('dragging'); | |
404 this.dragItem_.style.zIndex = 2; | |
405 e.dataTransfer.effectAllowed = 'copyLinkMove'; | |
406 } | |
407 }, | |
408 | |
409 handleDragEnter_: function(e) { | |
410 if (this.canDropOnElement_(this.currentOverItem)) { | |
411 e.preventDefault(); | |
412 } | |
413 }, | |
414 | |
415 handleDragOver_: function(e) { | |
416 var item = getItem(e.target); | |
417 this.currentOverItem = item; | |
418 if (this.canDropOnElement_(item)) { | |
419 e.preventDefault(); | |
420 e.dataTransfer.dropEffect = 'move'; | |
421 } | |
422 }, | |
423 | |
424 handleDragLeave_: function(e) { | |
425 var item = getItem(e.target); | |
426 if (item) { | |
427 e.preventDefault(); | |
428 } | |
429 | |
430 this.currentOverItem = null; | |
431 }, | |
432 | |
433 handleDrop_: function(e) { | |
434 var dropTarget = getItem(e.target); | |
435 if (this.canDropOnElement_(dropTarget)) { | |
436 dropTarget.style.zIndex = 1; | |
437 this.swapPosition_(this.dragItem_, dropTarget); | |
438 // The timeout below is to allow WebKit to see that we turned off | |
439 // pointer-event before moving the thumbnails so that we can get out of | |
440 // hover mode. | |
441 window.setTimeout((function() { | |
442 this.invalidate_(); | |
443 this.layout(); | |
444 }).bind(this), 10); | |
445 e.preventDefault(); | |
446 if (this.dragEndTimer_) { | |
447 window.clearTimeout(this.dragEndTimer_); | |
448 this.dragEndTimer_ = null; | |
449 } | |
450 afterTransition(function() { | |
451 dropTarget.style.zIndex = ''; | |
452 }); | |
453 } | |
454 }, | |
455 | |
456 handleDragEnd_: function(e) { | |
457 var dragItem = this.dragItem_; | |
458 if (dragItem) { | |
459 dragItem.style.pointerEvents = ''; | |
460 dragItem.classList.remove('dragging'); | |
461 | |
462 afterTransition(function() { | |
463 // Delay resetting zIndex to let the animation finish. | |
464 dragItem.style.zIndex = ''; | |
465 // Same for overflow. | |
466 dragItem.parentNode.style.overflow = ''; | |
467 }); | |
468 | |
469 this.invalidate_(); | |
470 this.layout(); | |
471 this.dragItem_ = null; | |
472 } | |
473 }, | |
474 | |
475 handleDrag_: function(e) { | |
476 // Moves the drag item making sure that it is not displayed outside the | |
477 // browser viewport. | |
478 var item = getItem(e.target); | |
479 var rect = this.element.getBoundingClientRect(); | |
480 item.style.pointerEvents = 'none'; | |
481 | |
482 var x = this.startX_ + e.screenX - this.startScreenX_; | |
483 var y = this.startY_ + e.screenY - this.startScreenY_; | |
484 | |
485 // The position of the item is relative to #most-visited so we need to | |
486 // subtract that when calculating the allowed position. | |
487 x = Math.max(x, -rect.left); | |
488 x = Math.min(x, document.body.clientWidth - rect.left - item.offsetWidth - | |
489 2); | |
490 // The shadow is 2px | |
491 y = Math.max(-rect.top, y); | |
492 y = Math.min(y, document.body.clientHeight - rect.top - | |
493 item.offsetHeight - 2); | |
494 | |
495 // Override right in case of RTL. | |
496 item.style.right = 'auto'; | |
497 item.style.left = x + 'px'; | |
498 item.style.top = y + 'px'; | |
499 item.style.zIndex = 2; | |
500 }, | |
501 | |
502 // We listen to mousedown to get the relative position of the cursor for | |
503 // dnd. | |
504 handleMouseDown_: function(e) { | |
505 var item = getItem(e.target); | |
506 if (item) { | |
507 this.startX_ = item.offsetLeft; | |
508 this.startY_ = item.offsetTop; | |
509 this.startScreenX_ = e.screenX; | |
510 this.startScreenY_ = e.screenY; | |
511 | |
512 // We don't want to focus the item on mousedown. However, to prevent | |
513 // focus one has to call preventDefault but this also prevents the drag | |
514 // and drop (sigh) so we only prevent it when the user is not doing a | |
515 // left mouse button drag. | |
516 if (e.button != 0) // LEFT | |
517 e.preventDefault(); | |
518 } | |
519 }, | |
520 | |
521 canDropOnElement_: function(el) { | |
522 return this.dragItem_ && el && | |
523 el.classList.contains('thumbnail-container') && | |
524 !el.classList.contains('filler'); | |
525 }, | |
526 | |
527 // Thumbnail hovering | |
528 | |
529 // TODO(mmenke): Either implement preconnect/prerendering based on | |
530 // hovering, or remove this code. | |
531 | |
532 /** | |
533 * Record the time the mouse has been hovering over a thumbnail. | |
534 * |clicked| must be true if the thumbnail was clicked, or false if | |
535 * the cursor was moved off of the thumbnail. | |
536 */ | |
537 RecordHoverTime_: function(clicked) { | |
538 if (!this.hoverStartTime_) | |
539 return; | |
540 var hoverDuration = (new Date()).getTime() - this.hoverStartTime_; | |
541 if (hoverDuration > 500) | |
542 hoverDuration = 500; | |
543 chrome.send('recordInHistogram', | |
544 [clicked ? 'NewTabPage.HoverTimeClicked' | |
545 : 'NewTabPage.HoverTimeNotClicked', | |
546 hoverDuration, | |
547 500]); | |
548 this.hoverStartTime_ = null; | |
549 }, | |
550 | |
551 /** | |
552 * Record the time the cursor started hovering over a thumbnail. | |
553 * Do nothing if currently dragging the thumbnail. | |
554 */ | |
555 handleMouseOver_: function() { | |
556 if (!this.isDragging()) | |
557 this.hoverStartTime_ = (new Date()).getTime(); | |
558 }, | |
559 | |
560 /** | |
561 * Record the time the cursor spend hovering over the thumbnail. | |
562 */ | |
563 handleMouseOut_: function() { | |
564 this.RecordHoverTime_(false); | |
565 }, | |
566 | |
567 | |
568 /// data | |
569 | |
570 data_: null, | |
571 get data() { | |
572 return this.data_; | |
573 }, | |
574 set data(data) { | |
575 // We append the class name with the "filler" so that we can style fillers | |
576 // differently. | |
577 var maxItems = 8; | |
578 data.length = Math.min(maxItems, data.length); | |
579 var len = data.length; | |
580 for (var i = len; i < maxItems; i++) { | |
581 data[i] = {filler: true}; | |
582 } | |
583 | |
584 // On setting we need to update the items | |
585 this.data_ = data; | |
586 this.updateMostVisited_(); | |
587 this.updateMiniview_(); | |
588 this.updateMenu_(); | |
589 }, | |
590 | |
591 updateMostVisited_: function() { | |
592 | |
593 function getThumbnailClassName(item) { | |
594 return 'thumbnail-container' + | |
595 (item.pinned ? ' pinned' : '') + | |
596 (item.filler ? ' filler' : ''); | |
597 } | |
598 | |
599 var data = this.data; | |
600 var children = this.element.children; | |
601 for (var i = 0; i < data.length; i++) { | |
602 var d = data[i]; | |
603 var t = children[i]; | |
604 | |
605 // If we have a filler continue | |
606 var oldClassName = t.className; | |
607 var newClassName = getThumbnailClassName(d); | |
608 if (oldClassName != newClassName) { | |
609 t.className = newClassName; | |
610 } | |
611 | |
612 // No need to continue if this is a filler. | |
613 if (newClassName == 'thumbnail-container filler') { | |
614 // Make sure the user cannot tab to the filler. | |
615 t.tabIndex = -1; | |
616 t.querySelector('.thumbnail-wrapper').style.backgroundImage = ''; | |
617 continue; | |
618 } | |
619 // Allow focus. | |
620 t.tabIndex = 1; | |
621 | |
622 t.href = d.url; | |
623 t.setAttribute('ping', | |
624 getAppPingUrl('PING_BY_URL', d.url, 'NTP_MOST_VISITED')); | |
625 t.querySelector('.pin').title = localStrings.getString(d.pinned ? | |
626 'unpinthumbnailtooltip' : 'pinthumbnailtooltip'); | |
627 t.querySelector('.remove').title = | |
628 localStrings.getString('removethumbnailtooltip'); | |
629 | |
630 // There was some concern that a malformed malicious URL could cause an | |
631 // XSS attack but setting style.backgroundImage = 'url(javascript:...)' | |
632 // does not execute the JavaScript in WebKit. | |
633 | |
634 var thumbnailUrl = d.thumbnailUrl || 'chrome://thumb/' + d.url; | |
635 t.querySelector('.thumbnail-wrapper').style.backgroundImage = | |
636 url(thumbnailUrl); | |
637 var titleDiv = t.querySelector('.title > div'); | |
638 titleDiv.xtitle = titleDiv.textContent = d.title; | |
639 var faviconUrl = d.faviconUrl || 'chrome://favicon/' + d.url; | |
640 titleDiv.style.backgroundImage = url(faviconUrl); | |
641 titleDiv.dir = d.direction; | |
642 } | |
643 }, | |
644 | |
645 updateMiniview_: function() { | |
646 this.miniview.textContent = ''; | |
647 var data = this.data.slice(0, MAX_MINIVIEW_ITEMS); | |
648 for (var i = 0, item; item = data[i]; i++) { | |
649 if (item.filler) { | |
650 continue; | |
651 } | |
652 | |
653 var span = document.createElement('span'); | |
654 var a = span.appendChild(document.createElement('a')); | |
655 a.href = item.url; | |
656 a.setAttribute('ping', | |
657 getAppPingUrl('PING_BY_URL', item.url, 'NTP_MOST_VISITED')); | |
658 a.textContent = item.title; | |
659 a.style.backgroundImage = url('chrome://favicon/' + item.url); | |
660 a.className = 'item'; | |
661 this.miniview.appendChild(span); | |
662 } | |
663 updateMiniviewClipping(this.miniview); | |
664 }, | |
665 | |
666 updateMenu_: function() { | |
667 clearClosedMenu(this.menu); | |
668 var data = this.data.slice(0, MAX_MINIVIEW_ITEMS); | |
669 for (var i = 0, item; item = data[i]; i++) { | |
670 if (!item.filler) { | |
671 addClosedMenuEntry( | |
672 this.menu, item.url, item.title, 'chrome://favicon/' + item.url, | |
673 getAppPingUrl('PING_BY_URL', item.url, 'NTP_MOST_VISITED')); | |
674 } | |
675 } | |
676 addClosedMenuFooter( | |
677 this.menu, 'most-visited', MENU_THUMB, Section.THUMB); | |
678 }, | |
679 | |
680 handleClick_: function(e) { | |
681 var target = e.target; | |
682 if (target.classList.contains('pin')) { | |
683 this.togglePinned_(getItem(target)); | |
684 e.preventDefault(); | |
685 } else if (target.classList.contains('remove')) { | |
686 this.blacklist(getItem(target)); | |
687 e.preventDefault(); | |
688 } else { | |
689 var item = getItem(target); | |
690 if (item) { | |
691 var index = Array.prototype.indexOf.call(item.parentNode.children, | |
692 item); | |
693 this.RecordHoverTime_(true); | |
694 if (index != -1) | |
695 chrome.send('recordInHistogram', | |
696 ['NewTabPage.MostVisited', index, 8]); | |
697 } | |
698 } | |
699 }, | |
700 | |
701 /** | |
702 * Allow blacklisting most visited site using the keyboard. | |
703 */ | |
704 handleKeyDown_: function(e) { | |
705 if (!cr.isMac && e.keyCode == 46 || // Del | |
706 cr.isMac && e.metaKey && e.keyCode == 8) { // Cmd + Backspace | |
707 this.blacklist(e.target); | |
708 } | |
709 } | |
710 }; | |
711 | |
712 return MostVisited; | |
713 })(); | |
OLD | NEW |