Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / sync_internals / sync_node_browser.js
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 (function() {
10   /**
11    * A helper function to determine if a node is the root of its type.
12    *
13    * @param {!Object} node The node to check.
14    */
15   var isTypeRootNode = function(node) {
16     return node.PARENT_ID == 'r' && node.UNIQUE_SERVER_TAG != '';
17   }
18
19   /**
20    * A helper function to determine if a node is a child of the given parent.
21    *
22    * @param {string} parentId The ID of the parent.
23    * @param {!Object} node The node to check.
24    */
25   var isChildOf = function(parentId, node) {
26     return node.PARENT_ID == parentId;
27   }
28
29   /**
30    * A helper function to sort sync nodes.
31    *
32    * Sorts by position index if possible, falls back to sorting by name, and
33    * finally sorting by METAHANDLE.
34    *
35    * If this proves to be slow and expensive, we should experiment with moving
36    * this functionality to C++ instead.
37    */
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);
44     } else {
45       return nodeA.METAHANDLE - nodeB.METAHANDLE;
46     }
47   }
48
49   /**
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.
52    */
53   function updateNodeDetailView(node) {
54     var nodeDetailsView = $('node-details');
55     nodeDetailsView.hidden = false;
56     jstProcess(new JsEvalContext(node.entry_), nodeDetailsView);
57   }
58
59   /**
60    * Updates the 'Last refresh time' display.
61    * @param {string} The text to display.
62    */
63   function setLastRefreshTime(str) {
64     $('node-browser-refresh-time').textContent = str;
65   }
66
67   /**
68    * Creates a new sync node tree item.
69    *
70    * @constructor
71    * @param {!Object} node The nodeDetails object for the node as returned by
72    *     chrome.sync.getAllNodes().
73    * @extends {cr.ui.TreeItem}
74    */
75   var SyncNodeTreeItem = function(node) {
76     var treeItem = new cr.ui.TreeItem();
77     treeItem.__proto__ = SyncNodeTreeItem.prototype;
78
79     treeItem.entry_ = node;
80     treeItem.label = node.NON_UNIQUE_NAME;
81     if (node.IS_DIR) {
82       treeItem.mayHaveChildren_ = true;
83
84       // Load children on expand.
85       treeItem.expanded_ = false;
86       treeItem.addEventListener('expand',
87                                 treeItem.handleExpand_.bind(treeItem));
88     } else {
89       treeItem.classList.add('leaf');
90     }
91     return treeItem;
92   };
93
94   SyncNodeTreeItem.prototype = {
95     __proto__: cr.ui.TreeItem.prototype,
96
97     /**
98      * Finds the children of this node and appends them to the tree.
99      */
100     handleExpand_: function(event) {
101       var treeItem = this;
102
103       if (treeItem.expanded_) {
104         return;
105       }
106       treeItem.expanded_ = true;
107
108       var children = treeItem.tree.allNodes.filter(
109           isChildOf.bind(undefined, treeItem.entry_.ID));
110       children.sort(nodeComparator);
111
112       children.forEach(function(node) {
113         treeItem.add(new SyncNodeTreeItem(node));
114       });
115     },
116   };
117
118   /**
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.
122    *
123    * @param {Object=} opt_propertyBag Optional properties.
124    * @constructor
125    * @extends {cr.ui.Tree}
126    */
127   var SyncNodeTree = cr.ui.define('tree');
128
129   SyncNodeTree.prototype = {
130     __proto__: cr.ui.Tree.prototype,
131
132     decorate: function() {
133       cr.ui.Tree.prototype.decorate.call(this);
134       this.addEventListener('change', this.handleChange_.bind(this));
135       this.allNodes = [];
136     },
137
138     populate: function(nodes) {
139       var tree = this;
140
141       // We store the full set of nodes in the SyncNodeTree object.
142       tree.allNodes = nodes;
143
144       var roots = tree.allNodes.filter(isTypeRootNode);
145       roots.sort(nodeComparator);
146
147       roots.forEach(function(typeRoot) {
148         tree.add(new SyncNodeTreeItem(typeRoot));
149       });
150     },
151
152     handleChange_: function(event) {
153       if (this.selectedItem) {
154         updateNodeDetailView(this.selectedItem);
155       }
156     }
157   };
158
159   /**
160    * Clears any existing UI state.  Useful prior to a refresh.
161    */
162   function clear() {
163     var treeContainer = $('sync-node-tree-container');
164     while (treeContainer.firstChild) {
165       treeContainer.removeChild(treeContainer.firstChild);
166     }
167
168     var nodeDetailsView = $('node-details');
169     nodeDetailsView.hidden = true;
170   }
171
172   /**
173    * Fetch the latest set of nodes and refresh the UI.
174    */
175   function refresh() {
176     $('node-browser-refresh-button').disabled = true;
177
178     clear();
179     setLastRefreshTime('In progress since ' + (new Date()).toLocaleString());
180
181     chrome.sync.getAllNodes(function(nodeMap) {
182       // Put all nodes into one big list that ignores the type.
183       var nodes = nodeMap.
184           map(function(x) { return x.nodes; }).
185           reduce(function(a, b) { return a.concat(b); });
186
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);
192
193       cr.ui.decorate(tree, SyncNodeTree);
194       tree.populate(nodes);
195
196       setLastRefreshTime((new Date()).toLocaleString());
197       $('node-browser-refresh-button').disabled = false;
198     });
199   }
200
201   document.addEventListener('DOMContentLoaded', function(e) {
202     $('node-browser-refresh-button').addEventListener('click', refresh);
203     cr.ui.decorate('#sync-node-splitter', cr.ui.Splitter);
204
205     // Automatically trigger a refresh the first time this tab is selected.
206     $('sync-browser-tab').addEventListener('selectedChange', function f(e) {
207       if (this.selected) {
208         $('sync-browser-tab').removeEventListener('selectedChange', f);
209         refresh();
210       }
211     });
212   });
213
214 })();