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.
6 cr
.define('bmm', function() {
8 * The id of the bookmark root.
14 /** @const */ var Tree
= cr
.ui
.Tree
;
15 /** @const */ var TreeItem
= cr
.ui
.TreeItem
;
20 // Manager for persisting the expanded state.
21 var expandedManager
= {
23 * A map of the collapsed IDs.
26 map
: 'bookmarkTreeState' in localStorage
?
27 JSON
.parse(localStorage
['bookmarkTreeState']) : {},
30 * Set the collapsed state for an ID.
31 * @param {string} The bookmark ID of the tree item that was expanded or
33 * @param {boolean} expanded Whether the tree item was expanded.
35 set: function(id
, expanded
) {
45 * @param {string} id The bookmark ID.
46 * @return {boolean} Whether the tree item should be expanded.
49 return !(id
in this.map
);
53 * Callback for the expand and collapse events from the tree.
54 * @param {!Event} e The collapse or expand event.
56 handleEvent: function(e
) {
57 this.set(e
.target
.bookmarkId
, e
.type
== 'expand');
61 * Cleans up old bookmark IDs.
64 for (var id
in this.map
) {
65 // If the id is no longer in the treeLookup the bookmark no longer
67 if (!(id
in treeLookup
))
76 * Saves the expanded state to the localStorage.
79 clearTimeout(this.timer
);
81 // Save in a timeout so that we can coalesce multiple changes.
82 this.timer
= setTimeout(function() {
83 localStorage
['bookmarkTreeState'] = JSON
.stringify(map
);
88 // Clean up once per session but wait until things settle down a bit.
89 setTimeout(expandedManager
.cleanUp
.bind(expandedManager
), 1e4
);
92 * Creates a new tree item for a bookmark node.
93 * @param {!Object} bookmarkNode The bookmark node.
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
104 ti
.__proto__
= BookmarkTreeItem
.prototype;
105 treeLookup
[bookmarkNode
.id
] = ti
;
109 BookmarkTreeItem
.prototype = {
110 __proto__
: TreeItem
.prototype,
113 * The ID of the bookmark this tree item represents.
117 return this.bookmarkNode
.id
;
122 * Asynchronousy adds a tree item at the correct index based on the bookmark
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
130 * This also exoands the parent so that newly added children are revealed.
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.
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
) {
141 }).indexOf(treeItem
.bookmarkNode
.id
);
142 parent
.addAt(treeItem
, index
);
143 parent
.expanded
= true;
151 * Creates a new bookmark list.
152 * @param {Object=} opt_propertyBag Optional properties.
154 * @extends {HTMLButtonElement}
156 var BookmarkTree
= cr
.ui
.define('tree');
158 BookmarkTree
.prototype = {
159 __proto__
: Tree
.prototype,
161 decorate: function() {
162 Tree
.prototype.decorate
.call(this);
163 this.addEventListener('expand', expandedManager
);
164 this.addEventListener('collapse', expandedManager
);
169 handleBookmarkChanged: function(id
, changeInfo
) {
170 var treeItem
= treeLookup
[id
];
172 treeItem
.label
= treeItem
.bookmarkNode
.title
= changeInfo
.title
;
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
);
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
);
193 handleMoved: function(id
, moveInfo
) {
194 var treeItem
= treeLookup
[id
];
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
);
205 handleRemoved: function(id
, removeInfo
) {
206 var parentItem
= treeLookup
[removeInfo
.parentId
];
207 var itemToRemove
= treeLookup
[id
];
208 if (parentItem
&& itemToRemove
)
209 parentItem
.remove(itemToRemove
);
212 insertSubtree: function(folder
) {
213 if (!bmm
.isFolder(folder
))
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
);
224 * Returns the bookmark node with the given ID. The tree only maintains
226 * @param {string} id The ID of the node to find.
227 * @return {BookmarkTreeNode} The bookmark tree node or null if not found.
229 getBookmarkNodeById: function(id
) {
230 var treeItem
= treeLookup
[id
];
232 return treeItem
.bookmarkNode
;
237 * Returns the selected bookmark folder node as an array.
238 * @type {!Array} Array of bookmark nodes.
240 get selectedFolders() {
241 return this.selectedItem
&& this.selectedItem
.bookmarkNode
?
242 [this.selectedItem
.bookmarkNode
] : [];
246 * Fetches the bookmark items and builds the tree control.
250 * Recursive helper function that adds all the directories to the
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
256 * @return {boolean} Whether any directories where added.
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
);
269 return hasDirectories
;
273 chrome
.bookmarkManagerPrivate
.getSubtree('', true, function(root
) {
275 buildTreeItems(self
, root
[0].children
);
276 cr
.dispatchSimpleEvent(self
, 'load');
284 // Remove all fields without recreating the object since other code
286 for (var id
in treeLookup
) {
287 delete treeLookup
[id
];
289 this.textContent
= '';
293 remove: function(child
) {
294 Tree
.prototype.remove
.call(this, child
);
295 if (child
.bookmarkNode
)
296 delete treeLookup
[child
.bookmarkNode
.id
];
301 BookmarkTree
: BookmarkTree
,
302 BookmarkTreeItem
: BookmarkTreeItem
,
303 treeLookup
: treeLookup
,