| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 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 | |
| 6 cr.define('bmm', function() { | |
| 7 /** | |
| 8 * The id of the bookmark root. | |
| 9 * @type {string} | |
| 10 * @const | |
| 11 */ | |
| 12 var ROOT_ID = '0'; | |
| 13 | |
| 14 /** @const */ var Tree = cr.ui.Tree; | |
| 15 /** @const */ var TreeItem = cr.ui.TreeItem; | |
| 16 | |
| 17 var treeLookup = {}; | |
| 18 var tree; | |
| 19 | |
| 20 // Manager for persisting the expanded state. | |
| 21 var expandedManager = { | |
| 22 /** | |
| 23 * A map of the collapsed IDs. | |
| 24 * @type {Object} | |
| 25 */ | |
| 26 map: 'bookmarkTreeState' in localStorage ? | |
| 27 JSON.parse(localStorage['bookmarkTreeState']) : {}, | |
| 28 | |
| 29 /** | |
| 30 * Set the collapsed state for an ID. | |
| 31 * @param {string} The bookmark ID of the tree item that was expanded or | |
| 32 * collapsed. | |
| 33 * @param {boolean} expanded Whether the tree item was expanded. | |
| 34 */ | |
| 35 set: function(id, expanded) { | |
| 36 if (expanded) | |
| 37 delete this.map[id]; | |
| 38 else | |
| 39 this.map[id] = 1; | |
| 40 | |
| 41 this.save(); | |
| 42 }, | |
| 43 | |
| 44 /** | |
| 45 * @param {string} id The bookmark ID. | |
| 46 * @return {boolean} Whether the tree item should be expanded. | |
| 47 */ | |
| 48 get: function(id) { | |
| 49 return !(id in this.map); | |
| 50 }, | |
| 51 | |
| 52 /** | |
| 53 * Callback for the expand and collapse events from the tree. | |
| 54 * @param {!Event} e The collapse or expand event. | |
| 55 */ | |
| 56 handleEvent: function(e) { | |
| 57 this.set(e.target.bookmarkId, e.type == 'expand'); | |
| 58 }, | |
| 59 | |
| 60 /** | |
| 61 * Cleans up old bookmark IDs. | |
| 62 */ | |
| 63 cleanUp: function() { | |
| 64 for (var id in this.map) { | |
| 65 // If the id is no longer in the treeLookup the bookmark no longer | |
| 66 // exists. | |
| 67 if (!(id in treeLookup)) | |
| 68 delete this.map[id]; | |
| 69 } | |
| 70 this.save(); | |
| 71 }, | |
| 72 | |
| 73 timer: null, | |
| 74 | |
| 75 /** | |
| 76 * Saves the expanded state to the localStorage. | |
| 77 */ | |
| 78 save: function() { | |
| 79 clearTimeout(this.timer); | |
| 80 var map = this.map; | |
| 81 // Save in a timeout so that we can coalesce multiple changes. | |
| 82 this.timer = setTimeout(function() { | |
| 83 localStorage['bookmarkTreeState'] = JSON.stringify(map); | |
| 84 }, 100); | |
| 85 } | |
| 86 }; | |
| 87 | |
| 88 // Clean up once per session but wait until things settle down a bit. | |
| 89 setTimeout(expandedManager.cleanUp.bind(expandedManager), 1e4); | |
| 90 | |
| 91 /** | |
| 92 * Creates a new tree item for a bookmark node. | |
| 93 * @param {!Object} bookmarkNode The bookmark node. | |
| 94 * @constructor | |
| 95 * @extends {TreeItem} | |
| 96 */ | |
| 97 function BookmarkTreeItem(bookmarkNode) { | |
| 98 var ti = new TreeItem({ | |
| 99 label: bookmarkNode.title, | |
| 100 bookmarkNode: bookmarkNode, | |
| 101 // Bookmark toolbar and Other bookmarks are not draggable. | |
| 102 draggable: bookmarkNode.parentId != ROOT_ID | |
| 103 }); | |
| 104 ti.__proto__ = BookmarkTreeItem.prototype; | |
| 105 treeLookup[bookmarkNode.id] = ti; | |
| 106 return ti; | |
| 107 } | |
| 108 | |
| 109 BookmarkTreeItem.prototype = { | |
| 110 __proto__: TreeItem.prototype, | |
| 111 | |
| 112 /** | |
| 113 * The ID of the bookmark this tree item represents. | |
| 114 * @type {string} | |
| 115 */ | |
| 116 get bookmarkId() { | |
| 117 return this.bookmarkNode.id; | |
| 118 } | |
| 119 }; | |
| 120 | |
| 121 /** | |
| 122 * Asynchronousy adds a tree item at the correct index based on the bookmark | |
| 123 * backend. | |
| 124 * | |
| 125 * Since the bookmark tree only contains folders the index we get from certain | |
| 126 * callbacks is not very useful so we therefore have this async call which | |
| 127 * gets the children of the parent and adds the tree item at the desired | |
| 128 * index. | |
| 129 * | |
| 130 * This also exoands the parent so that newly added children are revealed. | |
| 131 * | |
| 132 * @param {!cr.ui.TreeItem} parent The parent tree item. | |
| 133 * @param {!cr.ui.TreeItem} treeItem The tree item to add. | |
| 134 * @param {Function=} f A function which gets called after the item has been | |
| 135 * added at the right index. | |
| 136 */ | |
| 137 function addTreeItem(parent, treeItem, opt_f) { | |
| 138 chrome.bookmarks.getChildren(parent.bookmarkNode.id, function(children) { | |
| 139 var index = children.filter(bmm.isFolder).map(function(item) { | |
| 140 return item.id; | |
| 141 }).indexOf(treeItem.bookmarkNode.id); | |
| 142 parent.addAt(treeItem, index); | |
| 143 parent.expanded = true; | |
| 144 if (opt_f) | |
| 145 opt_f(); | |
| 146 }); | |
| 147 } | |
| 148 | |
| 149 | |
| 150 /** | |
| 151 * Creates a new bookmark list. | |
| 152 * @param {Object=} opt_propertyBag Optional properties. | |
| 153 * @constructor | |
| 154 * @extends {HTMLButtonElement} | |
| 155 */ | |
| 156 var BookmarkTree = cr.ui.define('tree'); | |
| 157 | |
| 158 BookmarkTree.prototype = { | |
| 159 __proto__: Tree.prototype, | |
| 160 | |
| 161 decorate: function() { | |
| 162 Tree.prototype.decorate.call(this); | |
| 163 this.addEventListener('expand', expandedManager); | |
| 164 this.addEventListener('collapse', expandedManager); | |
| 165 | |
| 166 bmm.tree = this; | |
| 167 }, | |
| 168 | |
| 169 handleBookmarkChanged: function(id, changeInfo) { | |
| 170 var treeItem = treeLookup[id]; | |
| 171 if (treeItem) | |
| 172 treeItem.label = treeItem.bookmarkNode.title = changeInfo.title; | |
| 173 }, | |
| 174 | |
| 175 handleChildrenReordered: function(id, reorderInfo) { | |
| 176 var parentItem = treeLookup[id]; | |
| 177 // The tree only contains folders. | |
| 178 var dirIds = reorderInfo.childIds.filter(function(id) { | |
| 179 return id in treeLookup; | |
| 180 }).forEach(function(id, i) { | |
| 181 parentItem.addAt(treeLookup[id], i); | |
| 182 }); | |
| 183 }, | |
| 184 | |
| 185 handleCreated: function(id, bookmarkNode) { | |
| 186 if (bmm.isFolder(bookmarkNode)) { | |
| 187 var parentItem = treeLookup[bookmarkNode.parentId]; | |
| 188 var newItem = new BookmarkTreeItem(bookmarkNode); | |
| 189 addTreeItem(parentItem, newItem); | |
| 190 } | |
| 191 }, | |
| 192 | |
| 193 handleMoved: function(id, moveInfo) { | |
| 194 var treeItem = treeLookup[id]; | |
| 195 if (treeItem) { | |
| 196 var oldParentItem = treeLookup[moveInfo.oldParentId]; | |
| 197 oldParentItem.remove(treeItem); | |
| 198 var newParentItem = treeLookup[moveInfo.parentId]; | |
| 199 // The tree only shows folders so the index is not the index we want. We | |
| 200 // therefore get the children need to adjust the index. | |
| 201 addTreeItem(newParentItem, treeItem); | |
| 202 } | |
| 203 }, | |
| 204 | |
| 205 handleRemoved: function(id, removeInfo) { | |
| 206 var parentItem = treeLookup[removeInfo.parentId]; | |
| 207 var itemToRemove = treeLookup[id]; | |
| 208 if (parentItem && itemToRemove) | |
| 209 parentItem.remove(itemToRemove); | |
| 210 }, | |
| 211 | |
| 212 insertSubtree: function(folder) { | |
| 213 if (!bmm.isFolder(folder)) | |
| 214 return; | |
| 215 var children = folder.children; | |
| 216 this.handleCreated(folder.id, folder); | |
| 217 for (var i = 0; i < children.length; i++) { | |
| 218 var child = children[i]; | |
| 219 this.insertSubtree(child); | |
| 220 } | |
| 221 }, | |
| 222 | |
| 223 /** | |
| 224 * Returns the bookmark node with the given ID. The tree only maintains | |
| 225 * folder nodes. | |
| 226 * @param {string} id The ID of the node to find. | |
| 227 * @return {BookmarkTreeNode} The bookmark tree node or null if not found. | |
| 228 */ | |
| 229 getBookmarkNodeById: function(id) { | |
| 230 var treeItem = treeLookup[id]; | |
| 231 if (treeItem) | |
| 232 return treeItem.bookmarkNode; | |
| 233 return null; | |
| 234 }, | |
| 235 | |
| 236 /** | |
| 237 * Returns the selected bookmark folder node as an array. | |
| 238 * @type {!Array} Array of bookmark nodes. | |
| 239 */ | |
| 240 get selectedFolders() { | |
| 241 return this.selectedItem && this.selectedItem.bookmarkNode ? | |
| 242 [this.selectedItem.bookmarkNode] : []; | |
| 243 }, | |
| 244 | |
| 245 /** | |
| 246 * Fetches the bookmark items and builds the tree control. | |
| 247 */ | |
| 248 reload: function() { | |
| 249 /** | |
| 250 * Recursive helper function that adds all the directories to the | |
| 251 * parentTreeItem. | |
| 252 * @param {!cr.ui.Tree|!cr.ui.TreeItem} parentTreeItem The parent tree | |
| 253 * element to append to. | |
| 254 * @param {!Array.<BookmarkTreeNode>} bookmarkNodes A list of bookmark | |
| 255 * nodes to be added. | |
| 256 * @return {boolean} Whether any directories where added. | |
| 257 */ | |
| 258 function buildTreeItems(parentTreeItem, bookmarkNodes) { | |
| 259 var hasDirectories = false; | |
| 260 for (var i = 0, bookmarkNode; bookmarkNode = bookmarkNodes[i]; i++) { | |
| 261 if (bmm.isFolder(bookmarkNode)) { | |
| 262 hasDirectories = true; | |
| 263 var item = new BookmarkTreeItem(bookmarkNode); | |
| 264 parentTreeItem.add(item); | |
| 265 var anyChildren = buildTreeItems(item, bookmarkNode.children); | |
| 266 item.expanded = anyChildren && expandedManager.get(bookmarkNode.id); | |
| 267 } | |
| 268 } | |
| 269 return hasDirectories; | |
| 270 } | |
| 271 | |
| 272 var self = this; | |
| 273 chrome.bookmarkManagerPrivate.getSubtree('', true, function(root) { | |
| 274 self.clear(); | |
| 275 buildTreeItems(self, root[0].children); | |
| 276 cr.dispatchSimpleEvent(self, 'load'); | |
| 277 }); | |
| 278 }, | |
| 279 | |
| 280 /** | |
| 281 * Clears the tree. | |
| 282 */ | |
| 283 clear: function() { | |
| 284 // Remove all fields without recreating the object since other code | |
| 285 // references it. | |
| 286 for (var id in treeLookup) { | |
| 287 delete treeLookup[id]; | |
| 288 } | |
| 289 this.textContent = ''; | |
| 290 }, | |
| 291 | |
| 292 /** @override */ | |
| 293 remove: function(child) { | |
| 294 Tree.prototype.remove.call(this, child); | |
| 295 if (child.bookmarkNode) | |
| 296 delete treeLookup[child.bookmarkNode.id]; | |
| 297 } | |
| 298 }; | |
| 299 | |
| 300 return { | |
| 301 BookmarkTree: BookmarkTree, | |
| 302 BookmarkTreeItem: BookmarkTreeItem, | |
| 303 treeLookup: treeLookup, | |
| 304 tree: tree, | |
| 305 ROOT_ID: ROOT_ID | |
| 306 }; | |
| 307 }); | |
| OLD | NEW |