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.
7 // require: cr/ui/tree.js
11 * A helper function to determine if a node is the root of its type.
13 * @param {!Object} node The node to check.
15 var isTypeRootNode = function(node
) {
16 return node
.PARENT_ID
== 'r' && node
.UNIQUE_SERVER_TAG
!= '';
20 * A helper function to determine if a node is a child of the given parent.
22 * @param {string} parentId The ID of the parent.
23 * @param {!Object} node The node to check.
25 var isChildOf = function(parentId
, node
) {
26 return node
.PARENT_ID
== parentId
;
30 * A helper function to sort sync nodes.
32 * Sorts by position index if possible, falls back to sorting by name, and
33 * finally sorting by METAHANDLE.
35 * If this proves to be slow and expensive, we should experiment with moving
36 * this functionality to C++ instead.
38 var nodeComparator = function(nodeA
, nodeB
) {
39 if (nodeA
.hasOwnProperty('positionIndex') &&
40 nodeB
.hasOwnProperty('positionIndex')) {
41 return nodeA
.positionIndex
- nodeB
.positionIndex
;
42 } else if (nodeA
.NON_UNIQUE_NAME
!= nodeB
.NON_UNIQUE_NAME
) {
43 return nodeA
.NON_UNIQUE_NAME
.localeCompare(nodeB
.NON_UNIQUE_NAME
);
45 return nodeA
.METAHANDLE
- nodeB
.METAHANDLE
;
50 * Updates the node detail view with the details for the given node.
51 * @param {!Object} node The struct representing the node we want to display.
53 function updateNodeDetailView(node
) {
54 var nodeDetailsView
= $('node-details');
55 nodeDetailsView
.hidden
= false;
56 jstProcess(new JsEvalContext(node
.entry_
), nodeDetailsView
);
60 * Updates the 'Last refresh time' display.
61 * @param {string} The text to display.
63 function setLastRefreshTime(str
) {
64 $('node-browser-refresh-time').textContent
= str
;
68 * Creates a new sync node tree item.
71 * @param {!Object} node The nodeDetails object for the node as returned by
72 * chrome.sync.getAllNodes().
73 * @extends {cr.ui.TreeItem}
75 var SyncNodeTreeItem = function(node
) {
76 var treeItem
= new cr
.ui
.TreeItem();
77 treeItem
.__proto__
= SyncNodeTreeItem
.prototype;
79 treeItem
.entry_
= node
;
80 treeItem
.label
= node
.NON_UNIQUE_NAME
;
82 treeItem
.mayHaveChildren_
= true;
84 // Load children on expand.
85 treeItem
.expanded_
= false;
86 treeItem
.addEventListener('expand',
87 treeItem
.handleExpand_
.bind(treeItem
));
89 treeItem
.classList
.add('leaf');
94 SyncNodeTreeItem
.prototype = {
95 __proto__
: cr
.ui
.TreeItem
.prototype,
98 * Finds the children of this node and appends them to the tree.
100 handleExpand_: function(event
) {
103 if (treeItem
.expanded_
) {
106 treeItem
.expanded_
= true;
108 var children
= treeItem
.tree
.allNodes
.filter(
109 isChildOf
.bind(undefined, treeItem
.entry_
.ID
));
110 children
.sort(nodeComparator
);
112 children
.forEach(function(node
) {
113 treeItem
.add(new SyncNodeTreeItem(node
));
119 * Creates a new sync node tree. Technically, it's a forest since it each
120 * type has its own root node for its own tree, but it still looks and acts
121 * mostly like a tree.
123 * @param {Object=} opt_propertyBag Optional properties.
125 * @extends {cr.ui.Tree}
127 var SyncNodeTree
= cr
.ui
.define('tree');
129 SyncNodeTree
.prototype = {
130 __proto__
: cr
.ui
.Tree
.prototype,
132 decorate: function() {
133 cr
.ui
.Tree
.prototype.decorate
.call(this);
134 this.addEventListener('change', this.handleChange_
.bind(this));
138 populate: function(nodes
) {
141 // We store the full set of nodes in the SyncNodeTree object.
142 tree
.allNodes
= nodes
;
144 var roots
= tree
.allNodes
.filter(isTypeRootNode
);
145 roots
.sort(nodeComparator
);
147 roots
.forEach(function(typeRoot
) {
148 tree
.add(new SyncNodeTreeItem(typeRoot
));
152 handleChange_: function(event
) {
153 if (this.selectedItem
) {
154 updateNodeDetailView(this.selectedItem
);
160 * Clears any existing UI state. Useful prior to a refresh.
163 var treeContainer
= $('sync-node-tree-container');
164 while (treeContainer
.firstChild
) {
165 treeContainer
.removeChild(treeContainer
.firstChild
);
168 var nodeDetailsView
= $('node-details');
169 nodeDetailsView
.hidden
= true;
173 * Fetch the latest set of nodes and refresh the UI.
176 $('node-browser-refresh-button').disabled
= true;
179 setLastRefreshTime('In progress since ' + (new Date()).toLocaleString());
181 chrome
.sync
.getAllNodes(function(nodeMap
) {
182 // Put all nodes into one big list that ignores the type.
184 map(function(x
) { return x
.nodes
; }).
185 reduce(function(a
, b
) { return a
.concat(b
); });
187 var treeContainer
= $('sync-node-tree-container');
188 var tree
= document
.createElement('tree');
189 tree
.setAttribute('id', 'sync-node-tree');
190 tree
.setAttribute('icon-visibility', 'parent');
191 treeContainer
.appendChild(tree
);
193 cr
.ui
.decorate(tree
, SyncNodeTree
);
194 tree
.populate(nodes
);
196 setLastRefreshTime((new Date()).toLocaleString());
197 $('node-browser-refresh-button').disabled
= false;
201 document
.addEventListener('DOMContentLoaded', function(e
) {
202 $('node-browser-refresh-button').addEventListener('click', refresh
);
203 cr
.ui
.decorate('#sync-node-splitter', cr
.ui
.Splitter
);
205 // Automatically trigger a refresh the first time this tab is selected.
206 $('sync-browser-tab').addEventListener('selectedChange', function f(e
) {
208 $('sync-browser-tab').removeEventListener('selectedChange', f
);