| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 var BookmarksStore = Polymer({ | 5 var BookmarksStore = Polymer({ |
| 6 is: 'bookmarks-store', | 6 is: 'bookmarks-store', |
| 7 | 7 |
| 8 properties: { | 8 properties: { |
| 9 /** @type {BookmarkTreeNode} */ | 9 /** @type {BookmarkTreeNode} */ |
| 10 rootNode: { | 10 rootNode: { |
| 11 type: Object, | 11 type: Object, |
| 12 notify: true, | 12 notify: true, |
| 13 }, | 13 }, |
| 14 | 14 |
| 15 /** @type {?string} */ | 15 /** @type {string} */ |
| 16 selectedId: { | 16 selectedId: { |
| 17 type: String, | 17 type: String, |
| 18 observer: 'updateSelectedDisplay_', | |
| 19 notify: true, | 18 notify: true, |
| 20 }, | 19 }, |
| 21 | 20 |
| 22 searchTerm: { | 21 searchTerm: { |
| 23 type: String, | 22 type: String, |
| 24 value: '', | 23 value: '', |
| 25 observer: 'updateSearchDisplay_', | 24 observer: 'updateSearchDisplay_', |
| 26 notify: true, | 25 notify: true, |
| 27 }, | 26 }, |
| 28 | 27 |
| 29 /** | 28 /** |
| 30 * This updates to either the result of a search or the contents of the | 29 * This updates to either the result of a search or the contents of the |
| 31 * selected folder. | 30 * selected folder. |
| 32 * @type {Array<BookmarkTreeNode>} | 31 * @type {Array<BookmarkTreeNode>} |
| 33 */ | 32 */ |
| 34 displayedList: { | 33 displayedList: { |
| 35 type: Array, | 34 type: Array, |
| 36 notify: true, | 35 notify: true, |
| 37 readOnly: true, | 36 readOnly: true, |
| 38 }, | 37 }, |
| 39 | 38 |
| 40 /** @type {Object<?string, !BookmarkTreeNode>} */ | 39 /** @type {Object<?string, !BookmarkTreeNode>} */ |
| 41 idToNodeMap_: Object, | 40 idToNodeMap_: Object, |
| 42 | 41 |
| 43 /** @type {?number} */ | 42 /** @type {?number} */ |
| 44 anchorIndex_: Number, | 43 anchorIndex_: { |
| 44 type: Number, |
| 45 value: null, |
| 46 }, |
| 45 | 47 |
| 46 /** @type {Set<string>} */ | 48 /** @type {Set<string>} */ |
| 47 searchResultSet_: Object, | 49 searchResultSet_: Object, |
| 48 }, | 50 }, |
| 49 | 51 |
| 50 /** @private {Object} */ | 52 /** @private {Object} */ |
| 51 documentListeners_: null, | 53 documentListeners_: null, |
| 52 | 54 |
| 53 /** @override */ | 55 /** @override */ |
| 54 attached: function() { | 56 attached: function() { |
| (...skipping 19 matching lines...) Expand all Loading... |
| 74 */ | 76 */ |
| 75 initializeStore: function() { | 77 initializeStore: function() { |
| 76 chrome.bookmarks.getTree(function(results) { | 78 chrome.bookmarks.getTree(function(results) { |
| 77 this.setupStore_(results[0]); | 79 this.setupStore_(results[0]); |
| 78 }.bind(this)); | 80 }.bind(this)); |
| 79 // Attach bookmarks API listeners. | 81 // Attach bookmarks API listeners. |
| 80 chrome.bookmarks.onRemoved.addListener(this.onBookmarkRemoved_.bind(this)); | 82 chrome.bookmarks.onRemoved.addListener(this.onBookmarkRemoved_.bind(this)); |
| 81 chrome.bookmarks.onChanged.addListener(this.onBookmarkChanged_.bind(this)); | 83 chrome.bookmarks.onChanged.addListener(this.onBookmarkChanged_.bind(this)); |
| 82 }, | 84 }, |
| 83 | 85 |
| 86 /** |
| 87 * Changes the selected folder to that of the id or else defaults to the |
| 88 * Bookmarks Bar. |
| 89 * @param {string} id The id of the folder to be selected. |
| 90 */ |
| 91 selectFolder: function(id) { |
| 92 if (!this.idToNodeMap_) |
| 93 return; |
| 94 |
| 95 if (!this.idToNodeMap_[id] || this.idToNodeMap_[id].url) |
| 96 id = this.rootNode.children[0].id; |
| 97 |
| 98 this.deselectFolders_(); |
| 99 this.selectedId = id; |
| 100 if (this.searchTerm) |
| 101 return; |
| 102 |
| 103 // Update the folder node to selected. |
| 104 this.selectedId = id; |
| 105 var folder = this.idToNodeMap_[this.selectedId]; |
| 106 this.set(folder.path + '.isSelectedFolder', true); |
| 107 |
| 108 // Update the displayed list to the selected folder. |
| 109 this.linkPaths('displayedList', folder.path + '.children'); |
| 110 this._setDisplayedList( |
| 111 /** @type {!Array<!BookmarkTreeNode>}*/ (folder.children)); |
| 112 |
| 113 // Open any closed ancestors of the selected in the sidebar. |
| 114 if (folder.id == this.rootNode.id) |
| 115 return; |
| 116 |
| 117 var parent = this.idToNodeMap_[/** @type {?string} */ (folder.parentId)]; |
| 118 while (parent) { |
| 119 if (!parent.isOpen) { |
| 120 this.fire('folder-open-changed', { |
| 121 id: parent.id, |
| 122 open: true, |
| 123 }); |
| 124 } |
| 125 parent = this.idToNodeMap_[/** @type {?string} */ (parent.parentId)]; |
| 126 } |
| 127 }, |
| 128 |
| 84 ////////////////////////////////////////////////////////////////////////////// | 129 ////////////////////////////////////////////////////////////////////////////// |
| 85 // bookmarks-store, private: | 130 // bookmarks-store, private: |
| 86 | 131 |
| 87 /** | 132 /** |
| 88 * @param {BookmarkTreeNode} rootNode | 133 * @param {BookmarkTreeNode} rootNode |
| 89 * @private | 134 * @private |
| 90 */ | 135 */ |
| 91 setupStore_: function(rootNode) { | 136 setupStore_: function(rootNode) { |
| 92 this.rootNode = rootNode; | 137 this.rootNode = rootNode; |
| 93 this.idToNodeMap_ = {}; | 138 this.idToNodeMap_ = {}; |
| 94 this.rootNode.path = 'rootNode'; | 139 this.rootNode.path = 'rootNode'; |
| 95 BookmarksStore.generatePaths(rootNode, 0); | 140 BookmarksStore.generatePaths(rootNode, 0); |
| 96 BookmarksStore.initNodes(this.rootNode, this.idToNodeMap_); | 141 BookmarksStore.initNodes(this.rootNode, this.idToNodeMap_); |
| 97 | 142 |
| 98 // Initialize the store's fields from the router. | 143 // Initialize the store's fields from the router. |
| 99 if (this.$.router.searchTerm) | 144 this.selectFolder(this.$.router.selectedId); |
| 100 this.searchTerm = this.$.router.searchTerm; | 145 this.searchTerm = this.$.router.searchTerm; |
| 101 else | |
| 102 this.fire('selected-folder-changed', this.$.router.selectedId); | |
| 103 }, | 146 }, |
| 104 | 147 |
| 105 /** @private */ | 148 /** @private */ |
| 106 deselectFolders_: function() { | 149 deselectFolders_: function() { |
| 150 if (!this.idToNodeMap_[this.selectedId]) |
| 151 return; |
| 152 |
| 107 this.unlinkPaths('displayedList'); | 153 this.unlinkPaths('displayedList'); |
| 108 this.set( | 154 this.set( |
| 109 this.idToNodeMap_[this.selectedId].path + '.isSelectedFolder', false); | 155 this.idToNodeMap_[this.selectedId].path + '.isSelectedFolder', false); |
| 110 this.selectedId = null; | |
| 111 }, | 156 }, |
| 112 | 157 |
| 113 /** | 158 /** |
| 114 * @param {BookmarkTreeNode} folder | 159 * @param {BookmarkTreeNode} folder |
| 115 * @private | 160 * @private |
| 116 * @return {boolean} | 161 * @return {boolean} |
| 117 */ | 162 */ |
| 118 isAncestorOfSelected_: function(folder) { | 163 isAncestorOfSelected_: function(folder) { |
| 119 if (!this.selectedId) | |
| 120 return false; | |
| 121 | |
| 122 var selectedNode = this.idToNodeMap_[this.selectedId]; | 164 var selectedNode = this.idToNodeMap_[this.selectedId]; |
| 123 return selectedNode.path.startsWith(folder.path); | 165 return selectedNode.path.startsWith(folder.path); |
| 124 }, | 166 }, |
| 125 | 167 |
| 126 /** @private */ | 168 /** @private */ |
| 127 updateSearchDisplay_: function() { | 169 updateSearchDisplay_: function() { |
| 128 if (!this.rootNode) | 170 if (!this.rootNode) |
| 129 return; | 171 return; |
| 130 | 172 |
| 131 if (!this.searchTerm) { | 173 if (!this.searchTerm) { |
| 132 this.fire('selected-folder-changed', this.rootNode.children[0].id); | 174 this.selectFolder(this.selectedId); |
| 133 } else { | 175 } else { |
| 134 chrome.bookmarks.search(this.searchTerm, function(results) { | 176 chrome.bookmarks.search(this.searchTerm, function(results) { |
| 135 this.anchorIndex_ = null; | 177 this.anchorIndex_ = null; |
| 136 this.clearSelectedItems_(); | 178 this.clearSelectedItems_(); |
| 137 this.searchResultSet_ = new Set(); | 179 this.searchResultSet_ = new Set(); |
| 138 | 180 |
| 139 if (this.selectedId) | 181 this.deselectFolders_(); |
| 140 this.deselectFolders_(); | |
| 141 | |
| 142 this.setupSearchResults_(results); | 182 this.setupSearchResults_(results); |
| 143 }.bind(this)); | 183 }.bind(this)); |
| 144 } | 184 } |
| 145 }, | 185 }, |
| 146 | 186 |
| 147 /** @private */ | |
| 148 updateSelectedDisplay_: function() { | |
| 149 // Don't change to the selected display if ID was cleared. | |
| 150 if (!this.selectedId) | |
| 151 return; | |
| 152 | |
| 153 this.clearSelectedItems_(); | |
| 154 this.anchorIndex_ = null; | |
| 155 | |
| 156 var selectedNode = this.idToNodeMap_[this.selectedId]; | |
| 157 this.linkPaths('displayedList', selectedNode.path + '.children'); | |
| 158 this._setDisplayedList( | |
| 159 /** @type {Array<BookmarkTreeNode>} */ (selectedNode.children)); | |
| 160 }, | |
| 161 | |
| 162 /** | 187 /** |
| 163 * Remove all descendants of a given node from the map. | 188 * Remove all descendants of a given node from the map. |
| 164 * @param {string} id | 189 * @param {string} id |
| 165 * @private | 190 * @private |
| 166 */ | 191 */ |
| 167 removeDescendantsFromMap_: function(id) { | 192 removeDescendantsFromMap_: function(id) { |
| 168 var node = this.idToNodeMap_[id]; | 193 var node = this.idToNodeMap_[id]; |
| 169 if (!node) | 194 if (!node) |
| 170 return; | 195 return; |
| 171 | 196 |
| 172 if (node.children) { | 197 if (node.children) { |
| 173 for (var i = 0; i < node.children.length; i++) | 198 for (var i = 0; i < node.children.length; i++) |
| 174 this.removeDescendantsFromMap_(node.children[i].id); | 199 this.removeDescendantsFromMap_(node.children[i].id); |
| 175 } | 200 } |
| 176 delete this.idToNodeMap_[id]; | 201 delete this.idToNodeMap_[id]; |
| 177 }, | 202 }, |
| 178 | 203 |
| 204 |
| 179 /** | 205 /** |
| 180 * Remove all selected items in the list. | 206 * Remove all selected items in the list. |
| 181 * @private | 207 * @private |
| 182 */ | 208 */ |
| 183 clearSelectedItems_: function() { | 209 clearSelectedItems_: function() { |
| 184 if (!this.displayedList) | 210 if (!this.displayedList) |
| 185 return; | 211 return; |
| 186 | 212 |
| 187 for (var i = 0; i < this.displayedList.length; i++) { | 213 for (var i = 0; i < this.displayedList.length; i++) { |
| 188 if (!this.displayedList[i].isSelectedItem) | 214 if (!this.displayedList[i].isSelectedItem) |
| 189 continue; | 215 continue; |
| 190 | |
| 191 this.set('displayedList.#' + i + '.isSelectedItem', false); | 216 this.set('displayedList.#' + i + '.isSelectedItem', false); |
| 192 } | 217 } |
| 193 }, | 218 }, |
| 194 | 219 |
| 195 /** | 220 /** |
| 196 * Return the index in the search result of an item. | 221 * Return the index in the search result of an item. |
| 197 * @param {BookmarkTreeNode} item | 222 * @param {BookmarkTreeNode} item |
| 198 * @return {number} | 223 * @return {number} |
| 199 * @private | 224 * @private |
| 200 */ | 225 */ |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 283 // indexes will have changed and Polymer doesn't update them. | 308 // indexes will have changed and Polymer doesn't update them. |
| 284 this.removeDescendantsFromMap_(id); | 309 this.removeDescendantsFromMap_(id); |
| 285 parentNode.path = this.idToNodeMap_[parentNode.id].path; | 310 parentNode.path = this.idToNodeMap_[parentNode.id].path; |
| 286 BookmarksStore.generatePaths(parentNode, 0); | 311 BookmarksStore.generatePaths(parentNode, 0); |
| 287 BookmarksStore.initNodes(parentNode, this.idToNodeMap_); | 312 BookmarksStore.initNodes(parentNode, this.idToNodeMap_); |
| 288 this.set(parentNode.path, parentNode); | 313 this.set(parentNode.path, parentNode); |
| 289 | 314 |
| 290 // Updates selectedId if the removed node is an ancestor of the current | 315 // Updates selectedId if the removed node is an ancestor of the current |
| 291 // selected node. | 316 // selected node. |
| 292 if (isAncestor) | 317 if (isAncestor) |
| 293 this.fire('selected-folder-changed', removeInfo.parentId); | 318 this.selectFolder(removeInfo.parentId); |
| 294 | 319 |
| 295 // Only update the displayedList if the removed node is in the | 320 // Only update the displayedList if the removed node is in the |
| 296 // displayedList. | 321 // displayedList. |
| 297 if (!wasInDisplayedList) | 322 if (!wasInDisplayedList) |
| 298 return; | 323 return; |
| 299 | 324 |
| 300 this.anchorIndex_ = null; | 325 this.anchorIndex_ = null; |
| 301 | 326 |
| 302 // Update the currently displayed list. | 327 // Update the currently displayed list. |
| 303 if (this.searchTerm) { | 328 if (this.searchTerm) { |
| 304 this.updateSearchDisplay_(); | 329 this.updateSearchDisplay_(); |
| 305 } else { | 330 } else { |
| 306 if (!isAncestor) | 331 if (!isAncestor) |
| 307 this.fire('selected-folder-changed', this.selectedId); | 332 this.selectFolder(this.selectedId); |
| 308 | 333 |
| 309 this._setDisplayedList(parentNode.children); | 334 this._setDisplayedList(parentNode.children); |
| 310 } | 335 } |
| 311 }.bind(this)); | 336 }.bind(this)); |
| 312 }, | 337 }, |
| 313 | 338 |
| 314 /** | 339 /** |
| 315 * Called when the title of a bookmark changes. | 340 * Called when the title of a bookmark changes. |
| 316 * @param {string} id The id of changed bookmark node. | 341 * @param {string} id The id of changed bookmark node. |
| 317 * @param {!Object} changeInfo | 342 * @param {!Object} changeInfo |
| (...skipping 19 matching lines...) Expand all Loading... |
| 337 this.searchTerm = /** @type {string} */ (e.detail); | 362 this.searchTerm = /** @type {string} */ (e.detail); |
| 338 }, | 363 }, |
| 339 | 364 |
| 340 /** | 365 /** |
| 341 * Selects the folder specified by the event and deselects the previously | 366 * Selects the folder specified by the event and deselects the previously |
| 342 * selected folder. | 367 * selected folder. |
| 343 * @param {CustomEvent} e | 368 * @param {CustomEvent} e |
| 344 * @private | 369 * @private |
| 345 */ | 370 */ |
| 346 onSelectedFolderChanged_: function(e) { | 371 onSelectedFolderChanged_: function(e) { |
| 347 if (this.searchTerm) | 372 if (/** @type {boolean} */ (e.detail.clearSearch)) |
| 348 this.searchTerm = ''; | 373 this.searchTerm = ''; |
| 349 | 374 |
| 350 // Deselect the old folder if defined. | 375 this.selectFolder(/** @type {string} */ (e.detail.id)); |
| 351 if (this.selectedId && this.idToNodeMap_[this.selectedId]) | |
| 352 this.set( | |
| 353 this.idToNodeMap_[this.selectedId].path + '.isSelectedFolder', false); | |
| 354 | |
| 355 // Check if the selected id is that of a defined folder. | |
| 356 var id = /** @type {string} */ (e.detail); | |
| 357 if (!this.idToNodeMap_[id] || this.idToNodeMap_[id].url) | |
| 358 id = this.rootNode.children[0].id; | |
| 359 | |
| 360 var folder = this.idToNodeMap_[id]; | |
| 361 this.set(folder.path + '.isSelectedFolder', true); | |
| 362 this.selectedId = id; | |
| 363 | |
| 364 if (folder.id == this.rootNode.id) | |
| 365 return; | |
| 366 | |
| 367 var parent = this.idToNodeMap_[/** @type {?string} */ (folder.parentId)]; | |
| 368 while (parent) { | |
| 369 if (!parent.isOpen) { | |
| 370 this.fire('folder-open-changed', { | |
| 371 id: parent.id, | |
| 372 open: true, | |
| 373 }); | |
| 374 } | |
| 375 | |
| 376 parent = this.idToNodeMap_[/** @type {?string} */ (parent.parentId)]; | |
| 377 } | |
| 378 }, | 376 }, |
| 379 | 377 |
| 380 /** | 378 /** |
| 381 * Handles events that open and close folders. | 379 * Handles events that open and close folders. |
| 382 * @param {CustomEvent} e | 380 * @param {CustomEvent} e |
| 383 * @private | 381 * @private |
| 384 */ | 382 */ |
| 385 onFolderOpenChanged_: function(e) { | 383 onFolderOpenChanged_: function(e) { |
| 386 var folder = this.idToNodeMap_[e.detail.id]; | 384 var folder = this.idToNodeMap_[e.detail.id]; |
| 387 this.set(folder.path + '.isOpen', e.detail.open); | 385 this.set(folder.path + '.isOpen', e.detail.open); |
| 388 if (!folder.isOpen && this.isAncestorOfSelected_(folder)) | 386 if (!folder.isOpen && this.isAncestorOfSelected_(folder)) |
| 389 this.fire('selected-folder-changed', folder.id); | 387 this.selectFolder(folder.id); |
| 390 }, | 388 }, |
| 391 | 389 |
| 392 /** | 390 /** |
| 393 * Selects items according to keyboard behaviours. | 391 * Selects items according to keyboard behaviours. |
| 394 * @param {CustomEvent} e | 392 * @param {CustomEvent} e |
| 395 * @private | 393 * @private |
| 396 */ | 394 */ |
| 397 onItemSelected_: function(e) { | 395 onItemSelected_: function(e) { |
| 398 if (!e.detail.add) | 396 if (!e.detail.add) |
| 399 this.clearSelectedItems_(); | 397 this.clearSelectedItems_(); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 437 idToNodeMap[bookmarkNode.id] = bookmarkNode; | 435 idToNodeMap[bookmarkNode.id] = bookmarkNode; |
| 438 | 436 |
| 439 if (bookmarkNode.url) | 437 if (bookmarkNode.url) |
| 440 return; | 438 return; |
| 441 | 439 |
| 442 bookmarkNode.isSelectedFolder = false; | 440 bookmarkNode.isSelectedFolder = false; |
| 443 bookmarkNode.isOpen = true; | 441 bookmarkNode.isOpen = true; |
| 444 for (var i = 0; i < bookmarkNode.children.length; i++) | 442 for (var i = 0; i < bookmarkNode.children.length; i++) |
| 445 BookmarksStore.initNodes(bookmarkNode.children[i], idToNodeMap); | 443 BookmarksStore.initNodes(bookmarkNode.children[i], idToNodeMap); |
| 446 }; | 444 }; |
| OLD | NEW |