1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // require: cr.js 6 // require: cr/ui.js 7 // require: cr/ui/tree.js 8 9 cr.define('chrome.sync', function() { 10 // Allow platform specific CSS rules. 11 // 12 // TODO(akalin): BMM and options page does something similar, too. 13 // Move this to util.js. 14 if (cr.isWindows) 15 document.documentElement.setAttribute('os', 'win'); 16 17 // TODO(akalin): Create SyncNodeTree/SyncNodeTreeItem classes that 18 // hide all these details. 19 20 /** 21 * Gets all children of the given node and passes it to the given 22 * callback. 23 * @param {Object} nodeInfo The info for the node whose children we 24 * want. 25 * @param {Function} callback The callback to call with the list of 26 * children. 27 */ 28 function getSyncNodeChildren(nodeInfo, callback) { 29 var children = []; 30 function processChildInfo(childNodeInfo) { 31 if (!childNodeInfo) { 32 callback(children); 33 return; 34 } 35 children.push(childNodeInfo); 36 chrome.sync.getNodeById(childNodeInfo.successorId, processChildInfo); 37 }; 38 chrome.sync.getNodeById(nodeInfo.firstChildId, processChildInfo); 39 } 40 41 /** 42 * Makes a tree item from the given node info. 43 * @param {dictionary} nodeInfo The node info to create the tree 44 * item from. 45 * @return {cr.ui.TreeItem} The created tree item. 46 */ 47 function makeNodeTreeItem(nodeInfo) { 48 var treeItem = new cr.ui.TreeItem({ 49 label: nodeInfo.title, 50 detail: nodeInfo 51 }); 52 53 if (nodeInfo.isFolder) { 54 treeItem.mayHaveChildren_ = true; 55 56 // Load children asynchronously on expand. 57 // TODO(akalin): Add a throbber while loading? 58 treeItem.triggeredLoad_ = false; 59 treeItem.addEventListener('expand', function(event) { 60 if (!treeItem.triggeredLoad_) { 61 getSyncNodeChildren(nodeInfo, function(children) { 62 for (var i = 0; i < children.length; ++i) { 63 var childTreeItem = makeNodeTreeItem(children[i]); 64 treeItem.add(childTreeItem); 65 } 66 }); 67 treeItem.triggeredLoad_ = true; 68 } 69 }); 70 } else { 71 treeItem.classList.add('leaf'); 72 } 73 return treeItem; 74 } 75 76 /** 77 * Updates the node detail view with the info for the given node. 78 * @param {dictionary} nodeInfo The info for the node we want to 79 * display. 80 */ 81 function updateNodeDetailView(nodeInfo) { 82 var nodeBrowser = document.getElementById('node-browser'); 83 // TODO(akalin): Get rid of this hack. 84 if (typeof nodeInfo.entry != 'string') 85 nodeInfo.entry = JSON.stringify(nodeInfo.entry, null, 2); 86 jstProcess(new JsEvalContext(nodeInfo), nodeBrowser); 87 } 88 89 function decorateSyncNodeBrowser(syncNodeBrowser) { 90 cr.ui.decorate(syncNodeBrowser, cr.ui.Tree); 91 92 syncNodeBrowser.addEventListener('change', function(event) { 93 if (syncNodeBrowser.selectedItem) 94 updateNodeDetailView(syncNodeBrowser.selectedItem.detail); 95 }); 96 97 chrome.sync.getRootNode(function(rootNodeInfo) { 98 var rootTreeItem = makeNodeTreeItem(rootNodeInfo); 99 rootTreeItem.label = 'Root'; 100 syncNodeBrowser.add(rootTreeItem); 101 }); 102 } 103 104 // This is needed because JsTemplate (which is needed by 105 // updateNodeDetailView) is loaded at the end of the file after 106 // everything else. 107 // 108 // TODO(akalin): Remove dependency on JsTemplate and get rid of 109 // this. 110 var domLoaded = false; 111 var pendingSyncNodeBrowsers = []; 112 function decorateSyncNodeBrowserAfterDOMLoad(id) { 113 var e = document.getElementById(id); 114 if (domLoaded) { 115 decorateSyncNodeBrowser(e); 116 } else { 117 pendingSyncNodeBrowsers.push(e); 118 } 119 } 120 121 document.addEventListener('DOMContentLoaded', function() { 122 for (var i = 0; i < pendingSyncNodeBrowsers.length; ++i) { 123 decorateSyncNodeBrowser(pendingSyncNodeBrowsers[i]); 124 } 125 domLoaded = true; 126 }); 127 128 return { 129 decorateSyncNodeBrowser: decorateSyncNodeBrowserAfterDOMLoad 130 }; 131 }); 132