| Index: chrome/browser/resources/new_tab.js
|
| diff --git a/chrome/browser/resources/new_tab.js b/chrome/browser/resources/new_tab.js
|
| deleted file mode 100644
|
| index bd013f0612d169e5b4b764a26dd6cb02da807ddf..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/resources/new_tab.js
|
| +++ /dev/null
|
| @@ -1,1489 +0,0 @@
|
| -// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -// To avoid creating tons of unnecessary nodes. We assume we cannot fit more
|
| -// than this many items in the miniview.
|
| -var MAX_MINIVIEW_ITEMS = 15;
|
| -
|
| -// Extra spacing at the top of the layout.
|
| -var LAYOUT_SPACING_TOP = 25;
|
| -
|
| -// The visible height of the expanded maxiview.
|
| -var maxiviewVisibleHeight = 0;
|
| -
|
| -var APP_LAUNCH = {
|
| - // The histogram buckets (keep in sync with extension_constants.h).
|
| - NTP_APPS_MAXIMIZED: 0,
|
| - NTP_APPS_COLLAPSED: 1,
|
| - NTP_APPS_MENU: 2,
|
| - NTP_MOST_VISITED: 3,
|
| - NTP_RECENTLY_CLOSED: 4,
|
| - NTP_APP_RE_ENABLE: 16
|
| -};
|
| -
|
| -var APP_LAUNCH_URL = {
|
| - // The URL prefix for pings that record app launches by URL.
|
| - PING_BY_URL: 'record-app-launch-by-url',
|
| -
|
| - // The URL prefix for pings that record app launches by ID.
|
| - PING_BY_ID: 'record-app-launch-by-id',
|
| -
|
| - // The URL prefix used by the webstore link 'ping' attributes.
|
| - PING_WEBSTORE: 'record-webstore-launch'
|
| -};
|
| -
|
| -function getAppPingUrl(prefix, data, bucket) {
|
| - return [APP_LAUNCH_URL[prefix],
|
| - encodeURIComponent(data),
|
| - APP_LAUNCH[bucket]].join('+');
|
| -}
|
| -
|
| -function getSectionCloseButton(sectionId) {
|
| - return document.querySelector('#' + sectionId + ' .section-close-button');
|
| -}
|
| -
|
| -function getSectionMenuButton(sectionId) {
|
| - return $(sectionId + '-button');
|
| -}
|
| -
|
| -function getSectionMenuButtonTextId(sectionId) {
|
| - return sectionId.replace(/-/g, '');
|
| -}
|
| -
|
| -function setSectionMenuMode(sectionId, section, menuModeEnabled, menuModeMask) {
|
| - var el = $(sectionId);
|
| - if (!menuModeEnabled) {
|
| - // Because sections are collapsed when they are in menu mode, it is not
|
| - // necessary to restore the maxiview here. It will happen if the section
|
| - // header is clicked.
|
| - // TODO(aa): Sections should maintain their collapse state when minimized.
|
| - el.classList.remove('menu');
|
| - shownSections &= ~menuModeMask;
|
| - } else {
|
| - if (section) {
|
| - hideSection(section); // To hide the maxiview.
|
| - }
|
| - el.classList.add('menu');
|
| - shownSections |= menuModeMask;
|
| - }
|
| - layoutSections();
|
| -}
|
| -
|
| -function clearClosedMenu(menu) {
|
| - menu.innerHTML = '';
|
| -}
|
| -
|
| -function addClosedMenuEntryWithLink(menu, a) {
|
| - var span = document.createElement('span');
|
| - a.className += ' item menuitem';
|
| - span.appendChild(a);
|
| - menu.appendChild(span);
|
| -}
|
| -
|
| -function addClosedMenuEntry(menu, url, title, imageUrl, opt_pingUrl) {
|
| - var a = document.createElement('a');
|
| - a.href = url;
|
| - a.textContent = title;
|
| - a.style.backgroundImage = 'url(' + imageUrl + ')';
|
| - if (opt_pingUrl)
|
| - a.ping = opt_pingUrl;
|
| - addClosedMenuEntryWithLink(menu, a);
|
| -}
|
| -
|
| -function addClosedMenuFooter(menu, sectionId, mask, opt_section) {
|
| - menu.appendChild(document.createElement('hr'));
|
| -
|
| - var span = document.createElement('span');
|
| - var a = span.appendChild(document.createElement('a'));
|
| - a.href = '';
|
| - if (cr.isChromeOS) {
|
| - a.textContent = localStrings.getString('expandMenu');
|
| - } else {
|
| - a.textContent =
|
| - localStrings.getString(getSectionMenuButtonTextId(sectionId));
|
| - }
|
| - a.className = 'item';
|
| - a.addEventListener(
|
| - 'click',
|
| - function(e) {
|
| - getSectionMenuButton(sectionId).hideMenu();
|
| - e.preventDefault();
|
| - setSectionMenuMode(sectionId, opt_section, false, mask);
|
| - shownSections &= ~mask;
|
| - saveShownSections();
|
| - });
|
| - menu.appendChild(span);
|
| -}
|
| -
|
| -function initializeSection(sectionId, mask, opt_section) {
|
| - var button = getSectionCloseButton(sectionId);
|
| - button.addEventListener(
|
| - 'click',
|
| - function() {
|
| - setSectionMenuMode(sectionId, opt_section, true, mask);
|
| - saveShownSections();
|
| - });
|
| -}
|
| -
|
| -function updateSimpleSection(id, section) {
|
| - var elm = $(id);
|
| - var maxiview = getSectionMaxiview(elm);
|
| - var miniview = getSectionMiniview(elm);
|
| - if (shownSections & section) {
|
| - // The section is expanded, so the maxiview should be opaque (visible) and
|
| - // the miniview should be hidden.
|
| - elm.classList.remove('collapsed');
|
| - if (maxiview) {
|
| - maxiview.classList.remove('collapsed');
|
| - maxiview.classList.add('opaque');
|
| - }
|
| - if (miniview)
|
| - miniview.classList.remove('opaque');
|
| - } else {
|
| - // The section is collapsed, so the maxiview should be hidden and the
|
| - // miniview should be opaque.
|
| - elm.classList.add('collapsed');
|
| - if (maxiview) {
|
| - maxiview.classList.add('collapsed');
|
| - maxiview.classList.remove('opaque');
|
| - }
|
| - if (miniview)
|
| - miniview.classList.add('opaque');
|
| - }
|
| -}
|
| -
|
| -var sessionItems = [];
|
| -
|
| -function foreignSessions(data) {
|
| - logEvent('received foreign sessions');
|
| - // We need to store the foreign sessions so we can update the layout on a
|
| - // resize.
|
| - sessionItems = data;
|
| - renderForeignSessions();
|
| - layoutSections();
|
| -}
|
| -
|
| -function renderForeignSessions() {
|
| - // Remove all existing items and create new items.
|
| - var sessionElement = $('foreign-sessions');
|
| - var parentSessionElement = sessionElement.lastElementChild;
|
| - parentSessionElement.textContent = '';
|
| -
|
| - // For each client, create entries and append the lists together.
|
| - sessionItems.forEach(function(item, i) {
|
| - // TODO(zea): Get real client names. See crbug/59672.
|
| - var name = 'Client ' + i;
|
| - parentSessionElement.appendChild(createForeignSession(item, name));
|
| - });
|
| -
|
| - layoutForeignSessions();
|
| -}
|
| -
|
| -function layoutForeignSessions() {
|
| - var sessionElement = $('foreign-sessions');
|
| - // We cannot use clientWidth here since the width has a transition.
|
| - var availWidth = useSmallGrid() ? 692 : 920;
|
| - var parentSessEl = sessionElement.lastElementChild;
|
| -
|
| - if (parentSessEl.hasChildNodes()) {
|
| - sessionElement.classList.remove('disabled');
|
| - sessionElement.classList.remove('opaque');
|
| - } else {
|
| - sessionElement.classList.add('disabled');
|
| - sessionElement.classList.add('opaque');
|
| - }
|
| -}
|
| -
|
| -function createForeignSession(client, name) {
|
| - // Vertically stack the windows in a client.
|
| - var stack = document.createElement('div');
|
| - stack.className = 'foreign-session-client item link';
|
| - stack.textContent = name;
|
| - stack.sessionTag = client[0].sessionTag;
|
| -
|
| - client.forEach(function(win, i) {
|
| - // Create a window entry.
|
| - var winSpan = document.createElement('span');
|
| - var winEl = document.createElement('p');
|
| - winEl.className = 'item link window';
|
| - winEl.tabItems = win.tabs;
|
| - winEl.tabIndex = 0;
|
| - winEl.textContent = formatTabsText(win.tabs.length);
|
| - winEl.xtitle = win.title;
|
| - winEl.sessionTag = win.sessionTag;
|
| - winEl.winNum = i;
|
| - winEl.addEventListener('click', maybeOpenForeignWindow);
|
| - winEl.addEventListener('keydown',
|
| - handleIfEnterKey(maybeOpenForeignWindow));
|
| - winSpan.appendChild(winEl);
|
| -
|
| - // Sort tabs by MRU order
|
| - win.tabs.sort(function(a, b) {
|
| - return a.timestamp < b.timestamp;
|
| - });
|
| -
|
| - // Create individual tab information.
|
| - win.tabs.forEach(function(data) {
|
| - var tabEl = document.createElement('a');
|
| - tabEl.className = 'item link tab';
|
| - tabEl.href = data.timestamp;
|
| - tabEl.style.backgroundImage = url('chrome://favicon/' + data.url);
|
| - tabEl.dir = data.direction;
|
| - tabEl.textContent = data.title;
|
| - tabEl.sessionTag = win.sessionTag;
|
| - tabEl.winNum = i;
|
| - tabEl.sessionId = data.sessionId;
|
| - tabEl.addEventListener('click', maybeOpenForeignTab);
|
| - tabEl.addEventListener('keydown',
|
| - handleIfEnterKey(maybeOpenForeignTab));
|
| -
|
| - winSpan.appendChild(tabEl);
|
| - });
|
| -
|
| - // Append the window.
|
| - stack.appendChild(winSpan);
|
| - });
|
| - return stack;
|
| -}
|
| -
|
| -var recentItems = [];
|
| -
|
| -function recentlyClosedTabs(data) {
|
| - logEvent('received recently closed tabs');
|
| - // We need to store the recent items so we can update the layout on a resize.
|
| - recentItems = data;
|
| - renderRecentlyClosed();
|
| - layoutSections();
|
| -}
|
| -
|
| -function renderRecentlyClosed() {
|
| - // Remove all existing items and create new items.
|
| - var recentElement = $('recently-closed');
|
| - var parentEl = recentElement.lastElementChild;
|
| - parentEl.textContent = '';
|
| - var recentMenu = $('recently-closed-menu');
|
| - clearClosedMenu(recentMenu);
|
| -
|
| - recentItems.forEach(function(item) {
|
| - parentEl.appendChild(createRecentItem(item));
|
| - addRecentMenuItem(recentMenu, item);
|
| - });
|
| - addClosedMenuFooter(recentMenu, 'recently-closed', MENU_RECENT);
|
| -
|
| - layoutRecentlyClosed();
|
| -}
|
| -
|
| -function createRecentItem(data) {
|
| - var isWindow = data.type == 'window';
|
| - var el;
|
| - if (isWindow) {
|
| - el = document.createElement('span');
|
| - el.className = 'item link window';
|
| - el.tabItems = data.tabs;
|
| - el.tabIndex = 0;
|
| - el.textContent = formatTabsText(data.tabs.length);
|
| - } else {
|
| - el = document.createElement('a');
|
| - el.className = 'item';
|
| - el.href = data.url;
|
| - el.ping = getAppPingUrl(
|
| - 'PING_BY_URL', data.url, 'NTP_RECENTLY_CLOSED');
|
| - el.style.backgroundImage = url('chrome://favicon/' + data.url);
|
| - el.dir = data.direction;
|
| - el.textContent = data.title;
|
| - }
|
| - el.sessionId = data.sessionId;
|
| - el.xtitle = data.title;
|
| - el.sessionTag = data.sessionTag;
|
| - var wrapperEl = document.createElement('span');
|
| - wrapperEl.appendChild(el);
|
| - return wrapperEl;
|
| -}
|
| -
|
| -function addRecentMenuItem(menu, data) {
|
| - var isWindow = data.type == 'window';
|
| - var a = document.createElement('a');
|
| - if (isWindow) {
|
| - a.textContent = formatTabsText(data.tabs.length);
|
| - a.className = 'window'; // To get the icon from the CSS .window rule.
|
| - a.href = ''; // To make underline show up.
|
| - } else {
|
| - a.href = data.url;
|
| - a.ping = getAppPingUrl(
|
| - 'PING_BY_URL', data.url, 'NTP_RECENTLY_CLOSED');
|
| - a.style.backgroundImage = 'url(chrome://favicon/' + data.url + ')';
|
| - a.textContent = data.title;
|
| - }
|
| - function clickHandler(e) {
|
| - chrome.send('reopenTab', [String(data.sessionId)]);
|
| - e.preventDefault();
|
| - }
|
| - a.addEventListener('click', clickHandler);
|
| - addClosedMenuEntryWithLink(menu, a);
|
| -}
|
| -
|
| -function saveShownSections() {
|
| - chrome.send('setShownSections', [shownSections]);
|
| -}
|
| -
|
| -var LayoutMode = {
|
| - SMALL: 1,
|
| - NORMAL: 2
|
| -};
|
| -
|
| -var layoutMode = useSmallGrid() ? LayoutMode.SMALL : LayoutMode.NORMAL;
|
| -
|
| -function handleWindowResize() {
|
| - if (window.innerWidth < 10) {
|
| - // We're probably a background tab, so don't do anything.
|
| - return;
|
| - }
|
| -
|
| - // TODO(jstritar): Remove the small-layout class and revert back to the
|
| - // @media (max-width) directive once http://crbug.com/70930 is fixed.
|
| - var oldLayoutMode = layoutMode;
|
| - var b = useSmallGrid();
|
| - if (b) {
|
| - layoutMode = LayoutMode.SMALL;
|
| - document.body.classList.add('small-layout');
|
| - } else {
|
| - layoutMode = LayoutMode.NORMAL;
|
| - document.body.classList.remove('small-layout');
|
| - }
|
| -
|
| - if (layoutMode != oldLayoutMode){
|
| - mostVisited.useSmallGrid = b;
|
| - mostVisited.layout();
|
| - apps.layout({force:true});
|
| - renderRecentlyClosed();
|
| - renderForeignSessions();
|
| - updateAllMiniviewClippings();
|
| - }
|
| -
|
| - layoutSections();
|
| -}
|
| -
|
| -// Stores some information about each section necessary to layout. A new
|
| -// instance is constructed for each section on each layout.
|
| -function SectionLayoutInfo(section) {
|
| - this.section = section;
|
| - this.header = section.querySelector('h2');
|
| - this.miniview = section.querySelector('.miniview');
|
| - this.maxiview = getSectionMaxiview(section);
|
| - this.expanded = this.maxiview && !section.classList.contains('collapsed');
|
| - this.fixedHeight = this.section.offsetHeight;
|
| - this.scrollingHeight = 0;
|
| -
|
| - if (this.expanded)
|
| - this.scrollingHeight = this.maxiview.offsetHeight;
|
| -}
|
| -
|
| -// Get all sections to be layed out.
|
| -SectionLayoutInfo.getAll = function() {
|
| - var sections = document.querySelectorAll(
|
| - '.section:not(.disabled):not(.menu)');
|
| - var result = [];
|
| - for (var i = 0, section; section = sections[i]; i++) {
|
| - result.push(new SectionLayoutInfo(section));
|
| - }
|
| - return result;
|
| -};
|
| -
|
| -// Ensure the miniview sections don't have any clipped items.
|
| -function updateMiniviewClipping(miniview) {
|
| - var clipped = false;
|
| - for (var j = 0, item; item = miniview.children[j]; j++) {
|
| - item.style.display = '';
|
| - if (clipped ||
|
| - (item.offsetLeft + item.offsetWidth) > miniview.offsetWidth) {
|
| - item.style.display = 'none';
|
| - clipped = true;
|
| - } else {
|
| - item.style.display = '';
|
| - }
|
| - }
|
| -}
|
| -
|
| -// Ensure none of the miniviews have any clipped items.
|
| -function updateAllMiniviewClippings() {
|
| - var miniviews = document.querySelectorAll('.section.collapsed .miniview');
|
| - for (var i = 0, miniview; miniview = miniviews[i]; i++) {
|
| - updateMiniviewClipping(miniview);
|
| - }
|
| -}
|
| -
|
| -// Returns whether or not vertical scrollbars are present.
|
| -function hasScrollBars() {
|
| - return window.innerHeight != document.body.clientHeight;
|
| -}
|
| -
|
| -// Enables scrollbars (they will only show up if needed).
|
| -function showScrollBars() {
|
| - document.body.classList.remove('noscroll');
|
| -}
|
| -
|
| -// Hides all scrollbars.
|
| -function hideScrollBars() {
|
| - document.body.classList.add('noscroll');
|
| -}
|
| -
|
| -// Returns whether or not the sections are currently animating due to a
|
| -// section transition.
|
| -function isAnimating() {
|
| - var de = document.documentElement;
|
| - return de.getAttribute('enable-section-animations') == 'true';
|
| -}
|
| -
|
| -// Layout the sections in a modified accordion. The header and miniview, if
|
| -// visible are fixed within the viewport. If there is an expanded section, its
|
| -// it scrolls.
|
| -//
|
| -// =============================
|
| -// | collapsed section | <- Any collapsed sections are fixed position.
|
| -// | and miniview |
|
| -// |---------------------------|
|
| -// | expanded section |
|
| -// | | <- There can be one expanded section and it
|
| -// | and maxiview | is absolutely positioned so that it can
|
| -// | | scroll "underneath" the fixed elements.
|
| -// | |
|
| -// |---------------------------|
|
| -// | another collapsed section |
|
| -// |---------------------------|
|
| -//
|
| -// We want the main frame scrollbar to be the one that scrolls the expanded
|
| -// region. To get this effect, we make the fixed elements position:fixed and the
|
| -// scrollable element position:absolute. We also artificially increase the
|
| -// height of the document so that it is possible to scroll down enough to
|
| -// display the end of the document, even with any fixed elements at the bottom
|
| -// of the viewport.
|
| -//
|
| -// There is a final twist: If the intrinsic height of the expanded section is
|
| -// less than the available height (because the window is tall), any collapsed
|
| -// sections sinch up and sit below the expanded section. This is so that we
|
| -// don't have a bunch of dead whitespace in the case of expanded sections that
|
| -// aren't very tall.
|
| -function layoutSections() {
|
| - // While transitioning sections, we only want scrollbars to appear if they're
|
| - // already present or the window is being resized (so there's no animation).
|
| - if (!hasScrollBars() && isAnimating())
|
| - hideScrollBars();
|
| -
|
| - var sections = SectionLayoutInfo.getAll();
|
| - var expandedSection = null;
|
| - var headerHeight = LAYOUT_SPACING_TOP;
|
| - var footerHeight = 0;
|
| -
|
| - // Calculate the height of the fixed elements above the expanded section. Also
|
| - // take note of the expanded section, if there is one.
|
| - var i;
|
| - var section;
|
| - for (i = 0; section = sections[i]; i++) {
|
| - headerHeight += section.fixedHeight;
|
| - if (section.expanded) {
|
| - expandedSection = section;
|
| - i++;
|
| - break;
|
| - }
|
| - }
|
| -
|
| - // Include the height of the sync promo bar.
|
| - var sync_promo_height = $('sync-promo').offsetHeight;
|
| - headerHeight += sync_promo_height;
|
| -
|
| - // Calculate the height of the fixed elements below the expanded section, if
|
| - // any.
|
| - for (; section = sections[i]; i++) {
|
| - footerHeight += section.fixedHeight;
|
| - }
|
| - // Leave room for bottom bar if it's visible.
|
| - footerHeight += $('closed-sections-bar').offsetHeight;
|
| -
|
| -
|
| - // Determine the height to use for the expanded section. If there isn't enough
|
| - // space to show the expanded section completely, this will be the available
|
| - // height. Otherwise, we use the intrinsic height of the expanded section.
|
| - var expandedSectionHeight;
|
| - var expandedSectionIsClipped = false;
|
| - if (expandedSection) {
|
| - var flexHeight = window.innerHeight - headerHeight - footerHeight;
|
| - if (flexHeight < expandedSection.scrollingHeight) {
|
| - expandedSectionHeight = flexHeight;
|
| -
|
| - // Also, artificially expand the height of the document so that we can see
|
| - // the entire expanded section.
|
| - //
|
| - // TODO(aa): Where does this come from? It is the difference between what
|
| - // we set document.body.style.height to and what
|
| - // document.body.scrollHeight measures afterward. I expect them to be the
|
| - // same if document.body has no margins.
|
| - var fudge = 44;
|
| - document.body.style.height =
|
| - headerHeight +
|
| - expandedSection.scrollingHeight +
|
| - footerHeight +
|
| - fudge +
|
| - 'px';
|
| - expandedSectionIsClipped = true;
|
| - } else {
|
| - expandedSectionHeight = expandedSection.scrollingHeight;
|
| - document.body.style.height = '';
|
| - }
|
| - } else {
|
| - // We only set the document height when a section is expanded. If
|
| - // all sections are collapsed, then get rid of the previous height.
|
| - document.body.style.height = '';
|
| - }
|
| -
|
| - maxiviewVisibleHeight = expandedSectionHeight;
|
| -
|
| - // Now position all the elements.
|
| - var y = LAYOUT_SPACING_TOP + sync_promo_height;
|
| - for (i = 0, section; section = sections[i]; i++) {
|
| - section.section.style.top = y + 'px';
|
| - y += section.fixedHeight;
|
| -
|
| - if (section.maxiview) {
|
| - if (section == expandedSection) {
|
| - section.maxiview.style.top = y + 'px';
|
| - } else {
|
| - // The miniviews fade out gradually, so it may have height at this
|
| - // point. We position the maxiview as if the miniview was not displayed
|
| - // by subtracting off the miniview's total height (height + margin).
|
| - var miniviewFudge = 40; // miniview margin-bottom + margin-top
|
| - var miniviewHeight = section.miniview.offsetHeight + miniviewFudge;
|
| - section.maxiview.style.top = y - miniviewHeight + 'px';
|
| - }
|
| - }
|
| -
|
| - if (section.maxiview && section == expandedSection)
|
| - updateMask(
|
| - section.maxiview, expandedSectionHeight, expandedSectionIsClipped);
|
| -
|
| - if (section == expandedSection)
|
| - y += expandedSectionHeight;
|
| - }
|
| - if (cr.isChromeOS)
|
| - $('closed-sections-bar').style.top = y + 'px';
|
| -
|
| - // Position the notification container below the sync promo.
|
| - $('notification-container').style.top = sync_promo_height + 'px';
|
| -
|
| - updateMenuSections();
|
| - updateAttributionDisplay(y);
|
| -}
|
| -
|
| -function updateMask(maxiview, visibleHeightPx, isClipped) {
|
| - // If the section isn't actually clipped, then we don't want to use a mask at
|
| - // all, since enabling one turns off subpixel anti-aliasing.
|
| - if (!isClipped) {
|
| - maxiview.style.WebkitMaskImage = 'none';
|
| - return;
|
| - }
|
| -
|
| - // We want to end up with 10px gradients at the top and bottom of
|
| - // visibleHeight, but webkit-mask only supports expression in terms of
|
| - // percentages.
|
| -
|
| - // We might not have enough room to do 10px gradients on each side. To get the
|
| - // right effect, we don't want to make the gradients smaller, but make them
|
| - // appear to mush into each other.
|
| - var gradientHeightPx = Math.min(10, Math.floor(visibleHeightPx / 2));
|
| - var gradientDestination = 'rgba(0,0,0,' + (gradientHeightPx / 10) + ')';
|
| -
|
| - var bottomSpacing = 15;
|
| - var first = parseFloat(maxiview.style.top) / window.innerHeight;
|
| - var second = first + gradientHeightPx / window.innerHeight;
|
| - var fourth = first + (visibleHeightPx - bottomSpacing) / window.innerHeight;
|
| - var third = fourth - gradientHeightPx / window.innerHeight;
|
| -
|
| - var gradientArguments = [
|
| - 'transparent',
|
| - getColorStopString(first, 'transparent'),
|
| - getColorStopString(second, gradientDestination),
|
| - getColorStopString(third, gradientDestination),
|
| - getColorStopString(fourth, 'transparent'),
|
| - 'transparent'
|
| - ];
|
| -
|
| - var gradient = '-webkit-linear-gradient(' + gradientArguments.join(',') + ')';
|
| - maxiview.style.WebkitMaskImage = gradient;
|
| -}
|
| -
|
| -function getColorStopString(height, color) {
|
| - // TODO(arv): The CSS3 gradient syntax allows px units so we should simplify
|
| - // this to use pixels instead.
|
| - return color + ' ' + height * 100 + '%';
|
| -}
|
| -
|
| -// Updates the visibility of the menu buttons for each section, based on
|
| -// whether they are currently enabled and in menu mode.
|
| -function updateMenuSections() {
|
| - var elms = document.getElementsByClassName('section');
|
| - for (var i = 0, elm; elm = elms[i]; i++) {
|
| - var button = getSectionMenuButton(elm.id);
|
| - if (!button)
|
| - continue;
|
| -
|
| - if (!elm.classList.contains('disabled') &&
|
| - elm.classList.contains('menu')) {
|
| - button.style.display = 'inline-block';
|
| - } else {
|
| - button.style.display = 'none';
|
| - }
|
| - }
|
| -}
|
| -
|
| -window.addEventListener('resize', handleWindowResize);
|
| -
|
| -var sectionToElementMap;
|
| -function getSectionElement(section) {
|
| - if (!sectionToElementMap) {
|
| - sectionToElementMap = {};
|
| - for (var key in Section) {
|
| - sectionToElementMap[Section[key]] =
|
| - document.querySelector('.section[section=' + key + ']');
|
| - }
|
| - }
|
| - return sectionToElementMap[section];
|
| -}
|
| -
|
| -function getSectionMaxiview(section) {
|
| - return $(section.id + '-maxiview');
|
| -}
|
| -
|
| -function getSectionMiniview(section) {
|
| - return section.querySelector('.miniview');
|
| -}
|
| -
|
| -// You usually want to call |showOnlySection()| instead of this.
|
| -function showSection(section) {
|
| - if (!(section & shownSections)) {
|
| - shownSections |= section;
|
| - var el = getSectionElement(section);
|
| - if (el) {
|
| - el.classList.remove('collapsed');
|
| -
|
| - var maxiview = getSectionMaxiview(el);
|
| - if (maxiview) {
|
| - maxiview.classList.remove('collapsing');
|
| - maxiview.classList.remove('collapsed');
|
| - // The opacity won't transition if you toggle the display property
|
| - // at the same time. To get a fade effect, we set the opacity
|
| - // asynchronously from another function, after the display is toggled.
|
| - // 1) 'collapsed' (display: none, opacity: 0)
|
| - // 2) none (display: block, opacity: 0)
|
| - // 3) 'opaque' (display: block, opacity: 1)
|
| - setTimeout(function () {
|
| - maxiview.classList.add('opaque');
|
| - }, 0);
|
| - }
|
| -
|
| - var miniview = getSectionMiniview(el);
|
| - if (miniview) {
|
| - // The miniview is hidden immediately (no need to set this async).
|
| - miniview.classList.remove('opaque');
|
| - }
|
| - }
|
| -
|
| - switch (section) {
|
| - case Section.THUMB:
|
| - mostVisited.visible = true;
|
| - mostVisited.layout();
|
| - break;
|
| - case Section.APPS:
|
| - apps.visible = true;
|
| - apps.layout({disableAnimations:true});
|
| - break;
|
| - }
|
| - }
|
| -}
|
| -
|
| -// Show this section and hide all other sections - at most one section can
|
| -// be open at one time.
|
| -function showOnlySection(section) {
|
| - for (var p in Section) {
|
| - if (p == section)
|
| - showSection(Section[p]);
|
| - else
|
| - hideSection(Section[p]);
|
| - }
|
| -}
|
| -
|
| -function hideSection(section) {
|
| - if (section & shownSections) {
|
| - shownSections &= ~section;
|
| -
|
| - switch (section) {
|
| - case Section.THUMB:
|
| - mostVisited.visible = false;
|
| - mostVisited.layout();
|
| - break;
|
| - case Section.APPS:
|
| - apps.visible = false;
|
| - apps.layout();
|
| - break;
|
| - }
|
| -
|
| - var el = getSectionElement(section);
|
| - if (el) {
|
| - el.classList.add('collapsed');
|
| -
|
| - var maxiview = getSectionMaxiview(el);
|
| - if (maxiview) {
|
| - maxiview.classList.add((isDoneLoading() && isAnimating()) ?
|
| - 'collapsing' : 'collapsed');
|
| - maxiview.classList.remove('opaque');
|
| - }
|
| -
|
| - var miniview = getSectionMiniview(el);
|
| - if (miniview) {
|
| - // We need to set this asynchronously to properly get the fade effect.
|
| - setTimeout(function() {
|
| - miniview.classList.add('opaque');
|
| - }, 0);
|
| - updateMiniviewClipping(miniview);
|
| - }
|
| - }
|
| - }
|
| -}
|
| -
|
| -window.addEventListener('webkitTransitionEnd', function(e) {
|
| - if (e.target.classList.contains('collapsing')) {
|
| - e.target.classList.add('collapsed');
|
| - e.target.classList.remove('collapsing');
|
| - }
|
| -
|
| - if (e.target.classList.contains('maxiview') ||
|
| - e.target.classList.contains('miniview')) {
|
| - document.documentElement.removeAttribute('enable-section-animations');
|
| - showScrollBars();
|
| - }
|
| -});
|
| -
|
| -/**
|
| - * Callback when the shown sections changes in another NTP.
|
| - * @param {number} newShownSections Bitmask of the shown sections.
|
| - */
|
| -function setShownSections(newShownSections) {
|
| - for (var key in Section) {
|
| - if (newShownSections & Section[key])
|
| - showSection(Section[key]);
|
| - else
|
| - hideSection(Section[key]);
|
| - }
|
| - setSectionMenuMode('apps', Section.APPS, newShownSections & MENU_APPS,
|
| - MENU_APPS);
|
| - setSectionMenuMode('most-visited', Section.THUMB,
|
| - newShownSections & MENU_THUMB, MENU_THUMB);
|
| - setSectionMenuMode('recently-closed', undefined,
|
| - newShownSections & MENU_RECENT, MENU_RECENT);
|
| - layoutSections();
|
| -}
|
| -
|
| -// Recently closed
|
| -
|
| -function layoutRecentlyClosed() {
|
| - var recentElement = $('recently-closed');
|
| - var miniview = getSectionMiniview(recentElement);
|
| -
|
| - updateMiniviewClipping(miniview);
|
| -
|
| - if (miniview.hasChildNodes()) {
|
| - recentElement.classList.remove('disabled');
|
| - miniview.classList.add('opaque');
|
| - } else {
|
| - recentElement.classList.add('disabled');
|
| - miniview.classList.remove('opaque');
|
| - }
|
| -
|
| - layoutSections();
|
| -}
|
| -
|
| -/**
|
| - * This function is called by the backend whenever the sync status section
|
| - * needs to be updated to reflect recent sync state changes. The backend passes
|
| - * the new status information in the newMessage parameter. The state includes
|
| - * the following:
|
| - *
|
| - * syncsectionisvisible: true if the sync section needs to show up on the new
|
| - * tab page and false otherwise.
|
| - * title: the header for the sync status section.
|
| - * msg: the actual message (e.g. "Synced to foo@gmail.com").
|
| - * linkisvisible: true if the link element should be visible within the sync
|
| - * section and false otherwise.
|
| - * linktext: the text to display as the link in the sync status (only used if
|
| - * linkisvisible is true).
|
| - * linkurlisset: true if an URL should be set as the href for the link and false
|
| - * otherwise. If this field is false, then clicking on the link
|
| - * will result in sending a message to the backend (see
|
| - * 'SyncLinkClicked').
|
| - * linkurl: the URL to use as the element's href (only used if linkurlisset is
|
| - * true).
|
| - */
|
| -function syncMessageChanged(newMessage) {
|
| - var syncStatusElement = $('sync-status');
|
| -
|
| - // Hide the section if the message is emtpy.
|
| - if (!newMessage['syncsectionisvisible']) {
|
| - syncStatusElement.classList.add('disabled');
|
| - return;
|
| - }
|
| -
|
| - syncStatusElement.classList.remove('disabled');
|
| -
|
| - var content = syncStatusElement.children[0];
|
| -
|
| - // Set the sync section background color based on the state.
|
| - if (newMessage.msgtype == 'error') {
|
| - content.style.backgroundColor = 'tomato';
|
| - } else {
|
| - content.style.backgroundColor = '';
|
| - }
|
| -
|
| - // Set the text for the header and sync message.
|
| - var titleElement = content.firstElementChild;
|
| - titleElement.textContent = newMessage.title;
|
| - var messageElement = titleElement.nextElementSibling;
|
| - messageElement.textContent = newMessage.msg;
|
| -
|
| - // Remove what comes after the message
|
| - while (messageElement.nextSibling) {
|
| - content.removeChild(messageElement.nextSibling);
|
| - }
|
| -
|
| - if (newMessage.linkisvisible) {
|
| - var el;
|
| - if (newMessage.linkurlisset) {
|
| - // Use a link
|
| - el = document.createElement('a');
|
| - el.href = newMessage.linkurl;
|
| - } else {
|
| - el = document.createElement('button');
|
| - el.className = 'link';
|
| - el.addEventListener('click', syncSectionLinkClicked);
|
| - }
|
| - el.textContent = newMessage.linktext;
|
| - content.appendChild(el);
|
| - fixLinkUnderline(el);
|
| - }
|
| -
|
| - layoutSections();
|
| -}
|
| -
|
| -/**
|
| - * Invoked when the link in the sync promo or sync status section is clicked.
|
| - */
|
| -function syncSectionLinkClicked(e) {
|
| - chrome.send('SyncLinkClicked');
|
| - e.preventDefault();
|
| -}
|
| -
|
| -/**
|
| - * Invoked when link to start sync in the promo message is clicked, and Chrome
|
| - * has already been synced to an account.
|
| - */
|
| -function syncAlreadyEnabled(message) {
|
| - showNotification(message.syncEnabledMessage);
|
| -}
|
| -
|
| -/**
|
| - * Returns the text used for a recently closed window.
|
| - * @param {number} numTabs Number of tabs in the window.
|
| - * @return {string} The text to use.
|
| - */
|
| -function formatTabsText(numTabs) {
|
| - if (numTabs == 1)
|
| - return localStrings.getString('closedwindowsingle');
|
| - return localStrings.getStringF('closedwindowmultiple', numTabs);
|
| -}
|
| -
|
| -// Theme related
|
| -
|
| -function themeChanged(hasAttribution) {
|
| - document.documentElement.setAttribute('hasattribution', hasAttribution);
|
| - $('themecss').href = 'chrome://theme/css/newtab.css?' + Date.now();
|
| - updateAttribution();
|
| -}
|
| -
|
| -function updateAttribution() {
|
| - // Default value for standard NTP with no theme attribution or custom logo.
|
| - logEvent('updateAttribution called');
|
| - var imageId = 'IDR_PRODUCT_LOGO';
|
| - // Theme attribution always overrides custom logos.
|
| - if (document.documentElement.getAttribute('hasattribution') == 'true') {
|
| - logEvent('updateAttribution called with THEME ATTR');
|
| - imageId = 'IDR_THEME_NTP_ATTRIBUTION';
|
| - } else if (document.documentElement.getAttribute('customlogo') == 'true') {
|
| - logEvent('updateAttribution with CUSTOMLOGO');
|
| - imageId = 'IDR_CUSTOM_PRODUCT_LOGO';
|
| - }
|
| -
|
| - $('attribution-img').src = 'chrome://theme/' + imageId + '?' + Date.now();
|
| -}
|
| -
|
| -// If the content overlaps with the attribution, we bump its opacity down.
|
| -function updateAttributionDisplay(contentBottom) {
|
| - var attribution = $('attribution');
|
| - var main = $('main');
|
| - var rtl = document.documentElement.dir == 'rtl';
|
| - var contentRect = main.getBoundingClientRect();
|
| - var attributionRect = attribution.getBoundingClientRect();
|
| -
|
| - // Hack. See comments for '.haslayout' in new_tab.css.
|
| - if (attributionRect.width == 0)
|
| - return;
|
| - else
|
| - attribution.classList.remove('nolayout');
|
| -
|
| - if (contentBottom > attribution.offsetTop) {
|
| - if ((!rtl && contentRect.right > attributionRect.left) ||
|
| - (rtl && attributionRect.right > contentRect.left)) {
|
| - attribution.classList.add('obscured');
|
| - return;
|
| - }
|
| - }
|
| -
|
| - attribution.classList.remove('obscured');
|
| -}
|
| -
|
| -function bookmarkBarAttached() {
|
| - document.documentElement.setAttribute('bookmarkbarattached', 'true');
|
| -}
|
| -
|
| -function bookmarkBarDetached() {
|
| - document.documentElement.setAttribute('bookmarkbarattached', 'false');
|
| -}
|
| -
|
| -function viewLog() {
|
| - var lines = [];
|
| - var start = log[0][1];
|
| -
|
| - for (var i = 0; i < log.length; i++) {
|
| - lines.push((log[i][1] - start) + ': ' + log[i][0]);
|
| - }
|
| -
|
| - console.log(lines.join('\n'));
|
| -}
|
| -
|
| -// We apply the size class here so that we don't trigger layout animations
|
| -// onload.
|
| -
|
| -handleWindowResize();
|
| -
|
| -var localStrings = new LocalStrings();
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -// Things we know are not needed at startup go below here
|
| -
|
| -function afterTransition(f) {
|
| - if (!isDoneLoading()) {
|
| - // Make sure we do not use a timer during load since it slows down the UI.
|
| - f();
|
| - } else {
|
| - // The duration of all transitions are .15s
|
| - window.setTimeout(f, 150);
|
| - }
|
| -}
|
| -
|
| -// Notification
|
| -
|
| -
|
| -var notificationTimeout;
|
| -
|
| -/*
|
| - * Displays a message (either a string or a document fragment) in the
|
| - * notification slot at the top of the NTP. A close button ("x") will be
|
| - * inserted at the end of the message.
|
| - * @param {string|Node} message String or node to use as message.
|
| - * @param {string} actionText The text to show as a link next to the message.
|
| - * @param {function=} opt_f Function to call when the user clicks the action
|
| - * link.
|
| - * @param {number=} opt_delay The time in milliseconds before hiding the
|
| - * notification.
|
| - */
|
| -function showNotification(message, actionText, opt_f, opt_delay) {
|
| -// TODO(arv): Create a notification component.
|
| - var notificationElement = $('notification');
|
| - var f = opt_f || function() {};
|
| - var delay = opt_delay || 10000;
|
| -
|
| - function show() {
|
| - window.clearTimeout(notificationTimeout);
|
| - notificationElement.classList.add('show');
|
| - document.body.classList.add('notification-shown');
|
| - }
|
| -
|
| - function delayedHide() {
|
| - notificationTimeout = window.setTimeout(hideNotification, delay);
|
| - }
|
| -
|
| - function doAction() {
|
| - f();
|
| - closeNotification();
|
| - }
|
| -
|
| - function closeNotification() {
|
| - if (notification.classList.contains('promo'))
|
| - chrome.send('closePromo');
|
| - hideNotification();
|
| - }
|
| -
|
| - // Remove classList entries from previous notifications.
|
| - notification.classList.remove('first-run');
|
| - notification.classList.remove('promo');
|
| -
|
| - var messageContainer = notificationElement.firstElementChild;
|
| - var actionLink = notificationElement.querySelector('#action-link');
|
| - var closeButton = notificationElement.querySelector('#notification-close');
|
| -
|
| - // Remove any previous actionLink entry.
|
| - actionLink.textContent = '';
|
| -
|
| - $('notification-close').onclick = closeNotification;
|
| -
|
| - if (typeof message == 'string') {
|
| - messageContainer.textContent = message;
|
| - } else {
|
| - messageContainer.textContent = ''; // Remove all children.
|
| - messageContainer.appendChild(message);
|
| - }
|
| -
|
| - if (actionText) {
|
| - actionLink.style.display = '';
|
| - actionLink.textContent = actionText;
|
| - } else {
|
| - actionLink.style.display = 'none';
|
| - }
|
| -
|
| - actionLink.onclick = doAction;
|
| - actionLink.onkeydown = handleIfEnterKey(doAction);
|
| - notificationElement.onmouseover = show;
|
| - notificationElement.onmouseout = delayedHide;
|
| - actionLink.onfocus = show;
|
| - actionLink.onblur = delayedHide;
|
| - // Enable tabbing to the link now that it is shown.
|
| - actionLink.tabIndex = 0;
|
| -
|
| - show();
|
| - delayedHide();
|
| -}
|
| -
|
| -/**
|
| - * Hides the notifier.
|
| - */
|
| -function hideNotification() {
|
| - var notificationElement = $('notification');
|
| - notificationElement.classList.remove('show');
|
| - document.body.classList.remove('notification-shown');
|
| - var actionLink = notificationElement.querySelector('#actionlink');
|
| - var closeButton = notificationElement.querySelector('#notification-close');
|
| - // Prevent tabbing to the hidden link.
|
| - // Setting tabIndex to -1 only prevents future tabbing to it. If, however, the
|
| - // user switches window or a tab and then moves back to this tab the element
|
| - // may gain focus. We therefore make sure that we blur the element so that the
|
| - // element focus is not restored when coming back to this window.
|
| - if (actionLink) {
|
| - actionLink.tabIndex = -1;
|
| - actionLink.blur();
|
| - }
|
| - if (closeButton) {
|
| - closeButton.tabIndex = -1;
|
| - closeButton.blur();
|
| - }
|
| -}
|
| -
|
| -function showPromoNotification() {
|
| - showNotification(parseHtmlSubset(localStrings.getString('serverpromo')),
|
| - localStrings.getString('syncpromotext'),
|
| - function () { chrome.send('SyncLinkClicked'); },
|
| - 60000);
|
| - var notificationElement = $('notification');
|
| - notification.classList.add('promo');
|
| -}
|
| -
|
| -$('main').addEventListener('click', function(e) {
|
| - var p = e.target;
|
| - while (p && p.tagName != 'H2') {
|
| - // In case the user clicks on a button we do not want to expand/collapse a
|
| - // section.
|
| - if (p.tagName == 'BUTTON')
|
| - return;
|
| - p = p.parentNode;
|
| - }
|
| -
|
| - if (!p)
|
| - return;
|
| -
|
| - p = p.parentNode;
|
| - if (!getSectionMaxiview(p))
|
| - return;
|
| -
|
| - toggleSectionVisibilityAndAnimate(p.getAttribute('section'));
|
| -});
|
| -
|
| -$('most-visited-settings').addEventListener('click', function() {
|
| - $('clear-all-blacklisted').execute();
|
| -});
|
| -
|
| -function toggleSectionVisibilityAndAnimate(section) {
|
| - if (!section)
|
| - return;
|
| -
|
| - // It looks better to return the scroll to the top when toggling sections.
|
| - document.body.scrollTop = 0;
|
| -
|
| - // We set it back in webkitTransitionEnd.
|
| - document.documentElement.setAttribute('enable-section-animations', 'true');
|
| - if (shownSections & Section[section]) {
|
| - hideSection(Section[section]);
|
| - } else {
|
| - showOnlySection(section);
|
| - }
|
| - layoutSections();
|
| - saveShownSections();
|
| -}
|
| -
|
| -function handleIfEnterKey(f) {
|
| - return function(e) {
|
| - if (e.keyIdentifier == 'Enter')
|
| - f(e);
|
| - };
|
| -}
|
| -
|
| -function maybeReopenTab(e) {
|
| - var el = findAncestor(e.target, function(el) {
|
| - return el.sessionId !== undefined;
|
| - });
|
| - if (el) {
|
| - chrome.send('reopenTab', [String(el.sessionId)]);
|
| - e.preventDefault();
|
| -
|
| - setWindowTooltipTimeout();
|
| - }
|
| -}
|
| -
|
| -// Note that the openForeignSession calls can fail, resulting this method to
|
| -// not have any action (hence the maybe).
|
| -function maybeOpenForeignSession(e) {
|
| - var el = findAncestor(e.target, function(el) {
|
| - return el.sessionTag !== undefined;
|
| - });
|
| - if (el) {
|
| - chrome.send('openForeignSession', [String(el.sessionTag)]);
|
| - e.stopPropagation();
|
| - e.preventDefault();
|
| - setWindowTooltipTimeout();
|
| - }
|
| -}
|
| -
|
| -function maybeOpenForeignWindow(e) {
|
| - var el = findAncestor(e.target, function(el) {
|
| - return el.winNum !== undefined;
|
| - });
|
| - if (el) {
|
| - chrome.send('openForeignSession', [String(el.sessionTag),
|
| - String(el.winNum)]);
|
| - e.stopPropagation();
|
| - e.preventDefault();
|
| - setWindowTooltipTimeout();
|
| - }
|
| -}
|
| -
|
| -function maybeOpenForeignTab(e) {
|
| - var el = findAncestor(e.target, function(el) {
|
| - return el.sessionId !== undefined;
|
| - });
|
| - if (el) {
|
| - chrome.send('openForeignSession', [String(el.sessionTag), String(el.winNum),
|
| - String(el.sessionId)]);
|
| - e.stopPropagation();
|
| - e.preventDefault();
|
| - setWindowTooltipTimeout();
|
| - }
|
| -}
|
| -
|
| -// HACK(arv): After the window onblur event happens we get a mouseover event
|
| -// on the next item and we want to make sure that we do not show a tooltip
|
| -// for that.
|
| -function setWindowTooltipTimeout(e) {
|
| - window.setTimeout(function() {
|
| - windowTooltip.hide();
|
| - }, 2 * WindowTooltip.DELAY);
|
| -}
|
| -
|
| -function maybeShowWindowTooltip(e) {
|
| - var f = function(el) {
|
| - return el.tabItems !== undefined;
|
| - };
|
| - var el = findAncestor(e.target, f);
|
| - var relatedEl = findAncestor(e.relatedTarget, f);
|
| - if (el && el != relatedEl) {
|
| - windowTooltip.handleMouseOver(e, el, el.tabItems);
|
| - }
|
| -}
|
| -
|
| -
|
| -var recentlyClosedElement = $('recently-closed');
|
| -
|
| -recentlyClosedElement.addEventListener('click', maybeReopenTab);
|
| -recentlyClosedElement.addEventListener('keydown',
|
| - handleIfEnterKey(maybeReopenTab));
|
| -
|
| -recentlyClosedElement.addEventListener('mouseover', maybeShowWindowTooltip);
|
| -recentlyClosedElement.addEventListener('focus', maybeShowWindowTooltip, true);
|
| -
|
| -var foreignSessionElement = $('foreign-sessions');
|
| -
|
| -foreignSessionElement.addEventListener('click', maybeOpenForeignSession);
|
| -foreignSessionElement.addEventListener('keydown',
|
| - handleIfEnterKey(
|
| - maybeOpenForeignSession));
|
| -
|
| -foreignSessionElement.addEventListener('mouseover', maybeShowWindowTooltip);
|
| -foreignSessionElement.addEventListener('focus', maybeShowWindowTooltip, true);
|
| -
|
| -/**
|
| - * This object represents a tooltip representing a closed window. It is
|
| - * shown when hovering over a closed window item or when the item is focused. It
|
| - * gets hidden when blurred or when mousing out of the menu or the item.
|
| - * @param {Element} tooltipEl The element to use as the tooltip.
|
| - * @constructor
|
| - */
|
| -function WindowTooltip(tooltipEl) {
|
| - this.tooltipEl = tooltipEl;
|
| - this.boundHide_ = this.hide.bind(this);
|
| - this.boundHandleMouseOut_ = this.handleMouseOut.bind(this);
|
| -}
|
| -
|
| -WindowTooltip.trackMouseMove_ = function(e) {
|
| - WindowTooltip.clientX = e.clientX;
|
| - WindowTooltip.clientY = e.clientY;
|
| -};
|
| -
|
| -/**
|
| - * Time in ms to delay before the tooltip is shown.
|
| - * @type {number}
|
| - */
|
| -WindowTooltip.DELAY = 300;
|
| -
|
| -WindowTooltip.prototype = {
|
| - timer: 0,
|
| - handleMouseOver: function(e, linkEl, tabs) {
|
| - this.linkEl_ = linkEl;
|
| - if (e.type == 'mouseover') {
|
| - this.linkEl_.addEventListener('mousemove', WindowTooltip.trackMouseMove_);
|
| - this.linkEl_.addEventListener('mouseout', this.boundHandleMouseOut_);
|
| - } else { // focus
|
| - this.linkEl_.addEventListener('blur', this.boundHide_);
|
| - }
|
| - this.timer = window.setTimeout(this.show.bind(this, e.type, linkEl, tabs),
|
| - WindowTooltip.DELAY);
|
| - },
|
| - show: function(type, linkEl, tabs) {
|
| - window.addEventListener('blur', this.boundHide_);
|
| - this.linkEl_.removeEventListener('mousemove',
|
| - WindowTooltip.trackMouseMove_);
|
| - window.clearTimeout(this.timer);
|
| -
|
| - this.renderItems(tabs);
|
| - var rect = linkEl.getBoundingClientRect();
|
| - var bodyRect = document.body.getBoundingClientRect();
|
| - var rtl = document.documentElement.dir == 'rtl';
|
| -
|
| - this.tooltipEl.style.display = 'block';
|
| - var tooltipRect = this.tooltipEl.getBoundingClientRect();
|
| - var x, y;
|
| -
|
| - // When focused show below, like a drop down menu.
|
| - if (type == 'focus') {
|
| - x = rtl ?
|
| - rect.left + bodyRect.left + rect.width - this.tooltipEl.offsetWidth :
|
| - rect.left + bodyRect.left;
|
| - y = rect.top + bodyRect.top + rect.height;
|
| - } else {
|
| - x = bodyRect.left + (rtl ?
|
| - WindowTooltip.clientX - this.tooltipEl.offsetWidth :
|
| - WindowTooltip.clientX);
|
| - // Offset like a tooltip
|
| - y = 20 + WindowTooltip.clientY + bodyRect.top;
|
| - }
|
| -
|
| - // We need to ensure that the tooltip is inside the window viewport.
|
| - x = Math.min(x, bodyRect.width - tooltipRect.width);
|
| - x = Math.max(x, 0);
|
| - y = Math.min(y, bodyRect.height - tooltipRect.height);
|
| - y = Math.max(y, 0);
|
| -
|
| - this.tooltipEl.style.left = x + 'px';
|
| - this.tooltipEl.style.top = y + 'px';
|
| - },
|
| - handleMouseOut: function(e) {
|
| - // Don't hide when move to another item in the link.
|
| - var f = function(el) {
|
| - return el.tabItems !== undefined;
|
| - };
|
| - var el = findAncestor(e.target, f);
|
| - var relatedEl = findAncestor(e.relatedTarget, f);
|
| - if (el && el != relatedEl) {
|
| - this.hide();
|
| - }
|
| - },
|
| - hide: function() {
|
| - window.clearTimeout(this.timer);
|
| - window.removeEventListener('blur', this.boundHide_);
|
| - this.linkEl_.removeEventListener('mousemove',
|
| - WindowTooltip.trackMouseMove_);
|
| - this.linkEl_.removeEventListener('mouseout', this.boundHandleMouseOut_);
|
| - this.linkEl_.removeEventListener('blur', this.boundHide_);
|
| - this.linkEl_ = null;
|
| -
|
| - this.tooltipEl.style.display = 'none';
|
| - },
|
| - renderItems: function(tabs) {
|
| - var tooltip = this.tooltipEl;
|
| - tooltip.textContent = '';
|
| -
|
| - tabs.forEach(function(tab) {
|
| - var span = document.createElement('span');
|
| - span.className = 'item';
|
| - span.style.backgroundImage = url('chrome://favicon/' + tab.url);
|
| - span.dir = tab.direction;
|
| - span.textContent = tab.title;
|
| - tooltip.appendChild(span);
|
| - });
|
| - }
|
| -};
|
| -
|
| -var windowTooltip = new WindowTooltip($('window-tooltip'));
|
| -
|
| -window.addEventListener('load',
|
| - logEvent.bind(global, 'Tab.NewTabOnload', true));
|
| -
|
| -window.addEventListener('resize', handleWindowResize);
|
| -document.addEventListener('DOMContentLoaded',
|
| - logEvent.bind(global, 'Tab.NewTabDOMContentLoaded', true));
|
| -
|
| -// Whether or not we should send the initial 'GetSyncMessage' to the backend
|
| -// depends on the value of the attribue 'syncispresent' which the backend sets
|
| -// to indicate if there is code in the backend which is capable of processing
|
| -// this message. This attribute is loaded by the JSTemplate and therefore we
|
| -// must make sure we check the attribute after the DOM is loaded.
|
| -document.addEventListener('DOMContentLoaded',
|
| - callGetSyncMessageIfSyncIsPresent);
|
| -
|
| -/**
|
| - * The sync code is not yet built by default on all platforms so we have to
|
| - * make sure we don't send the initial sync message to the backend unless the
|
| - * backend told us that the sync code is present.
|
| - */
|
| -function callGetSyncMessageIfSyncIsPresent() {
|
| - if (document.documentElement.getAttribute('syncispresent') == 'true') {
|
| - chrome.send('GetSyncMessage');
|
| - }
|
| -}
|
| -
|
| -// Tooltip for elements that have text that overflows.
|
| -document.addEventListener('mouseover', function(e) {
|
| - // We don't want to do this while we are dragging because it makes things very
|
| - // janky
|
| - if (mostVisited.isDragging()) {
|
| - return;
|
| - }
|
| -
|
| - var el = findAncestor(e.target, function(el) {
|
| - return el.xtitle;
|
| - });
|
| - if (el && el.xtitle != el.title) {
|
| - if (el.scrollWidth > el.clientWidth) {
|
| - el.title = el.xtitle;
|
| - } else {
|
| - el.title = '';
|
| - }
|
| - }
|
| -});
|
| -
|
| -/**
|
| - * Makes links and buttons support a different underline color.
|
| - * @param {Node} node The node to search for links and buttons in.
|
| - */
|
| -function fixLinkUnderlines(node) {
|
| - var elements = node.querySelectorAll('a,button');
|
| - Array.prototype.forEach.call(elements, fixLinkUnderline);
|
| -}
|
| -
|
| -/**
|
| - * Wraps the content of an element in a a link-color span.
|
| - * @param {Element} el The element to wrap.
|
| - */
|
| -function fixLinkUnderline(el) {
|
| - var span = document.createElement('span');
|
| - span.className = 'link-color';
|
| - while (el.hasChildNodes()) {
|
| - span.appendChild(el.firstChild);
|
| - }
|
| - el.appendChild(span);
|
| -}
|
| -
|
| -updateAttribution();
|
| -
|
| -function initializeLogin() {
|
| - chrome.send('initializeLogin', []);
|
| -}
|
| -
|
| -function updateLogin(login) {
|
| - $('login-container').style.display = login ? 'block' : '';
|
| - if (login)
|
| - $('login-username').textContent = login;
|
| -}
|
| -
|
| -var mostVisited = new MostVisited(
|
| - $('most-visited-maxiview'),
|
| - document.querySelector('#most-visited .miniview'),
|
| - $('most-visited-menu'),
|
| - useSmallGrid(),
|
| - shownSections & Section.THUMB);
|
| -
|
| -function setMostVisitedPages(data, hasBlacklistedUrls) {
|
| - logEvent('received most visited pages');
|
| -
|
| - mostVisited.updateSettingsLink(hasBlacklistedUrls);
|
| - mostVisited.data = data;
|
| - mostVisited.layout();
|
| - layoutSections();
|
| -
|
| - // Remove class name in a timeout so that changes done in this JS thread are
|
| - // not animated.
|
| - window.setTimeout(function() {
|
| - mostVisited.ensureSmallGridCorrect();
|
| - maybeDoneLoading();
|
| - }, 1);
|
| -
|
| - if (localStrings.getString('serverpromo')) {
|
| - showPromoNotification();
|
| - }
|
| -
|
| -}
|
| -
|
| -function maybeDoneLoading() {
|
| - if (mostVisited.data && apps.loaded)
|
| - document.body.classList.remove('loading');
|
| -}
|
| -
|
| -function isDoneLoading() {
|
| - return !document.body.classList.contains('loading');
|
| -}
|
| -
|
| -document.addEventListener('DOMContentLoaded', function() {
|
| - cr.enablePlatformSpecificCSSRules();
|
| -
|
| - // Initialize the listener for the "hide this" link on the apps promo. We do
|
| - // this outside of getAppsCallback because it only needs to be done once per
|
| - // NTP load.
|
| - $('apps-promo-hide').addEventListener('click', function() {
|
| - chrome.send('hideAppsPromo', []);
|
| - document.documentElement.classList.remove('apps-promo-visible');
|
| - layoutSections();
|
| - });
|
| -});
|
|
|