• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2012 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
5cr.define('bmm', function() {
6  /**
7   * Whether a node contains another node.
8   * TODO(yosin): Once JavaScript style guide is updated and linter follows
9   * that, we'll remove useless documentations for |parent| and |descendant|.
10   * TODO(yosin): bmm.contains() should be method of BookmarkTreeNode.
11   * @param {!BookmarkTreeNode} parent .
12   * @param {!BookmarkTreeNode} descendant .
13   * @return {boolean} Whether the parent contains the descendant.
14   */
15  function contains(parent, descendant) {
16    if (descendant.parentId == parent.id)
17      return true;
18    // the bmm.treeLookup contains all folders
19    var parentTreeItem = bmm.treeLookup[descendant.parentId];
20    if (!parentTreeItem || !parentTreeItem.bookmarkNode)
21      return false;
22    return this.contains(parent, parentTreeItem.bookmarkNode);
23  }
24
25  /**
26   * @param {!BookmarkTreeNode} node The node to test.
27   * @return {boolean} Whether a bookmark node is a folder.
28   */
29  function isFolder(node) {
30    return !('url' in node);
31  }
32
33  var loadingPromises = {};
34
35  /**
36   * Promise version of chrome.bookmarkManagerPrivate.getSubtree.
37   * @param {string} id .
38   * @param {boolean} foldersOnly .
39   * @return {!Promise.<!Array.<!BookmarkTreeNode>>} .
40   */
41  function getSubtreePromise(id, foldersOnly) {
42    return new Promise(function(resolve) {
43      chrome.bookmarkManagerPrivate.getSubtree(id, foldersOnly, resolve);
44    });
45  }
46
47  /**
48   * Loads a subtree of the bookmark tree and returns a {@code Promise} that
49   * will be fulfilled when done. This reuses multiple loads so that we do not
50   * load the same subtree more than once at the same time.
51   * @return {!Promise.<!BookmarkTreeNode>} The future promise for the load.
52   */
53  function loadSubtree(id) {
54    if (!loadingPromises[id]) {
55      loadingPromises[id] = getSubtreePromise(id, false).then(function(nodes) {
56        delete loadingPromises[id];
57        return nodes && nodes[0];
58      });
59    }
60    return loadingPromises[id];
61  }
62
63  /**
64   * Loads the entire bookmark tree and returns a {@code Promise} that will
65   * be fulfilled when done. This reuses multiple loads so that we do not load
66   * the same tree more than once at the same time.
67   * @return {!Promise.<Node>} The future promise for the load.
68   */
69  function loadTree() {
70    return loadSubtree('');
71  }
72
73  var bookmarkCache = {
74    /**
75     * Removes the cached item from both the list and tree lookups.
76     */
77    remove: function(id) {
78      var treeItem = bmm.treeLookup[id];
79      if (treeItem) {
80        var items = treeItem.items; // is an HTMLCollection
81        for (var i = 0; i < items.length; ++i) {
82          var item = items[i];
83          var bookmarkNode = item.bookmarkNode;
84          delete bmm.treeLookup[bookmarkNode.id];
85        }
86        delete bmm.treeLookup[id];
87      }
88    },
89
90    /**
91     * Updates the underlying bookmark node for the tree items and list items by
92     * querying the bookmark backend.
93     * @param {string} id The id of the node to update the children for.
94     * @param {Function=} opt_f A funciton to call when done.
95     */
96    updateChildren: function(id, opt_f) {
97      function updateItem(bookmarkNode) {
98        var treeItem = bmm.treeLookup[bookmarkNode.id];
99        if (treeItem) {
100          treeItem.bookmarkNode = bookmarkNode;
101        }
102      }
103
104      chrome.bookmarks.getChildren(id, function(children) {
105        if (children)
106          children.forEach(updateItem);
107
108        if (opt_f)
109          opt_f(children);
110      });
111    }
112  };
113
114  /**
115   * Called when the title of a bookmark changes.
116   * @param {string} id The id of changed bookmark node.
117   * @param {!Object} changeInfo The information about how the node changed.
118   */
119  function handleBookmarkChanged(id, changeInfo) {
120    if (bmm.tree)
121      bmm.tree.handleBookmarkChanged(id, changeInfo);
122    if (bmm.list)
123      bmm.list.handleBookmarkChanged(id, changeInfo);
124  }
125
126  /**
127   * Callback for when the user reorders by title.
128   * @param {string} id The id of the bookmark folder that was reordered.
129   * @param {!Object} reorderInfo The information about how the items where
130   *     reordered.
131   */
132  function handleChildrenReordered(id, reorderInfo) {
133    if (bmm.tree)
134      bmm.tree.handleChildrenReordered(id, reorderInfo);
135    if (bmm.list)
136      bmm.list.handleChildrenReordered(id, reorderInfo);
137    bookmarkCache.updateChildren(id);
138  }
139
140  /**
141   * Callback for when a bookmark node is created.
142   * @param {string} id The id of the newly created bookmark node.
143   * @param {!Object} bookmarkNode The new bookmark node.
144   */
145  function handleCreated(id, bookmarkNode) {
146    if (bmm.list)
147      bmm.list.handleCreated(id, bookmarkNode);
148    if (bmm.tree)
149      bmm.tree.handleCreated(id, bookmarkNode);
150    bookmarkCache.updateChildren(bookmarkNode.parentId);
151  }
152
153  /**
154   * Callback for when a bookmark node is moved.
155   * @param {string} id The id of the moved bookmark node.
156   * @param {!Object} moveInfo The information about move.
157   */
158  function handleMoved(id, moveInfo) {
159    if (bmm.list)
160      bmm.list.handleMoved(id, moveInfo);
161    if (bmm.tree)
162      bmm.tree.handleMoved(id, moveInfo);
163
164    bookmarkCache.updateChildren(moveInfo.parentId);
165    if (moveInfo.parentId != moveInfo.oldParentId)
166      bookmarkCache.updateChildren(moveInfo.oldParentId);
167  }
168
169  /**
170   * Callback for when a bookmark node is removed.
171   * @param {string} id The id of the removed bookmark node.
172   * @param {!Object} bookmarkNode The information about removed.
173   */
174  function handleRemoved(id, removeInfo) {
175    if (bmm.list)
176      bmm.list.handleRemoved(id, removeInfo);
177    if (bmm.tree)
178      bmm.tree.handleRemoved(id, removeInfo);
179
180    bookmarkCache.updateChildren(removeInfo.parentId);
181    bookmarkCache.remove(id);
182  }
183
184  /**
185   * Callback for when all bookmark nodes have been deleted.
186   */
187  function handleRemoveAll() {
188    // Reload the list and the tree.
189    if (bmm.list)
190      bmm.list.reload();
191    if (bmm.tree)
192      bmm.tree.reload();
193  }
194
195  /**
196   * Callback for when importing bookmark is started.
197   */
198  function handleImportBegan() {
199    chrome.bookmarks.onCreated.removeListener(handleCreated);
200    chrome.bookmarks.onChanged.removeListener(handleBookmarkChanged);
201  }
202
203  /**
204   * Callback for when importing bookmark node is finished.
205   */
206  function handleImportEnded() {
207    // When importing is done we reload the tree and the list.
208
209    function f() {
210      bmm.tree.removeEventListener('load', f);
211
212      chrome.bookmarks.onCreated.addListener(handleCreated);
213      chrome.bookmarks.onChanged.addListener(handleBookmarkChanged);
214
215      if (!bmm.list)
216        return;
217
218      // TODO(estade): this should navigate to the newly imported folder, which
219      // may be the bookmark bar if there were no previous bookmarks.
220      bmm.list.reload();
221    }
222
223    if (bmm.tree) {
224      bmm.tree.addEventListener('load', f);
225      bmm.tree.reload();
226    }
227  }
228
229  /**
230   * Adds the listeners for the bookmark model change events.
231   */
232  function addBookmarkModelListeners() {
233    chrome.bookmarks.onChanged.addListener(handleBookmarkChanged);
234    chrome.bookmarks.onChildrenReordered.addListener(handleChildrenReordered);
235    chrome.bookmarks.onCreated.addListener(handleCreated);
236    chrome.bookmarks.onMoved.addListener(handleMoved);
237    chrome.bookmarks.onRemoved.addListener(handleRemoved);
238    chrome.bookmarks.onImportBegan.addListener(handleImportBegan);
239    chrome.bookmarks.onImportEnded.addListener(handleImportEnded);
240  };
241
242  return {
243    contains: contains,
244    isFolder: isFolder,
245    loadSubtree: loadSubtree,
246    loadTree: loadTree,
247    addBookmarkModelListeners: addBookmarkModelListeners
248  };
249});
250