blob: 47c1ab9392aa340da1810351a8a6ece4dc367342 [file] [log] [blame]
// Copyright (c) 2012 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.
cr.define('chrome.sync.about_tab', function() {
// Contains the latest snapshot of sync about info.
chrome.sync.aboutInfo = {};
function highlightIfChanged(node, oldVal, newVal) {
function clearHighlight() {
this.removeAttribute('highlighted');
}
var oldStr = oldVal.toString();
var newStr = newVal.toString();
if (oldStr != '' && oldStr != newStr) {
// Note the addListener function does not end up creating duplicate
// listeners. There can be only one listener per event at a time.
// Reference: https://developer.mozilla.org/en/DOM/element.addEventListener
node.addEventListener('webkitAnimationEnd', clearHighlight, false);
node.setAttribute('highlighted', '');
}
}
function refreshAboutInfo(aboutInfo) {
chrome.sync.aboutInfo = aboutInfo;
var aboutInfoDiv = $('about-info');
jstProcess(new JsEvalContext(aboutInfo), aboutInfoDiv);
}
function onAboutInfoUpdatedEvent(e) {
refreshAboutInfo(e.details);
}
/**
* Helper to determine if an element is scrolled to its bottom limit.
* @param {Element} elem element to check
* @return {boolean} true if the element is scrolled to the bottom
*/
function isScrolledToBottom(elem) {
return elem.scrollHeight - elem.scrollTop == elem.clientHeight;
}
/**
* Helper to scroll an element to its bottom limit.
* @param {Element} elem element to be scrolled
*/
function scrollToBottom(elem) {
elem.scrollTop = elem.scrollHeight - elem.clientHeight;
}
/** Container for accumulated sync protocol events. */
var protocolEvents = [];
/** We may receive re-delivered events. Keep a record of ones we've seen. */
var knownEventTimestamps = {};
/**
* Callback for incoming protocol events.
* @param {Event} e The protocol event.
*/
function onReceivedProtocolEvent(e) {
var details = e.details;
// Return early if we've seen this event before. Assumes that timestamps
// are sufficiently high resolution to uniquely identify an event.
if (knownEventTimestamps.hasOwnProperty(details.time)) {
return;
}
knownEventTimestamps[details.time] = true;
protocolEvents.push(details);
var trafficContainer = $('traffic-event-container');
// Scroll to the bottom if we were already at the bottom. Otherwise, leave
// the scrollbar alone.
var shouldScrollDown = isScrolledToBottom(trafficContainer);
var context = new JsEvalContext({ events: protocolEvents });
jstProcess(context, trafficContainer);
if (shouldScrollDown)
scrollToBottom(trafficContainer);
}
/**
* Initializes state and callbacks for the protocol event log UI.
*/
function initProtocolEventLog() {
chrome.sync.events.addEventListener(
'onProtocolEvent', onReceivedProtocolEvent);
// Make the prototype jscontent element disappear.
jstProcess({}, $('traffic-event-container'));
}
/**
* Initializes listeners for status dump and import UI.
*/
function initStatusDumpButton() {
$('status-data').hidden = true;
var dumpStatusButton = $('dump-status');
dumpStatusButton.addEventListener('click', function(event) {
var aboutInfo = chrome.sync.aboutInfo;
if (!$('include-ids').checked) {
aboutInfo.details = chrome.sync.aboutInfo.details.filter(function(el) {
return !el.is_sensitive;
});
}
var data = '';
data += new Date().toString() + '\n';
data += '======\n';
data += 'Status\n';
data += '======\n';
data += JSON.stringify(aboutInfo, null, 2) + '\n';
$('status-text').value = data;
$('status-data').hidden = false;
});
var importStatusButton = $('import-status');
importStatusButton.addEventListener('click', function(event) {
$('status-data').hidden = false;
if ($('status-text').value.length == 0) {
$('status-text').value =
'Paste sync status dump here then click import.';
return;
}
// First remove any characters before the '{'.
var data = $('status-text').value;
var firstBrace = data.indexOf('{');
if (firstBrace < 0) {
$('status-text').value = 'Invalid sync status dump.';
return;
}
data = data.substr(firstBrace);
// Remove listeners to prevent sync events from overwriting imported data.
chrome.sync.events.removeEventListener(
'onAboutInfoUpdated',
onAboutInfoUpdatedEvent);
var aboutInfo = JSON.parse(data);
refreshAboutInfo(aboutInfo);
});
}
/**
* Toggles the given traffic event entry div's "expanded" state.
* @param {MouseEvent} e the click event that triggered the toggle.
*/
function expandListener(e) {
e.target.classList.toggle('traffic-event-entry-expanded');
}
/**
* Attaches a listener to the given traffic event entry div.
* @param {HTMLElement} element the element to attach the listener to.
*/
function addExpandListener(element) {
element.addEventListener('click', expandListener, false);
}
function onLoad() {
initStatusDumpButton();
initProtocolEventLog();
chrome.sync.events.addEventListener(
'onAboutInfoUpdated',
onAboutInfoUpdatedEvent);
// Register to receive a stream of event notifications.
chrome.sync.registerForEvents();
// Request an about info update event to initialize the page.
chrome.sync.requestUpdatedAboutInfo();
}
return {
onLoad: onLoad,
addExpandListener: addExpandListener,
highlightIfChanged: highlightIfChanged
};
});
document.addEventListener(
'DOMContentLoaded', chrome.sync.about_tab.onLoad, false);