|  | // 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. | 
|  |  | 
|  | (function() { | 
|  | var dumpToTextButton = $('dump-to-text'); | 
|  | var dataDump = $('data-dump'); | 
|  | dumpToTextButton.addEventListener('click', function(event) { | 
|  | // TODO(akalin): Add info like Chrome version, OS, date dumped, etc. | 
|  |  | 
|  | var data = ''; | 
|  | data += '======\n'; | 
|  | data += 'Status\n'; | 
|  | data += '======\n'; | 
|  | data += JSON.stringify(chrome.sync.aboutInfo, null, 2); | 
|  | data += '\n'; | 
|  | data += '\n'; | 
|  |  | 
|  | data += '=============\n'; | 
|  | data += 'Notifications\n'; | 
|  | data += '=============\n'; | 
|  | data += JSON.stringify(chrome.sync.notifications, null, 2); | 
|  | data += '\n'; | 
|  | data += '\n'; | 
|  |  | 
|  | data += '===\n'; | 
|  | data += 'Log\n'; | 
|  | data += '===\n'; | 
|  | data += JSON.stringify(chrome.sync.log.entries, null, 2); | 
|  | data += '\n'; | 
|  |  | 
|  | dataDump.textContent = data; | 
|  | }); | 
|  |  | 
|  | var allFields = [ | 
|  | 'ID', | 
|  | 'IS_UNSYNCED', | 
|  | 'IS_UNAPPLIED_UPDATE', | 
|  | 'BASE_VERSION', | 
|  | 'BASE_VERSION_TIME', | 
|  | 'SERVER_VERSION', | 
|  | 'SERVER_VERSION_TIME', | 
|  | 'PARENT_ID', | 
|  | 'SERVER_PARENT_ID', | 
|  | 'IS_DEL', | 
|  | 'SERVER_IS_DEL', | 
|  | 'modelType', | 
|  | 'SERVER_SPECIFICS', | 
|  | 'SPECIFICS', | 
|  | ]; | 
|  |  | 
|  | function versionToDateString(version) { | 
|  | // TODO(mmontgomery): ugly? Hacky? Is there a better way? | 
|  | var epochLength = Date.now().toString().length; | 
|  | var epochTime = parseInt(version.slice(0, epochLength)); | 
|  | var date = new Date(epochTime); | 
|  | return date.toString(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {!Object} node A JavaScript represenation of a sync entity. | 
|  | * @return {string} A string representation of the sync entity. | 
|  | */ | 
|  | function serializeNode(node) { | 
|  | return allFields.map(function(field) { | 
|  | var fieldVal; | 
|  | if (field == 'SERVER_VERSION_TIME') { | 
|  | var version = node['SERVER_VERSION']; | 
|  | fieldVal = versionToDateString(version); | 
|  | } if (field == 'BASE_VERSION_TIME') { | 
|  | var version = node['BASE_VERSION']; | 
|  | fieldVal = versionToDateString(version); | 
|  | } else if ((field == 'SERVER_SPECIFICS' || field == 'SPECIFICS') && | 
|  | (!$('include-specifics').checked)) { | 
|  | fieldVal = 'REDACTED'; | 
|  | } else if ((field == 'SERVER_SPECIFICS' || field == 'SPECIFICS') && | 
|  | $('include-specifics').checked) { | 
|  | fieldVal = JSON.stringify(node[field]); | 
|  | } else { | 
|  | fieldVal = node[field]; | 
|  | } | 
|  | return fieldVal; | 
|  | }); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {string} type The name of a sync model type. | 
|  | * @return {boolean} True if the type's checkbox is selected. | 
|  | */ | 
|  | function isSelectedDatatype(type) { | 
|  | var typeCheckbox = $(type); | 
|  | // Some types, such as 'Top level folder', appear in the list of nodes | 
|  | // but not in the list of selectable items. | 
|  | if (typeCheckbox == null) { | 
|  | return false; | 
|  | } | 
|  | return typeCheckbox.checked; | 
|  | } | 
|  |  | 
|  | function makeBlobUrl(data) { | 
|  | var textBlob = new Blob([data], {type: 'octet/stream'}); | 
|  | var blobUrl = window.URL.createObjectURL(textBlob); | 
|  | return blobUrl; | 
|  | } | 
|  |  | 
|  | function makeDownloadName() { | 
|  | // Format is sync-data-dump-$epoch-$year-$month-$day-$OS.csv. | 
|  | var now = new Date(); | 
|  | var friendlyDate = [now.getFullYear(), | 
|  | now.getMonth() + 1, | 
|  | now.getDate()].join('-'); | 
|  | var name = ['sync-data-dump', | 
|  | friendlyDate, | 
|  | Date.now(), | 
|  | navigator.platform].join('-'); | 
|  | return [name, 'csv'].join('.'); | 
|  | } | 
|  |  | 
|  | function makeDateUserAgentHeader() { | 
|  | var now = new Date(); | 
|  | var userAgent = window.navigator.userAgent; | 
|  | var dateUaHeader = [now.toISOString(), userAgent].join(','); | 
|  | return dateUaHeader; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Builds a summary of current state and exports it as a downloaded file. | 
|  | * | 
|  | * @param {!Array<{type: string, nodes: !Array<!Object>}>} nodesMap | 
|  | *     Summary of local state by model type. | 
|  | */ | 
|  | function triggerDataDownload(nodesMap) { | 
|  | // Prepend a header with ISO date and useragent. | 
|  | var output = [makeDateUserAgentHeader()]; | 
|  | output.push('====='); | 
|  |  | 
|  | var aboutInfo = JSON.stringify(chrome.sync.aboutInfo, null, 2); | 
|  | output.push(aboutInfo); | 
|  |  | 
|  | // Filter out non-selected types. | 
|  | var selectedTypesNodes = nodesMap.filter(function(x) { | 
|  | return isSelectedDatatype(x.type); | 
|  | }); | 
|  |  | 
|  | // Serialize the remaining nodes and add them to the output. | 
|  | selectedTypesNodes.forEach(function(typeNodes) { | 
|  | output.push('====='); | 
|  | output.push(typeNodes.nodes.map(serializeNode).join('\n')); | 
|  | }); | 
|  |  | 
|  | output = output.join('\n'); | 
|  |  | 
|  | var anchor = $('dump-to-file-anchor'); | 
|  | anchor.href = makeBlobUrl(output); | 
|  | anchor.download = makeDownloadName(); | 
|  | anchor.click(); | 
|  | } | 
|  |  | 
|  | function createTypesCheckboxes(types) { | 
|  | var containerElt = $('node-type-checkboxes'); | 
|  |  | 
|  | types.map(function(type) { | 
|  | var div = document.createElement('div'); | 
|  |  | 
|  | var checkbox = document.createElement('input'); | 
|  | checkbox.id = type; | 
|  | checkbox.type = 'checkbox'; | 
|  | checkbox.checked = 'yes'; | 
|  | div.appendChild(checkbox); | 
|  |  | 
|  | var label = document.createElement('label'); | 
|  | // Assigning to label.for doesn't work. | 
|  | label.setAttribute('for', type); | 
|  | label.innerText = type; | 
|  | div.appendChild(label); | 
|  |  | 
|  | containerElt.appendChild(div); | 
|  | }); | 
|  | } | 
|  |  | 
|  | function onReceivedListOfTypes(e) { | 
|  | var types = e.details.types; | 
|  | types.sort(); | 
|  | createTypesCheckboxes(types); | 
|  | chrome.sync.events.removeEventListener( | 
|  | 'onReceivedListOfTypes', | 
|  | onReceivedListOfTypes); | 
|  | } | 
|  |  | 
|  | document.addEventListener('DOMContentLoaded', function() { | 
|  | chrome.sync.events.addEventListener( | 
|  | 'onReceivedListOfTypes', | 
|  | onReceivedListOfTypes); | 
|  | chrome.sync.requestListOfTypes(); | 
|  | }); | 
|  |  | 
|  | var dumpToFileLink = $('dump-to-file'); | 
|  | dumpToFileLink.addEventListener('click', function(event) { | 
|  | chrome.sync.getAllNodes(triggerDataDownload); | 
|  | }); | 
|  | })(); |