summaryrefslogtreecommitdiffstats
path: root/chrome/browser/resources/sync_internals/sync_node_browser.js
blob: 8d1ff3d1be0fd28b86ca19bc968981ae5aae94e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// 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
  };
});