blob: a255b22ac38beb1bb1f83c453f80a52937ee6521 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* Converts a number in bytes to a string in megabytes split by comma into
* three digit block.
* @param {number} bytes The number in bytes.
* @return {string} Formatted string in megabytes.
*/
function toMegaByteString(bytes) {
const mb = Math.floor(bytes / (1 << 20));
return mb.toString().replace(
/\d+?(?=(\d{3})+$)/g, // Digit sequence (\d+) followed (?=) by 3n digits.
function(three_digit_block) {
return three_digit_block + ',';
});
}
/**
* Updates the Drive related Preferences section.
* @param {Array} preferences List of dictionaries describing preferences.
*/
function updateDriveRelatedPreferences(preferences) {
const ul = $('drive-related-preferences');
updateKeyValueList(ul, preferences);
}
/**
* Updates the Connection Status section.
* @param {Object} connStatus Dictionary containing connection status.
*/
function updateConnectionStatus(connStatus) {
$('connection-status').textContent = connStatus['status'];
$('push-notification-enabled').textContent =
connStatus['push-notification-enabled'];
}
/**
* Updates the Path Configurations section.
* @param {Array} paths List of dictionaries describing paths.
*/
function updatePathConfigurations(paths) {
const ul = $('path-configurations');
updateKeyValueList(ul, paths);
}
/**
* Updates the GCache Contents section.
* @param {Array} gcacheContents List of dictionaries describing metadata
* of files and directories under the GCache directory.
* @param {Object} gcacheSummary Dictionary of summary of GCache.
*/
function updateGCacheContents(gcacheContents, gcacheSummary) {
const tbody = $('gcache-contents');
for (let i = 0; i < gcacheContents.length; i++) {
const entry = gcacheContents[i];
const tr = document.createElement('tr');
// Add some suffix based on the type.
let path = entry.path;
if (entry.is_directory) {
path += '/';
} else if (entry.is_symbolic_link) {
path += '@';
}
tr.appendChild(createElementFromText('td', path));
tr.appendChild(createElementFromText('td', entry.size));
tr.appendChild(createElementFromText('td', entry.last_modified));
tr.appendChild(createElementFromText('td', entry.permission));
tbody.appendChild(tr);
}
$('gcache-summary-total-size').textContent =
toMegaByteString(gcacheSummary['total_size']);
}
/**
* Updates the Cache Contents section.
* @param {Object} cacheEntry Dictionary describing a cache entry.
* The function is called from the C++ side repeatedly.
*/
function updateCacheContents(cacheEntry) {
const tr = document.createElement('tr');
tr.appendChild(createElementFromText('td', cacheEntry.local_id));
tr.appendChild(createElementFromText('td', cacheEntry.md5));
tr.appendChild(createElementFromText('td', cacheEntry.is_present));
tr.appendChild(createElementFromText('td', cacheEntry.is_pinned));
tr.appendChild(createElementFromText('td', cacheEntry.is_dirty));
$('cache-contents').appendChild(tr);
}
function updateBulkPinningVisible(enabled) {
$('bulk-pinning-visible').checked = enabled;
}
function updateVerboseLogging(enabled) {
$('verbose-logging-toggle').checked = enabled;
}
function updateMirroring(enabled) {
$('mirroring-toggle').checked = enabled;
}
function updateBulkPinning(enabled) {
$('bulk-pinning-toggle').checked = enabled;
}
function onBulkPinningProgress(progress) {
updateBulkPinning(progress.enabled);
$('bulk-pinning-stage').innerText = progress.stage;
$('bulk-pinning-free-space').innerText = progress.free_space;
$('bulk-pinning-required-space').innerText = progress.required_space;
$('bulk-pinning-bytes-to-pin').innerText = progress.bytes_to_pin;
$('bulk-pinning-pinned-bytes').innerText = progress.pinned_bytes;
$('bulk-pinning-pinned-bytes-percent').innerText =
progress.pinned_bytes_percent;
$('bulk-pinning-files-to-pin').innerText = progress.files_to_pin;
$('bulk-pinning-pinned-files').innerText = progress.pinned_files;
$('bulk-pinning-pinned-files-percent').innerText =
progress.pinned_files_percent;
$('bulk-pinning-failed-files').innerText = progress.failed_files;
$('bulk-pinning-syncing-files').innerText = progress.syncing_files;
$('bulk-pinning-skipped-items').innerText = progress.skipped_items;
$('bulk-pinning-listed-items').innerText = progress.listed_items;
$('bulk-pinning-listed-dirs').innerText = progress.listed_dirs;
$('bulk-pinning-listed-files').innerText = progress.listed_files;
$('bulk-pinning-listed-docs').innerText = progress.listed_docs;
$('bulk-pinning-listed-shortcuts').innerText = progress.listed_shortcuts;
$('bulk-pinning-active-queries').innerText = progress.active_queries;
$('bulk-pinning-max-active-queries').innerText = progress.max_active_queries;
$('bulk-pinning-time-spent-listing-items').innerText =
progress.time_spent_listing_items;
$('bulk-pinning-time-spent-pinning-files').innerText =
progress.time_spent_pinning_files;
$('bulk-pinning-remaining-time').innerText = progress.remaining_time;
}
function updateStartupArguments(args) {
$('startup-arguments-input').value = args;
}
/**
* Updates the Local Storage summary.
* @param {Object} localStorageSummary Dictionary describing the status of local
* stogage.
*/
function updateLocalStorageUsage(localStorageSummary) {
const freeSpaceInMB = toMegaByteString(localStorageSummary.free_space);
$('local-storage-freespace').innerText = freeSpaceInMB;
}
/**
* Updates the summary about in-flight operations.
* @param {Array} inFlightOperations List of dictionaries describing the status
* of in-flight operations.
*/
function updateInFlightOperations(inFlightOperations) {
const container = $('in-flight-operations-contents');
// Reset the table. Remove children in reverse order. Otherwides each
// existingNodes[i] changes as a side effect of removeChild.
const existingNodes = container.childNodes;
for (let i = existingNodes.length - 1; i >= 0; i--) {
const node = existingNodes[i];
if (node.className === 'in-flight-operation') {
container.removeChild(node);
}
}
// Add in-flight operations.
for (let i = 0; i < inFlightOperations.length; i++) {
const operation = inFlightOperations[i];
const tr = document.createElement('tr');
tr.className = 'in-flight-operation';
tr.appendChild(createElementFromText('td', operation.id));
tr.appendChild(createElementFromText('td', operation.type));
tr.appendChild(createElementFromText('td', operation.file_path));
tr.appendChild(createElementFromText('td', operation.state));
let progress = operation.progress_current + '/' + operation.progress_total;
if (operation.progress_total > 0) {
const percent =
operation.progress_current / operation.progress_total * 100;
progress += ' (' + Math.round(percent) + '%)';
}
tr.appendChild(createElementFromText('td', progress));
container.appendChild(tr);
}
}
/**
* Updates the summary about about resource.
* @param {Object} aboutResource Dictionary describing about resource.
*/
function updateAboutResource(aboutResource) {
const quotaTotalInMb = toMegaByteString(aboutResource['account-quota-total']);
const quotaUsedInMb = toMegaByteString(aboutResource['account-quota-used']);
$('account-quota-info').textContent =
quotaUsedInMb + ' / ' + quotaTotalInMb + ' (MB)';
$('account-largest-changestamp-remote').textContent =
aboutResource['account-largest-changestamp-remote'];
$('root-resource-id').textContent = aboutResource['root-resource-id'];
}
/*
* Updates the summary about delta update status.
* @param {Object} deltaUpdateStatus Dictionary describing delta update status.
*/
function updateDeltaUpdateStatus(deltaUpdateStatus) {
const itemContainer = $('delta-update-status');
for (let i = 0; i < deltaUpdateStatus['items'].length; i++) {
const update = deltaUpdateStatus['items'][i];
const tr = document.createElement('tr');
tr.className = 'delta-update';
tr.appendChild(createElementFromText('td', update.id));
tr.appendChild(createElementFromText('td', update.root_entry_path));
const startPageToken = update.start_page_token;
tr.appendChild(createElementFromText(
'td',
startPageToken + (startPageToken ? ' (loaded)' : ' (not loaded)')));
tr.appendChild(createElementFromText('td', update.last_check_time));
tr.appendChild(createElementFromText('td', update.last_check_result));
tr.appendChild(createElementFromText('td', update.refreshing));
itemContainer.appendChild(tr);
}
}
/**
* Updates the event log section.
* @param {Array} log Array of events.
*/
function updateEventLog(log) {
const ul = $('event-log');
updateKeyValueList(ul, log);
}
/**
* Updates the service log section.
* @param {Array} log Log lines.
*/
function updateServiceLog(log) {
const ul = $('service-log');
updateKeyValueList(ul, log);
}
/**
* Updates the service log section.
* @param {Array} log Log lines.
*/
function updateOtherServiceLogsUrl(url) {
const link = $('other-logs');
link.setAttribute('href', url);
}
/**
* Adds a new row to the syncing paths table upon successful completion.
* @param {string} path The path that was synced.
* @param {string} status The drive::FileError as a string without the
* "FILE_ERROR_" prefix.
*/
function onAddSyncPath(path, status) {
$('mirroring-path-status').textContent = status;
if (status !== 'OK') {
console.error(`Cannot add sync path '${path}': ${status}`);
return;
}
// Avoid adding paths to the table if they already exist.
if ($(`mirroring-${path}`)) {
return;
}
const newRow = document.createElement('tr');
newRow.id = `mirroring-${path}`;
const deleteButton = createElementFromText('button', 'Delete');
deleteButton.addEventListener('click', function(e) {
e.preventDefault();
chrome.send('removeSyncPath', [path]);
});
const deleteCell = document.createElement('td');
deleteCell.appendChild(deleteButton);
newRow.appendChild(deleteCell);
const pathCell = createElementFromText('td', path);
newRow.appendChild(pathCell);
$('mirror-sync-paths').appendChild(newRow);
}
/**
* Remove a path from the syncing table.
* @param {string} path The path that was synced.
* @param {string} status The drive::FileError as a string without the
* "FILE_ERROR_" prefix.
*/
function onRemoveSyncPath(path, status) {
if (status !== 'OK') {
console.error(`Cannot remove sync path '${path}': ${status}`);
return;
}
if (!$(`mirroring-${path}`)) {
return;
}
$(`mirroring-${path}`).remove();
}
/**
* Creates an element named |elementName| containing the content |text|.
* @param {string} elementName Name of the new element to be created.
* @param {string} text Text to be contained in the new element.
* @return {HTMLElement} The newly created HTML element.
*/
function createElementFromText(elementName, text) {
const element = document.createElement(elementName);
element.appendChild(document.createTextNode(text));
return element;
}
/**
* Updates <ul> element with the given key-value list.
* @param {HTMLElement} ul <ul> element to be modified.
* @param {Array} list List of dictionaries containing 'key', 'value' (optional)
* and 'class' (optional). For each element <li> element with specified class is
* created.
*/
function updateKeyValueList(ul, list) {
for (let i = 0; i < list.length; i++) {
const item = list[i];
let text = item.key;
if (item.value !== '') {
text += ': ' + item.value;
}
const li = createElementFromText('li', text);
if (item.class) {
li.classList.add(item.class);
}
ul.appendChild(li);
}
}
function updateStartupArgumentsStatus(success) {
$('arguments-status-text').textContent = (success ? 'success' : 'failed');
}
/**
* Updates the text next to the 'reset' button to update the status.
* @param {boolean} success whether or not resetting has succeeded.
*/
function updateResetStatus(success) {
$('reset-status-text').textContent = (success ? 'success' : 'failed');
}
/**
* Makes up-to-date table of contents.
*/
function updateToc() {
const toc = $('toc');
while (toc.firstChild) {
toc.removeChild(toc.firstChild);
}
const sections = document.getElementsByTagName('section');
for (let i = 0; i < sections.length; i++) {
const section = sections[i];
if (!section.hidden) {
const header = section.getElementsByTagName('h2')[0];
const a = createElementFromText('a', header.textContent);
a.href = '#' + section.id;
const li = document.createElement('li');
li.appendChild(a);
toc.appendChild(li);
}
}
}
/**
* Shows or hides a section.
* @param {string} section Which section to change.
* @param {boolean} enabled Whether to enable.
*/
function setSectionEnabled(section, enable) {
const element = $(section);
if (element.hidden !== !enable) {
element.hidden = !enable;
updateToc();
}
}
function onZipDone(success) {
$('button-export-logs').removeAttribute('disabled');
}
document.addEventListener('DOMContentLoaded', () => {
chrome.send('pageLoaded');
updateToc();
$('bulk-pinning-visible')
.addEventListener(
'change',
e => chrome.send('setBulkPinningVisible', [e.target.checked]));
$('verbose-logging-toggle')
.addEventListener(
'change',
e => chrome.send('setVerboseLoggingEnabled', [e.target.checked]));
$('mirroring-toggle')
.addEventListener(
'change',
e => chrome.send('setMirroringEnabled', [e.target.checked]));
$('bulk-pinning-toggle')
.addEventListener(
'change',
e => chrome.send('setBulkPinningEnabled', [e.target.checked]));
$('startup-arguments-form').addEventListener('submit', e => {
e.preventDefault();
$('arguments-status-text').textContent = 'applying...';
chrome.send('setStartupArguments', [$('startup-arguments-input').value]);
});
$('mirror-path-form').addEventListener('submit', e => {
e.preventDefault();
$('mirroring-path-status').textContent = 'adding...';
chrome.send('addSyncPath', [$('mirror-path-input').value]);
});
$('button-enable-tracing')
.addEventListener('click', () => chrome.send('enableTracing'));
$('button-disable-tracing')
.addEventListener('click', () => chrome.send('disableTracing'));
$('button-enable-networking')
.addEventListener('click', () => chrome.send('enableNetworking'));
$('button-disable-networking')
.addEventListener('click', () => chrome.send('disableNetworking'));
$('button-enable-force-pause-syncing')
.addEventListener('click', () => chrome.send('enableForcePauseSyncing'));
$('button-disable-force-pause-syncing')
.addEventListener('click', () => chrome.send('disableForcePauseSyncing'));
$('button-dump-account-settings')
.addEventListener('click', () => chrome.send('dumpAccountSettings'));
$('button-load-account-settings')
.addEventListener('click', () => chrome.send('loadAccountSettings'));
$('button-restart-drive')
.addEventListener('click', () => chrome.send('restartDrive'));
$('button-reset-drive-filesystem').addEventListener('click', () => {
if (window.confirm(
'Warning: Any local changes not yet uploaded to the Drive server ' +
'will be lost, continue?')) {
$('reset-status-text').textContent = 'resetting...';
chrome.send('resetDriveFileSystem');
}
});
$('button-export-logs').addEventListener('click', () => {
$('button-export-logs').setAttribute('disabled', 'true');
chrome.send('zipLogs');
});
window.setInterval(() => chrome.send('periodicUpdate'), 1000);
});