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