| // 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); |
| }); |
| })(); |