blob: 8d1ff3d1be0fd28b86ca19bc968981ae5aae94e9 [file] [log] [blame]
// 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.
// require: cr.js
// require: cr/ui.js
// require: cr/ui/tree.js
cr.define('chrome.sync', function() {
/**
* Gets all children of the given node and passes it to the given
* callback.
* @param {string} id The id whose children we want.
* @param {function(Array.<!Object>)} callback The callback to call
* with the list of children summaries.
*/
function getSyncNodeChildrenSummaries(id, callback) {
var timer = chrome.sync.makeTimer();
chrome.sync.getChildNodeIds(id, function(childNodeIds) {
console.debug('getChildNodeIds took ' +
timer.elapsedSeconds + 's to retrieve ' +
childNodeIds.length + ' ids');
timer = chrome.sync.makeTimer();
chrome.sync.getNodeSummariesById(
childNodeIds, function(childrenSummaries) {
console.debug('getNodeSummariesById took ' +
timer.elapsedSeconds + 's to retrieve summaries for ' +
childrenSummaries.length + ' nodes');
callback(childrenSummaries);
});
});
}
/**
* Creates a new sync node tree item.
* @param {{id: string, title: string, isFolder: boolean}}
* nodeSummary The nodeSummary object for the node (as returned
* by chrome.sync.getNodeSummariesById()).
* @constructor
* @extends {cr.ui.TreeItem}
*/
var SyncNodeTreeItem = function(nodeSummary) {
var treeItem = new cr.ui.TreeItem({
id_: nodeSummary.id
});
treeItem.__proto__ = SyncNodeTreeItem.prototype;
treeItem.label = nodeSummary.title;
if (nodeSummary.isFolder) {
treeItem.mayHaveChildren_ = true;
// Load children asynchronously on expand.
// TODO(akalin): Add a throbber while loading?
treeItem.triggeredLoad_ = false;
treeItem.addEventListener('expand',
treeItem.handleExpand_.bind(treeItem));
} else {
treeItem.classList.add('leaf');
}
return treeItem;
};
SyncNodeTreeItem.prototype = {
__proto__: cr.ui.TreeItem.prototype,
/**
* Retrieves the details for this node.
* @param {function(Object)} callback The callback that will be
* called with the node details, or null if it could not be
* retrieved.
*/
getDetails: function(callback) {
chrome.sync.getNodeDetailsById([this.id_], function(nodeDetails) {
callback(nodeDetails[0] || null);
});
},
handleExpand_: function(event) {
if (!this.triggeredLoad_) {
getSyncNodeChildrenSummaries(this.id_, this.addChildNodes_.bind(this));
this.triggeredLoad_ = true;
}
},
/**
* Adds children from the list of children summaries.
* @param {Array.<{id: string, title: string, isFolder: boolean}>}
* childrenSummaries The list of children summaries with which
* to create the child nodes.
*/
addChildNodes_: function(childrenSummaries) {
var timer = chrome.sync.makeTimer();
for (var i = 0; i < childrenSummaries.length; ++i) {
var childTreeItem = new SyncNodeTreeItem(childrenSummaries[i]);
this.add(childTreeItem);
}
console.debug('adding ' + childrenSummaries.length +
' children took ' + timer.elapsedSeconds + 's');
}
};
/**
* Updates the node detail view with the details for the given node.
* @param {!Object} nodeDetails The details for the node we want
* to display.
*/
function updateNodeDetailView(nodeDetails) {
var nodeBrowser = document.getElementById('node-browser');
// TODO(akalin): Write a nicer detail viewer.
nodeDetails.entry = JSON.stringify(nodeDetails.entry, null, 2);
jstProcess(new JsEvalContext(nodeDetails), nodeBrowser);
}
/**
* Creates a new sync node tree.
* @param {Object=} opt_propertyBag Optional properties.
* @constructor
* @extends {cr.ui.Tree}
*/
var SyncNodeTree = cr.ui.define('tree');
SyncNodeTree.prototype = {
__proto__: cr.ui.Tree.prototype,
decorate: function() {
cr.ui.Tree.prototype.decorate.call(this);
this.addEventListener('change', this.handleChange_.bind(this));
chrome.sync.getRootNodeDetails(this.makeRoot_.bind(this));
},
/**
* Creates the root of the tree.
* @param {{id: string, title: string, isFolder: boolean}}
* rootNodeSummary The summary info for the root node.
*/
makeRoot_: function(rootNodeSummary) {
// The root node usually doesn't have a title.
rootNodeSummary.title = rootNodeSummary.title || 'Root';
var rootTreeItem = new SyncNodeTreeItem(rootNodeSummary);
this.add(rootTreeItem);
},
handleChange_: function(event) {
if (this.selectedItem) {
this.selectedItem.getDetails(updateNodeDetailView);
}
}
};
function decorateSyncNodeBrowser(syncNodeBrowser) {
cr.ui.decorate(syncNodeBrowser, SyncNodeTree);
}
// This is needed because JsTemplate (which is needed by
// updateNodeDetailView) is loaded at the end of the file after
// everything else.
//
// TODO(akalin): Remove dependency on JsTemplate and get rid of
// this.
var domLoaded = false;
var pendingSyncNodeBrowsers = [];
function decorateSyncNodeBrowserAfterDOMLoad(id) {
var e = document.getElementById(id);
if (domLoaded) {
decorateSyncNodeBrowser(e);
} else {
pendingSyncNodeBrowsers.push(e);
}
}
document.addEventListener('DOMContentLoaded', function() {
for (var i = 0; i < pendingSyncNodeBrowsers.length; ++i) {
decorateSyncNodeBrowser(pendingSyncNodeBrowsers[i]);
}
domLoaded = true;
});
return {
decorateSyncNodeBrowser: decorateSyncNodeBrowserAfterDOMLoad
};
});