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
9cr.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