| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 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 'use strict'; | |
| 6 | |
| 7 //////////////////////////////////////////////////////////////////////////////// | |
| 8 // DirectoryTreeUtil | |
| 9 | |
| 10 /** | |
| 11 * Utility methods. They are intended for use only in this file. | |
| 12 */ | |
| 13 var DirectoryTreeUtil = {}; | |
| 14 | |
| 15 /** | |
| 16 * Generate a list of the directory entries for the top level on the tree. | |
| 17 * @return {Array.<DirectoryEntry>} Entries for the top level on the tree. | |
| 18 */ | |
| 19 DirectoryTreeUtil.generateTopLevelEntries = function() { | |
| 20 var entries = [ | |
| 21 DirectoryModel.fakeDriveEntry_, | |
| 22 DirectoryModel.fakeDriveOfflineEntry_, | |
| 23 DirectoryModel.fakeDriveSharedWithMeEntry_, | |
| 24 DirectoryModel.fakeDriveRecentEntry_, | |
| 25 ]; | |
| 26 | |
| 27 for (var i = 0; i < entries.length; i++) { | |
| 28 entries[i]['label'] = PathUtil.getRootLabel(entries[i].fullPath); | |
| 29 } | |
| 30 | |
| 31 return entries; | |
| 32 }; | |
| 33 | |
| 34 /** | |
| 35 * Checks if the given directory can be on the tree or not. | |
| 36 * | |
| 37 * @param {string} path Path to be checked. | |
| 38 * @return {boolean} True if the path is eligible for the directory tree. | |
| 39 * Otherwise, false. | |
| 40 */ | |
| 41 DirectoryTreeUtil.isEligiblePathForDirectoryTree = function(path) { | |
| 42 return PathUtil.isDriveBasedPath(path); | |
| 43 }; | |
| 44 | |
| 45 Object.freeze(DirectoryTreeUtil); | |
| 46 | |
| 47 //////////////////////////////////////////////////////////////////////////////// | |
| 48 // DirectoryTreeBase | |
| 49 | |
| 50 /** | |
| 51 * Implementation of methods for DirectoryTree and DirectoryItem. These classes | |
| 52 * inherits cr.ui.Tree/TreeItem so we can't make them inherit this class. | |
| 53 * Instead, we separate their implementations to this separate object and call | |
| 54 * it with setting 'this' from DirectoryTree/Item. | |
| 55 */ | |
| 56 var DirectoryItemTreeBaseMethods = {}; | |
| 57 | |
| 58 /** | |
| 59 * Updates sub-elements of {@code this} reading {@code DirectoryEntry}. | |
| 60 * The list of {@code DirectoryEntry} are not updated by this method. | |
| 61 * | |
| 62 * @param {boolean} recursive True if the all visible sub-directories are | |
| 63 * updated recursively including left arrows. If false, the update walks | |
| 64 * only immediate child directories without arrows. | |
| 65 */ | |
| 66 DirectoryItemTreeBaseMethods.updateSubElementsFromList = function(recursive) { | |
| 67 var index = 0; | |
| 68 var tree = this.parentTree_ || this; // If no parent, 'this' itself is tree. | |
| 69 while (this.entries_[index]) { | |
| 70 var currentEntry = this.entries_[index]; | |
| 71 var currentElement = this.items[index]; | |
| 72 | |
| 73 if (index >= this.items.length) { | |
| 74 var item = new DirectoryItem(currentEntry, this, tree); | |
| 75 this.add(item); | |
| 76 index++; | |
| 77 } else if (currentEntry.fullPath == currentElement.fullPath) { | |
| 78 if (recursive && this.expanded) | |
| 79 currentElement.updateSubDirectories(true /* recursive */); | |
| 80 | |
| 81 index++; | |
| 82 } else if (currentEntry.fullPath < currentElement.fullPath) { | |
| 83 var item = new DirectoryItem(currentEntry, this, tree); | |
| 84 this.addAt(item, index); | |
| 85 index++; | |
| 86 } else if (currentEntry.fullPath > currentElement.fullPath) { | |
| 87 this.remove(currentElement); | |
| 88 } | |
| 89 } | |
| 90 | |
| 91 var removedChild; | |
| 92 while (removedChild = this.items[index]) { | |
| 93 this.remove(removedChild); | |
| 94 } | |
| 95 | |
| 96 if (index == 0) { | |
| 97 this.hasChildren = false; | |
| 98 this.expanded = false; | |
| 99 } else { | |
| 100 this.hasChildren = true; | |
| 101 } | |
| 102 }; | |
| 103 | |
| 104 /** | |
| 105 * Finds a parent directory of the {@code entry} in {@code this}, and | |
| 106 * invokes the DirectoryItem.selectByEntry() of the found directory. | |
| 107 * | |
| 108 * @param {DirectoryEntry|Object} entry The entry to be searched for. Can be | |
| 109 * a fake. | |
| 110 * @return {boolean} True if the parent item is found. | |
| 111 */ | |
| 112 DirectoryItemTreeBaseMethods.searchAndSelectByEntry = function(entry) { | |
| 113 for (var i = 0; i < this.items.length; i++) { | |
| 114 var item = this.items[i]; | |
| 115 if (util.isParentEntry(item.entry, entry)) { | |
| 116 item.selectByEntry(entry); | |
| 117 return true; | |
| 118 } | |
| 119 } | |
| 120 return false; | |
| 121 }; | |
| 122 | |
| 123 Object.freeze(DirectoryItemTreeBaseMethods); | |
| 124 | |
| 125 //////////////////////////////////////////////////////////////////////////////// | |
| 126 // DirectoryItem | |
| 127 | |
| 128 /** | |
| 129 * A directory in the tree. Each element represents one directory. | |
| 130 * | |
| 131 * @param {DirectoryEntry} dirEntry DirectoryEntry of this item. | |
| 132 * @param {DirectoryItem|DirectoryTree} parentDirItem Parent of this item. | |
| 133 * @param {DirectoryTree} tree Current tree, which contains this item. | |
| 134 * @extends {cr.ui.TreeItem} | |
| 135 * @constructor | |
| 136 */ | |
| 137 function DirectoryItem(dirEntry, parentDirItem, tree) { | |
| 138 var item = cr.doc.createElement('div'); | |
| 139 DirectoryItem.decorate(item, dirEntry, parentDirItem, tree); | |
| 140 return item; | |
| 141 } | |
| 142 | |
| 143 /** | |
| 144 * @param {HTMLElement} el Element to be DirectoryItem. | |
| 145 * @param {DirectoryEntry} dirEntry DirectoryEntry of this item. | |
| 146 * @param {DirectoryItem|DirectoryTree} parentDirItem Parent of this item. | |
| 147 * @param {DirectoryTree} tree Current tree, which contains this item. | |
| 148 */ | |
| 149 DirectoryItem.decorate = | |
| 150 function(el, dirEntry, parentDirItem, tree) { | |
| 151 el.__proto__ = DirectoryItem.prototype; | |
| 152 (/** @type {DirectoryItem} */ el).decorate( | |
| 153 dirEntry, parentDirItem, tree); | |
| 154 }; | |
| 155 | |
| 156 DirectoryItem.prototype = { | |
| 157 __proto__: cr.ui.TreeItem.prototype, | |
| 158 | |
| 159 /** | |
| 160 * The DirectoryEntry corresponding to this DirectoryItem. This may be | |
| 161 * a dummy DirectoryEntry. | |
| 162 * @type {DirectoryEntry|Object} | |
| 163 */ | |
| 164 get entry() { | |
| 165 return this.dirEntry_; | |
| 166 }, | |
| 167 | |
| 168 /** | |
| 169 * The element containing the label text and the icon. | |
| 170 * @type {!HTMLElement} | |
| 171 * @override | |
| 172 */ | |
| 173 get labelElement() { | |
| 174 return this.firstElementChild.querySelector('.label'); | |
| 175 } | |
| 176 }; | |
| 177 | |
| 178 /** | |
| 179 * Calls DirectoryItemTreeBaseMethods.updateSubElementsFromList(). | |
| 180 * | |
| 181 * @param {boolean} recursive True if the all visible sub-directories are | |
| 182 * updated recursively including left arrows. If false, the update walks | |
| 183 * only immediate child directories without arrows. | |
| 184 */ | |
| 185 DirectoryItem.prototype.updateSubElementsFromList = function(recursive) { | |
| 186 DirectoryItemTreeBaseMethods.updateSubElementsFromList.call(this, recursive); | |
| 187 }; | |
| 188 | |
| 189 /** | |
| 190 * Calls DirectoryItemTreeBaseMethods.updateSubElementsFromList(). | |
| 191 * @param {DirectoryEntry|Object} entry The entry to be searched for. Can be | |
| 192 * a fake. | |
| 193 * @return {boolean} True if the parent item is found. | |
| 194 */ | |
| 195 DirectoryItem.prototype.searchAndSelectByEntry = function(entry) { | |
| 196 return DirectoryItemTreeBaseMethods.searchAndSelectByEntry.call(this, entry); | |
| 197 }; | |
| 198 | |
| 199 /** | |
| 200 * @param {DirectoryEntry} dirEntry DirectoryEntry of this item. | |
| 201 * @param {DirectoryItem|DirectoryTree} parentDirItem Parent of this item. | |
| 202 * @param {DirectoryTree} tree Current tree, which contains this item. | |
| 203 */ | |
| 204 DirectoryItem.prototype.decorate = function( | |
| 205 dirEntry, parentDirItem, tree) { | |
| 206 var path = dirEntry.fullPath; | |
| 207 var label; | |
| 208 label = dirEntry.label ? dirEntry.label : dirEntry.name; | |
| 209 | |
| 210 this.className = 'tree-item'; | |
| 211 this.innerHTML = | |
| 212 '<div class="tree-row">' + | |
| 213 ' <span class="expand-icon"></span>' + | |
| 214 ' <span class="icon"></span>' + | |
| 215 ' <span class="label"></span>' + | |
| 216 ' <div class="root-eject"></div>' + | |
| 217 '</div>' + | |
| 218 '<div class="tree-children"></div>'; | |
| 219 this.setAttribute('role', 'treeitem'); | |
| 220 | |
| 221 this.parentTree_ = tree; | |
| 222 this.directoryModel_ = tree.directoryModel; | |
| 223 this.parent_ = parentDirItem; | |
| 224 this.label = label; | |
| 225 this.fullPath = path; | |
| 226 this.dirEntry_ = dirEntry; | |
| 227 this.fileFilter_ = this.directoryModel_.getFileFilter(); | |
| 228 | |
| 229 // Sets hasChildren=false tentatively. This will be overridden after | |
| 230 // scanning sub-directories in DirectoryTreeUtil.updateSubElementsFromList. | |
| 231 this.hasChildren = false; | |
| 232 | |
| 233 this.addEventListener('expand', this.onExpand_.bind(this), false); | |
| 234 var icon = this.querySelector('.icon'); | |
| 235 icon.classList.add('volume-icon'); | |
| 236 var iconType = PathUtil.getRootType(path); | |
| 237 if (iconType && PathUtil.isRootPath(path)) | |
| 238 icon.setAttribute('volume-type-icon', iconType); | |
| 239 else | |
| 240 icon.setAttribute('file-type-icon', 'folder'); | |
| 241 | |
| 242 var eject = this.querySelector('.root-eject'); | |
| 243 eject.hidden = !PathUtil.isUnmountableByUser(path); | |
| 244 eject.addEventListener('click', | |
| 245 function(event) { | |
| 246 event.stopPropagation(); | |
| 247 if (!PathUtil.isUnmountableByUser(path)) | |
| 248 return; | |
| 249 | |
| 250 tree.volumeManager.unmount(path, function() {}, function() {}); | |
| 251 }.bind(this)); | |
| 252 | |
| 253 if (this.parentTree_.contextMenuForSubitems) | |
| 254 this.setContextMenu(this.parentTree_.contextMenuForSubitems); | |
| 255 // Adds handler for future change. | |
| 256 this.parentTree_.addEventListener( | |
| 257 'contextMenuForSubitemsChange', | |
| 258 function(e) { this.setContextMenu(e.newValue); }.bind(this)); | |
| 259 | |
| 260 if (parentDirItem.expanded) | |
| 261 this.updateSubDirectories(false /* recursive */); | |
| 262 }; | |
| 263 | |
| 264 /** | |
| 265 * Overrides WebKit's scrollIntoViewIfNeeded, which doesn't work well with | |
| 266 * a complex layout. This call is not necessary, so we are ignoring it. | |
| 267 * | |
| 268 * @param {boolean} unused Unused. | |
| 269 * @override | |
| 270 */ | |
| 271 DirectoryItem.prototype.scrollIntoViewIfNeeded = function(unused) { | |
| 272 }; | |
| 273 | |
| 274 /** | |
| 275 * Removes the child node, but without selecting the parent item, to avoid | |
| 276 * unintended changing of directories. Removing is done externally, and other | |
| 277 * code will navigate to another directory. | |
| 278 * | |
| 279 * @param {!cr.ui.TreeItem} child The tree item child to remove. | |
| 280 * @override | |
| 281 */ | |
| 282 DirectoryItem.prototype.remove = function(child) { | |
| 283 this.lastElementChild.removeChild(child); | |
| 284 if (this.items.length == 0) | |
| 285 this.hasChildren = false; | |
| 286 }; | |
| 287 | |
| 288 /** | |
| 289 * Invoked when the item is being expanded. | |
| 290 * @param {!UIEvent} e Event. | |
| 291 * @private | |
| 292 **/ | |
| 293 DirectoryItem.prototype.onExpand_ = function(e) { | |
| 294 this.updateSubDirectories( | |
| 295 true /* recursive */, | |
| 296 function() {}, | |
| 297 function() { | |
| 298 this.expanded = false; | |
| 299 }.bind(this)); | |
| 300 | |
| 301 e.stopPropagation(); | |
| 302 }; | |
| 303 | |
| 304 /** | |
| 305 * Retrieves the latest subdirectories and update them on the tree. | |
| 306 * @param {boolean} recursive True if the update is recursively. | |
| 307 * @param {function()=} opt_successCallback Callback called on success. | |
| 308 * @param {function()=} opt_errorCallback Callback called on error. | |
| 309 */ | |
| 310 DirectoryItem.prototype.updateSubDirectories = function( | |
| 311 recursive, opt_successCallback, opt_errorCallback) { | |
| 312 if (util.isFakeDirectoryEntry(this.entry)) { | |
| 313 if (opt_errorCallback) | |
| 314 opt_errorCallback(); | |
| 315 return; | |
| 316 } | |
| 317 | |
| 318 var sortEntries = function(fileFilter, entries) { | |
| 319 entries.sort(function(a, b) { | |
| 320 return (a.name.toLowerCase() > b.name.toLowerCase()) ? 1 : -1; | |
| 321 }); | |
| 322 return entries.filter(fileFilter.filter.bind(fileFilter)); | |
| 323 }; | |
| 324 | |
| 325 var onSuccess = function(entries) { | |
| 326 this.entries_ = entries; | |
| 327 this.redrawSubDirectoryList_(recursive); | |
| 328 opt_successCallback && opt_successCallback(); | |
| 329 }.bind(this); | |
| 330 | |
| 331 var reader = this.entry.createReader(); | |
| 332 var entries = []; | |
| 333 var readEntry = function() { | |
| 334 reader.readEntries(function(results) { | |
| 335 if (!results.length) { | |
| 336 onSuccess(sortEntries(this.fileFilter_, entries)); | |
| 337 return; | |
| 338 } | |
| 339 | |
| 340 for (var i = 0; i < results.length; i++) { | |
| 341 var entry = results[i]; | |
| 342 if (entry.isDirectory) | |
| 343 entries.push(entry); | |
| 344 } | |
| 345 readEntry(); | |
| 346 }.bind(this)); | |
| 347 }.bind(this); | |
| 348 readEntry(); | |
| 349 }; | |
| 350 | |
| 351 /** | |
| 352 * Updates sub-elements of {@code parentElement} reading {@code DirectoryEntry} | |
| 353 * with calling {@code iterator}. | |
| 354 * | |
| 355 * @param {string} changedDirectryPath The path of the changed directory. | |
| 356 */ | |
| 357 DirectoryItem.prototype.updateItemByPath = function(changedDirectryPath) { | |
| 358 if (changedDirectryPath === this.entry.fullPath) { | |
| 359 this.updateSubDirectories(false /* recursive */); | |
| 360 return; | |
| 361 } | |
| 362 | |
| 363 for (var i = 0; i < this.items.length; i++) { | |
| 364 var item = this.items[i]; | |
| 365 if (PathUtil.isParentPath(item.entry.fullPath, changedDirectryPath)) { | |
| 366 item.updateItemByPath(changedDirectryPath); | |
| 367 break; | |
| 368 } | |
| 369 } | |
| 370 }; | |
| 371 | |
| 372 /** | |
| 373 * Redraw subitems with the latest information. The items are sorted in | |
| 374 * alphabetical order, case insensitive. | |
| 375 * @param {boolean} recursive True if the update is recursively. | |
| 376 * @private | |
| 377 */ | |
| 378 DirectoryItem.prototype.redrawSubDirectoryList_ = function(recursive) { | |
| 379 this.updateSubElementsFromList(recursive); | |
| 380 }; | |
| 381 | |
| 382 /** | |
| 383 * Select the item corresponding to the given {@code entry}. | |
| 384 * @param {DirectoryEntry|Object} entry The entry to be selected. Can be a fake. | |
| 385 */ | |
| 386 DirectoryItem.prototype.selectByEntry = function(entry) { | |
| 387 if (util.isSameEntry(entry, this.entry)) { | |
| 388 this.selected = true; | |
| 389 return; | |
| 390 } | |
| 391 | |
| 392 if (this.searchAndSelectByEntry(entry)) | |
| 393 return; | |
| 394 | |
| 395 // If the path doesn't exist, updates sub directories and tryes again. | |
| 396 this.updateSubDirectories( | |
| 397 false /* recursive */, | |
| 398 this.searchAndSelectByEntry.bind(this, entry)); | |
| 399 }; | |
| 400 | |
| 401 /** | |
| 402 * Executes the assigned action as a drop target. | |
| 403 */ | |
| 404 DirectoryItem.prototype.doDropTargetAction = function() { | |
| 405 this.expanded = true; | |
| 406 }; | |
| 407 | |
| 408 /** | |
| 409 * Executes the assigned action. DirectoryItem performs changeDirectory. | |
| 410 */ | |
| 411 DirectoryItem.prototype.doAction = function() { | |
| 412 if (this.fullPath != this.directoryModel_.getCurrentDirPath()) | |
| 413 this.directoryModel_.changeDirectory(this.fullPath); | |
| 414 }; | |
| 415 | |
| 416 /** | |
| 417 * Sets the context menu for directory tree. | |
| 418 * @param {cr.ui.Menu} menu Menu to be set. | |
| 419 */ | |
| 420 DirectoryItem.prototype.setContextMenu = function(menu) { | |
| 421 if (this.entry && PathUtil.isEligibleForFolderShortcut(this.entry.fullPath)) | |
| 422 cr.ui.contextMenuHandler.setContextMenu(this, menu); | |
| 423 }; | |
| 424 | |
| 425 //////////////////////////////////////////////////////////////////////////////// | |
| 426 // DirectoryTree | |
| 427 | |
| 428 /** | |
| 429 * Tree of directories on the middle bar. This element is also the root of | |
| 430 * items, in other words, this is the parent of the top-level items. | |
| 431 * | |
| 432 * @constructor | |
| 433 * @extends {cr.ui.Tree} | |
| 434 */ | |
| 435 function DirectoryTree() {} | |
| 436 | |
| 437 /** | |
| 438 * Decorates an element. | |
| 439 * @param {HTMLElement} el Element to be DirectoryTree. | |
| 440 * @param {DirectoryModel} directoryModel Current DirectoryModel. | |
| 441 * @param {VolumeManagerWrapper} volumeManager VolumeManager of the system. | |
| 442 */ | |
| 443 DirectoryTree.decorate = function(el, directoryModel, volumeManager) { | |
| 444 el.__proto__ = DirectoryTree.prototype; | |
| 445 (/** @type {DirectoryTree} */ el).decorate(directoryModel, volumeManager); | |
| 446 }; | |
| 447 | |
| 448 DirectoryTree.prototype = { | |
| 449 __proto__: cr.ui.Tree.prototype, | |
| 450 | |
| 451 // DirectoryTree is always expanded. | |
| 452 get expanded() { return true; }, | |
| 453 /** | |
| 454 * @param {boolean} value Not used. | |
| 455 */ | |
| 456 set expanded(value) {}, | |
| 457 | |
| 458 /** | |
| 459 * The DirectoryEntry corresponding to this DirectoryItem. This may be | |
| 460 * a dummy DirectoryEntry. | |
| 461 * @type {DirectoryEntry|Object} | |
| 462 * @override | |
| 463 **/ | |
| 464 get entry() { | |
| 465 return this.dirEntry_; | |
| 466 }, | |
| 467 | |
| 468 /** | |
| 469 * The DirectoryModel this tree corresponds to. | |
| 470 * @type {DirectoryModel} | |
| 471 */ | |
| 472 get directoryModel() { | |
| 473 return this.directoryModel_; | |
| 474 }, | |
| 475 | |
| 476 /** | |
| 477 * The VolumeManager instance of the system. | |
| 478 * @type {VolumeManager} | |
| 479 */ | |
| 480 get volumeManager() { | |
| 481 return this.volumeManager_; | |
| 482 }, | |
| 483 }; | |
| 484 | |
| 485 cr.defineProperty(DirectoryTree, 'contextMenuForSubitems', cr.PropertyKind.JS); | |
| 486 | |
| 487 /** | |
| 488 * Calls DirectoryItemTreeBaseMethods.updateSubElementsFromList(). | |
| 489 * | |
| 490 * @param {boolean} recursive True if the all visible sub-directories are | |
| 491 * updated recursively including left arrows. If false, the update walks | |
| 492 * only immediate child directories without arrows. | |
| 493 */ | |
| 494 DirectoryTree.prototype.updateSubElementsFromList = function(recursive) { | |
| 495 DirectoryItemTreeBaseMethods.updateSubElementsFromList.call(this, recursive); | |
| 496 }; | |
| 497 | |
| 498 /** | |
| 499 * Calls DirectoryItemTreeBaseMethods.updateSubElementsFromList(). | |
| 500 * @param {DirectoryEntry|Object} entry The entry to be searched for. Can be | |
| 501 * a fake. | |
| 502 * @return {boolean} True if the parent item is found. | |
| 503 */ | |
| 504 DirectoryTree.prototype.searchAndSelectByEntry = function(entry) { | |
| 505 return DirectoryItemTreeBaseMethods.searchAndSelectByEntry.call(this, entry); | |
| 506 }; | |
| 507 | |
| 508 /** | |
| 509 * Decorates an element. | |
| 510 * @param {DirectoryModel} directoryModel Current DirectoryModel. | |
| 511 * @param {VolumeManagerWrapper} volumeManager VolumeManager of the system. | |
| 512 */ | |
| 513 DirectoryTree.prototype.decorate = function(directoryModel, volumeManager) { | |
| 514 cr.ui.Tree.prototype.decorate.call(this); | |
| 515 | |
| 516 this.directoryModel_ = directoryModel; | |
| 517 this.volumeManager_ = volumeManager; | |
| 518 this.entries_ = DirectoryTreeUtil.generateTopLevelEntries(); | |
| 519 | |
| 520 this.fileFilter_ = this.directoryModel_.getFileFilter(); | |
| 521 this.fileFilter_.addEventListener('changed', | |
| 522 this.onFilterChanged_.bind(this)); | |
| 523 | |
| 524 this.directoryModel_.addEventListener('directory-changed', | |
| 525 this.onCurrentDirectoryChanged_.bind(this)); | |
| 526 | |
| 527 // Add a handler for directory change. | |
| 528 this.addEventListener('change', function() { | |
| 529 if (this.selectedItem && | |
| 530 (!this.currentEntry_ || | |
| 531 !util.isSameEntry(this.currentEntry_, this.selectedItem.entry))) { | |
| 532 this.currentEntry_ = this.selectedItem.entry; | |
| 533 this.selectedItem.doAction(); | |
| 534 return; | |
| 535 } | |
| 536 }.bind(this)); | |
| 537 | |
| 538 this.privateOnDirectoryChangedBound_ = | |
| 539 this.onDirectoryContentChanged_.bind(this); | |
| 540 chrome.fileBrowserPrivate.onDirectoryChanged.addListener( | |
| 541 this.privateOnDirectoryChangedBound_); | |
| 542 | |
| 543 this.scrollBar_ = MainPanelScrollBar(); | |
| 544 this.scrollBar_.initialize(this.parentNode, this); | |
| 545 | |
| 546 // Once, draws the list with the fake '/drive/' entry. | |
| 547 this.redraw(false /* recursive */); | |
| 548 // Resolves 'My Drive' entry and replaces the fake with the true one. | |
| 549 this.maybeResolveMyDriveRoot_(function() { | |
| 550 // After the true entry is resolved, draws the list again. | |
| 551 this.redraw(true /* recursive */); | |
| 552 }.bind(this)); | |
| 553 }; | |
| 554 | |
| 555 /** | |
| 556 * Select the item corresponding to the given entry. | |
| 557 * @param {DirectoryEntry|Object} entry The directory entry to be selected. Can | |
| 558 * be a fake. | |
| 559 */ | |
| 560 DirectoryTree.prototype.selectByEntry = function(entry) { | |
| 561 // If the target directory is not in the tree, do nothing. | |
| 562 if (!DirectoryTreeUtil.isEligiblePathForDirectoryTree(entry.fullPath)) | |
| 563 return; | |
| 564 | |
| 565 this.maybeResolveMyDriveRoot_(function() { | |
| 566 if (this.selectedItem && util.isSameEntry(entry, this.selectedItem.entry)) | |
| 567 return; | |
| 568 | |
| 569 if (this.searchAndSelectByEntry(entry)) | |
| 570 return; | |
| 571 | |
| 572 this.selectedItem = null; | |
| 573 this.updateSubDirectories( | |
| 574 false /* recursive */, | |
| 575 // Success callback, failure is not handled. | |
| 576 function() { | |
| 577 if (!this.searchAndSelectByEntry(entry)) | |
| 578 this.selectedItem = null; | |
| 579 }.bind(this)); | |
| 580 }.bind(this)); | |
| 581 }; | |
| 582 | |
| 583 /** | |
| 584 * Resolves the My Drive root's entry, if it is a fake. If the entry is already | |
| 585 * resolved to a DirectoryEntry, completionCallback() will be called | |
| 586 * immediately. | |
| 587 * @param {function()} completionCallback Called when the resolving is | |
| 588 * done (or the entry is already resolved), regardless if it is | |
| 589 * successfully done or not. | |
| 590 * @private | |
| 591 */ | |
| 592 DirectoryTree.prototype.maybeResolveMyDriveRoot_ = function( | |
| 593 completionCallback) { | |
| 594 var myDriveItem = this.items[0]; | |
| 595 if (!util.isFakeDirectoryEntry(myDriveItem.entry)) { | |
| 596 // The entry is already resolved. Don't need to try again. | |
| 597 completionCallback(); | |
| 598 return; | |
| 599 } | |
| 600 | |
| 601 // The entry is a fake. | |
| 602 this.directoryModel_.resolveDirectory( | |
| 603 myDriveItem.fullPath, | |
| 604 function(entry) { | |
| 605 if (!util.isFakeDirectoryEntry(entry)) { | |
| 606 myDriveItem.dirEntry_ = entry; | |
| 607 } | |
| 608 | |
| 609 completionCallback(); | |
| 610 }, | |
| 611 completionCallback); | |
| 612 }; | |
| 613 | |
| 614 /** | |
| 615 * Retrieves the latest subdirectories and update them on the tree. | |
| 616 * @param {boolean} recursive True if the update is recursively. | |
| 617 * @param {function()=} opt_successCallback Callback called on success. | |
| 618 * @param {function()=} opt_errorCallback Callback called on error. | |
| 619 */ | |
| 620 DirectoryTree.prototype.updateSubDirectories = function( | |
| 621 recursive, opt_successCallback, opt_errorCallback) { | |
| 622 this.entries_ = DirectoryTreeUtil.generateTopLevelEntries(); | |
| 623 this.redraw(recursive); | |
| 624 if (opt_successCallback) | |
| 625 opt_successCallback(); | |
| 626 }; | |
| 627 | |
| 628 /** | |
| 629 * Redraw the list. | |
| 630 * @param {boolean} recursive True if the update is recursively. False if the | |
| 631 * only root items are updated. | |
| 632 */ | |
| 633 DirectoryTree.prototype.redraw = function(recursive) { | |
| 634 this.updateSubElementsFromList(recursive); | |
| 635 }; | |
| 636 | |
| 637 /** | |
| 638 * Invoked when the filter is changed. | |
| 639 * @private | |
| 640 */ | |
| 641 DirectoryTree.prototype.onFilterChanged_ = function() { | |
| 642 // Returns immediately, if the tree is hidden. | |
| 643 if (this.hidden) | |
| 644 return; | |
| 645 | |
| 646 this.redraw(true /* recursive */); | |
| 647 }; | |
| 648 | |
| 649 /** | |
| 650 * Invoked when a directory is changed. | |
| 651 * @param {!UIEvent} event Event. | |
| 652 * @private | |
| 653 */ | |
| 654 DirectoryTree.prototype.onDirectoryContentChanged_ = function(event) { | |
| 655 if (event.eventType == 'changed') { | |
| 656 var path = util.extractFilePath(event.directoryUrl); | |
| 657 if (!DirectoryTreeUtil.isEligiblePathForDirectoryTree(path)) | |
| 658 return; | |
| 659 | |
| 660 var myDriveItem = this.items[0]; | |
| 661 myDriveItem.updateItemByPath(path); | |
| 662 } | |
| 663 }; | |
| 664 | |
| 665 /** | |
| 666 * Invoked when the current directory is changed. | |
| 667 * @param {!UIEvent} event Event. | |
| 668 * @private | |
| 669 */ | |
| 670 DirectoryTree.prototype.onCurrentDirectoryChanged_ = function(event) { | |
| 671 this.selectByEntry(event.newDirEntry); | |
| 672 }; | |
| 673 | |
| 674 /** | |
| 675 * Sets the margin height for the transparent preview panel at the bottom. | |
| 676 * @param {number} margin Margin to be set in px. | |
| 677 */ | |
| 678 DirectoryTree.prototype.setBottomMarginForPanel = function(margin) { | |
| 679 this.style.paddingBottom = margin + 'px'; | |
| 680 this.scrollBar_.setBottomMarginForPanel(margin); | |
| 681 }; | |
| 682 | |
| 683 /** | |
| 684 * Updates the UI after the layout has changed. | |
| 685 */ | |
| 686 DirectoryTree.prototype.relayout = function() { | |
| 687 cr.dispatchSimpleEvent(this, 'relayout'); | |
| 688 }; | |
| OLD | NEW |