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   /**
      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