// 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() { // Allow platform specific CSS rules. // // TODO(akalin): BMM and options page does something similar, too. // Move this to util.js. if (cr.isWindows) document.documentElement.setAttribute('os', 'win'); // TODO(akalin): Create SyncNodeTree/SyncNodeTreeItem classes that // hide all these details. /** * Gets all children of the given node and passes it to the given * callback. * @param {Object} nodeInfo The info for the node whose children we * want. * @param {Function} callback The callback to call with the list of * children. */ function getSyncNodeChildren(nodeInfo, callback) { var children = []; function processChildInfo(childNodeInfo) { if (!childNodeInfo) { callback(children); return; } children.push(childNodeInfo); chrome.sync.getNodeById(childNodeInfo.successorId, processChildInfo); }; chrome.sync.getNodeById(nodeInfo.firstChildId, processChildInfo); } /** * Makes a tree item from the given node info. * @param {dictionary} nodeInfo The node info to create the tree * item from. * @return {cr.ui.TreeItem} The created tree item. */ function makeNodeTreeItem(nodeInfo) { var treeItem = new cr.ui.TreeItem({ label: nodeInfo.title, detail: nodeInfo }); if (nodeInfo.isFolder) { treeItem.mayHaveChildren_ = true; // Load children asynchronously on expand. // TODO(akalin): Add a throbber while loading? treeItem.triggeredLoad_ = false; treeItem.addEventListener('expand', function(event) { if (!treeItem.triggeredLoad_) { getSyncNodeChildren(nodeInfo, function(children) { for (var i = 0; i < children.length; ++i) { var childTreeItem = makeNodeTreeItem(children[i]); treeItem.add(childTreeItem); } }); treeItem.triggeredLoad_ = true; } }); } else { treeItem.classList.add('leaf'); } return treeItem; } /** * Updates the node detail view with the info for the given node. * @param {dictionary} nodeInfo The info for the node we want to * display. */ function updateNodeDetailView(nodeInfo) { var nodeBrowser = document.getElementById('node-browser'); // TODO(akalin): Get rid of this hack. if (typeof nodeInfo.entry != 'string') nodeInfo.entry = JSON.stringify(nodeInfo.entry, null, 2); jstProcess(new JsEvalContext(nodeInfo), nodeBrowser); } function decorateSyncNodeBrowser(syncNodeBrowser) { cr.ui.decorate(syncNodeBrowser, cr.ui.Tree); syncNodeBrowser.addEventListener('change', function(event) { if (syncNodeBrowser.selectedItem) updateNodeDetailView(syncNodeBrowser.selectedItem.detail); }); chrome.sync.getRootNode(function(rootNodeInfo) { var rootTreeItem = makeNodeTreeItem(rootNodeInfo); rootTreeItem.label = 'Root'; syncNodeBrowser.add(rootTreeItem); }); } // 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 }; });