| Index: ui/file_manager/file_manager/foreground/js/directory_tree.js
|
| diff --git a/ui/file_manager/file_manager/foreground/js/directory_tree.js b/ui/file_manager/file_manager/foreground/js/directory_tree.js
|
| index a689242c98aaaacd383259dea833949c7849feb5..aa87168bfdaa34a16119ec825d9c18e96237698a 100644
|
| --- a/ui/file_manager/file_manager/foreground/js/directory_tree.js
|
| +++ b/ui/file_manager/file_manager/foreground/js/directory_tree.js
|
| @@ -85,6 +85,14 @@ DirectoryItemTreeBaseMethods.searchAndSelectByEntry = function(entry) {
|
|
|
| Object.freeze(DirectoryItemTreeBaseMethods);
|
|
|
| +var TREE_ITEM_INNTER_HTML =
|
| + '<div class="tree-row">' +
|
| + ' <span class="expand-icon"></span>' +
|
| + ' <span class="icon"></span>' +
|
| + ' <span class="label entry-name"></span>' +
|
| + '</div>' +
|
| + '<div class="tree-children"></div>';
|
| +
|
| ////////////////////////////////////////////////////////////////////////////////
|
| // DirectoryItem
|
|
|
| @@ -170,14 +178,7 @@ DirectoryItem.prototype.searchAndSelectByEntry = function(entry) {
|
| */
|
| DirectoryItem.prototype.decorate = function(
|
| label, dirEntry, parentDirItem, tree) {
|
| - this.innerHTML =
|
| - '<div class="tree-row">' +
|
| - ' <span class="expand-icon"></span>' +
|
| - ' <span class="icon"></span>' +
|
| - ' <span class="label entry-name"></span>' +
|
| - '</div>' +
|
| - '<div class="tree-children"></div>';
|
| -
|
| + this.innerHTML = TREE_ITEM_INNTER_HTML;
|
| this.parentTree_ = tree;
|
| this.directoryModel_ = tree.directoryModel;
|
| this.parent_ = parentDirItem;
|
| @@ -339,11 +340,11 @@ DirectoryItem.prototype.updateItemByEntry = function(changedDirectoryEntry) {
|
| */
|
| DirectoryItem.prototype.updateSharedStatusIcon = function() {
|
| var icon = this.querySelector('.icon');
|
| - this.parentTree_.metadataCache.getOne(
|
| - this.dirEntry_,
|
| + this.parentTree_.metadataCache.getLatest(
|
| + [this.dirEntry_],
|
| 'drive',
|
| function(metadata) {
|
| - icon.classList.toggle('shared', metadata && metadata.shared);
|
| + icon.classList.toggle('shared', metadata[0] && metadata[0].shared);
|
| });
|
| };
|
|
|
| @@ -394,6 +395,378 @@ DirectoryItem.prototype.setContextMenu = function(menu) {
|
| cr.ui.contextMenuHandler.setContextMenu(this, menu);
|
| };
|
|
|
| +DirectoryItem.prototype.activate = function() {
|
| + this.parentTree_.directoryModel.activateDirectoryEntry(this.entry);
|
| +};
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// VolumeItem
|
| +
|
| +/**
|
| + * A TreeItem which represents a volume. Volume items are displayed as
|
| + * top-level children of DirectoryTree.
|
| + *
|
| + * @param {DirectoryEntry} dirEntry DirectoryEntry of this item.
|
| + * @param {NavigationModelItem} modelItem NavigationModelItem of this volume.
|
| + * @param {DirectoryTree} tree Current tree, which contains this item.
|
| + * @extends {cr.ui.TreeItem}
|
| + * @constructor
|
| + */
|
| +function VolumeItem(entry, modelItem, tree) {
|
| + var item = new cr.ui.TreeItem();
|
| + item.__proto__ = VolumeItem.prototype;
|
| + item.decorate(entry, modelItem, tree);
|
| + return item;
|
| +}
|
| +
|
| +VolumeItem.prototype = {
|
| + __proto__: cr.ui.TreeItem.prototype,
|
| + get entry() {
|
| + return this.dirEntry_;
|
| + },
|
| + get modelItem() {
|
| + return this.modelItem_;
|
| + },
|
| + get volumeInfo() {
|
| + return this.volumeInfo_;
|
| + },
|
| + get labelElement() {
|
| + return this.firstElementChild.querySelector('.label');
|
| + },
|
| + // Overrides the property 'expanded' to prevent volume items from shrinking.
|
| + get expanded() {
|
| + return Object.getOwnPropertyDescriptor(
|
| + cr.ui.TreeItem.prototype, 'expanded').get.call(this);
|
| + },
|
| + set expanded(b) {
|
| + if (!b)
|
| + return;
|
| + Object.getOwnPropertyDescriptor(
|
| + cr.ui.TreeItem.prototype, 'expanded').set.call(this, b);
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Calls DirectoryItemTreeBaseMethods.updateSubElementsFromList().
|
| + *
|
| + * @param {DirectoryEntry|Object} entry The entry to be searched for. Can be
|
| + * a fake.
|
| + * @return {boolean} True if the parent item is found.
|
| + */
|
| +VolumeItem.prototype.searchAndSelectByEntry = function(entry) {
|
| + return DirectoryItemTreeBaseMethods.searchAndSelectByEntry.call(this, entry);
|
| +};
|
| +
|
| +/**
|
| + * Decorates this element.
|
| + * @param {DirectoryEntry} dirEntry DirectoryEntry of this item.
|
| + * @param {NavigationModelItem} modelItem NavigationModelItem of this volume.
|
| + * @param {DirectoryTree} tree Current tree, which contains this item.
|
| + */
|
| +VolumeItem.prototype.decorate = function(entry, modelItem, tree) {
|
| + this.innerHTML = TREE_ITEM_INNTER_HTML;
|
| + this.parentTree_ = tree;
|
| + this.dirEntry_ = entry;
|
| + this.modelItem_ = modelItem;
|
| + this.volumeInfo_ = modelItem.volumeInfo;
|
| + this.label = modelItem.volumeInfo.label;
|
| +
|
| + this.setupIcon_(this.querySelector('.icon'), this.volumeInfo);
|
| + this.setupEjectButton_(this.rowElement);
|
| + if (tree.contextMenuForRootItems)
|
| + this.setContextMenu(tree.contextMenuForRootItems);
|
| +
|
| + this.updateSubDirectories(false /* recursive */);
|
| +};
|
| +
|
| +/**
|
| + * Invoked when the tree item is clicked.
|
| + *
|
| + * @param {Event} e Click event.
|
| + * @override
|
| + */
|
| +VolumeItem.prototype.handleClick = function(e) {
|
| + cr.ui.TreeItem.prototype.handleClick.call(this, e);
|
| +
|
| + // Resets file selection when a volume is clicked.
|
| + this.parentTree_.directoryModel.clearSelection();
|
| +
|
| + // If the Drive volume is clicked, select one of the children instead of this
|
| + // item itself.
|
| + if (this.isDrive())
|
| + this.searchAndSelectByEntry(this.entry);
|
| +};
|
| +
|
| +/**
|
| + * Retrieves the latest subdirectories and update them on the tree.
|
| + * @param {boolean} recursive True if the update is recursively.
|
| + */
|
| +VolumeItem.prototype.updateSubDirectories = function(recursive) {
|
| + // Drive volume has children including fake entries (offline, recent, etc...).
|
| + if (this.isDrive()) {
|
| + var entries = [this.entry];
|
| + if (this.parentTree_.fakeEntriesVisible_) {
|
| + for (var key in this.volumeInfo.fakeEntries)
|
| + entries.push(this.volumeInfo.fakeEntries[key]);
|
| + }
|
| + entries.sort(function(a, b) { return a.toURL() < b.toURL(); });
|
| +
|
| + for (var i = 0; i < entries.length; i++) {
|
| + var item = new DirectoryItem(
|
| + util.getEntryLabel(this.parentTree_.volumeManager_, entries[i]),
|
| + entries[i], this, this.parentTree_);
|
| + this.add(item);
|
| + item.updateSubDirectories(false);
|
| + }
|
| + this.expanded = true;
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Searches for the changed directory in the current subtree, and if it is found
|
| + * then updates it.
|
| + *
|
| + * @param {DirectoryEntry} changedDirectoryEntry The entry ot the changed
|
| + * directory.
|
| + */
|
| +VolumeItem.prototype.updateItemByEntry = function(changedDirectoryEntry) {
|
| + if (this.isDrive())
|
| + this.items[0].updateItemByEntry(changedDirectoryEntry);
|
| +};
|
| +
|
| +/**
|
| + * Select the item corresponding to the given entry.
|
| + * @param {DirectoryEntry|Object} entry The directory entry to be selected. Can
|
| + * be a fake.
|
| + */
|
| +VolumeItem.prototype.selectByEntry = function(entry) {
|
| + // If this volume is drive, find the item to be selected amang children.
|
| + if (this.isDrive()) {
|
| + this.searchAndSelectByEntry(entry);
|
| + return;
|
| + }
|
| + if (util.isSameEntry(entry, this.entry))
|
| + this.selected = true;
|
| +};
|
| +
|
| +/**
|
| + * Sets the context menu for volume items.
|
| + * @param {cr.ui.Menu} menu Menu to be set.
|
| + */
|
| +VolumeItem.prototype.setContextMenu = function(menu) {
|
| + if (this.isRemovable_())
|
| + cr.ui.contextMenuHandler.setContextMenu(this, menu);
|
| +};
|
| +
|
| +/**
|
| + * Change current entry to this volume's root directory.
|
| + */
|
| +VolumeItem.prototype.activate = function() {
|
| + var directoryModel = this.parentTree_.directoryModel;
|
| + var onEntryResolved = function(entry) {
|
| + // Changes directory to the model item's root directory if needed.
|
| + if (!util.isSameEntry(directoryModel.getCurrentDirEntry(), entry)) {
|
| + metrics.recordUserAction('FolderShortcut.Navigate');
|
| + directoryModel.changeDirectoryEntry(entry);
|
| + }
|
| + }.bind(this);
|
| +
|
| + this.volumeInfo.resolveDisplayRoot(
|
| + onEntryResolved,
|
| + function() {
|
| + // Error, the display root is not available. It may happen on Drive.
|
| + this.parentTree_.dataModel.onItemNotFoundError(this.modelItem);
|
| + }.bind(this))
|
| +};
|
| +
|
| +/**
|
| + * @return {boolean} True if this is Drive volume.
|
| + */
|
| +VolumeItem.prototype.isDrive = function() {
|
| + return this.volumeInfo.volumeType === VolumeManagerCommon.VolumeType.DRIVE;
|
| +};
|
| +
|
| +/**
|
| + * @return {boolean} True if this volume can be removed by user operation.
|
| + * @private
|
| + */
|
| +VolumeItem.prototype.isRemovable_ = function() {
|
| + var volumeType = this.volumeInfo.volumeType;
|
| + return volumeType === VolumeManagerCommon.VolumeType.ARCHIVE ||
|
| + volumeType === VolumeManagerCommon.VolumeType.REMOVABLE ||
|
| + volumeType === VolumeManagerCommon.VolumeType.PROVIDED;
|
| +};
|
| +
|
| +/**
|
| + * Set up icon of this volume item.
|
| + * @param {HTMLElement} icon Icon element to be setup.
|
| + * @param {VolumeInfo} volumeInfo VolumeInfo determines the icon type.
|
| + * @private
|
| + */
|
| +VolumeItem.prototype.setupIcon_ = function(icon, volumeInfo) {
|
| + icon.classList.add('volume-icon');
|
| + if (volumeInfo.volumeType === 'provided') {
|
| + var backgroundImage = '-webkit-image-set(' +
|
| + 'url(chrome://extension-icon/' + volumeInfo.extensionId +
|
| + '/24/1) 1x, ' +
|
| + 'url(chrome://extension-icon/' + volumeInfo.extensionId +
|
| + '/48/1) 2x);';
|
| + // The icon div is not yet added to DOM, therefore it is impossible to
|
| + // use style.backgroundImage.
|
| + icon.setAttribute(
|
| + 'style', 'background-image: ' + backgroundImage);
|
| + }
|
| + icon.setAttribute('volume-type-icon', volumeInfo.volumeType);
|
| + icon.setAttribute('volume-subtype', volumeInfo.deviceType);
|
| +};
|
| +
|
| +/**
|
| + * Set up eject button if needed.
|
| + * @param {HTMLElement} rowElement The parent element for eject button.
|
| + * @private
|
| + */
|
| +VolumeItem.prototype.setupEjectButton_ = function(rowElement) {
|
| + if (this.isRemovable_()) {
|
| + var ejectButton = cr.doc.createElement('div');
|
| + // Block other mouse handlers.
|
| + ejectButton.addEventListener(
|
| + 'mouseup', function(event) { event.stopPropagation() });
|
| + ejectButton.addEventListener(
|
| + 'mousedown', function(event) { event.stopPropagation() });
|
| + ejectButton.className = 'root-eject';
|
| + ejectButton.addEventListener('click', function(event) {
|
| + event.stopPropagation();
|
| + var unmountCommand = cr.doc.querySelector('command#unmount');
|
| + // Let's make sure 'canExecute' state of the command is properly set for
|
| + // the root before executing it.
|
| + unmountCommand.canExecuteChange(this);
|
| + unmountCommand.execute(this);
|
| + }.bind(this));
|
| + rowElement.appendChild(ejectButton);
|
| + }
|
| +};
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// ShortcutItem
|
| +
|
| +/**
|
| + * A TreeItem which represents a shortcut for Drive folder.
|
| + * Shortcut items are displayed as top-level children of DirectoryTree.
|
| + *
|
| + * @param {DirectoryEntry} dirEntry DirectoryEntry of this item.
|
| + * @param {NavigationModelItem} modelItem NavigationModelItem of this volume.
|
| + * @param {DirectoryTree} tree Current tree, which contains this item.
|
| + * @extends {cr.ui.TreeItem}
|
| + * @constructor
|
| + */
|
| +function ShortcutItem(dirEntry, modelItem, tree) {
|
| + var item = new cr.ui.TreeItem();
|
| + item.__proto__ = ShortcutItem.prototype;
|
| + item.decorate(dirEntry, modelItem, tree);
|
| + return item;
|
| +}
|
| +
|
| +ShortcutItem.prototype = {
|
| + __proto__: cr.ui.TreeItem.prototype,
|
| + get entry() {
|
| + return this.dirEntry_;
|
| + },
|
| + get modelItem() {
|
| + return this.modelItem_;
|
| + },
|
| + get labelElement() {
|
| + return this.firstElementChild.querySelector('.label');
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Finds a parent directory of the {@code entry} in {@code this}, and
|
| + * invokes the DirectoryItem.selectByEntry() of the found directory.
|
| + *
|
| + * @param {DirectoryEntry|Object} entry The entry to be searched for. Can be
|
| + * a fake.
|
| + * @return {boolean} True if the parent item is found.
|
| + */
|
| +ShortcutItem.prototype.searchAndSelectByEntry = function(entry) {
|
| + // Always false as shortcuts have no children.
|
| + return false;
|
| +};
|
| +
|
| +/**
|
| + * Decorates this element.
|
| + * @param {DirectoryEntry} dirEntry DirectoryEntry of this item.
|
| + * @param {NavigationModelItem} modelItem NavigationModelItem of this volume.
|
| + * @param {DirectoryTree} tree Current tree, which contains this item.
|
| + */
|
| +ShortcutItem.prototype.decorate = function(dirEntry, modelItem, tree) {
|
| + this.innerHTML = TREE_ITEM_INNTER_HTML;
|
| + this.parentTree_ = tree;
|
| + this.label = dirEntry.name;
|
| + this.dirEntry_ = dirEntry;
|
| + this.modelItem_ = modelItem;
|
| +
|
| + var icon = this.querySelector('.icon');
|
| + icon.classList.add('volume-icon');
|
| + icon.setAttribute('volume-type-icon', VolumeManagerCommon.VolumeType.DRIVE);
|
| +
|
| + if (tree.contextMenuForRootItems)
|
| + this.setContextMenu(tree.contextMenuForRootItems);
|
| +};
|
| +
|
| +/**
|
| + * Invoked when the tree item is clicked.
|
| + *
|
| + * @param {Event} e Click event.
|
| + * @override
|
| + */
|
| +ShortcutItem.prototype.handleClick = function(e) {
|
| + cr.ui.TreeItem.prototype.handleClick.call(this, e);
|
| + this.parentTree_.directoryModel.clearSelection();
|
| +};
|
| +
|
| +/**
|
| + * Select the item corresponding to the given entry.
|
| + * @param {DirectoryEntry} entry The directory entry to be selected.
|
| + */
|
| +ShortcutItem.prototype.selectByEntry = function(entry) {
|
| + if (util.isSameEntry(entry, this.entry))
|
| + this.selected = true;
|
| +};
|
| +
|
| +/**
|
| + * Sets the context menu for shortcut items.
|
| + * @param {cr.ui.Menu} menu Menu to be set.
|
| + */
|
| +ShortcutItem.prototype.setContextMenu = function(menu) {
|
| + cr.ui.contextMenuHandler.setContextMenu(this, menu);
|
| +};
|
| +
|
| +/**
|
| + * Change current entry to the entry corresponding to this shortcut.
|
| + */
|
| +ShortcutItem.prototype.activate = function() {
|
| + var directoryModel = this.parentTree_.directoryModel;
|
| + var onEntryResolved = function(entry) {
|
| + // Changes directory to the model item's root directory if needed.
|
| + if (!util.isSameEntry(directoryModel.getCurrentDirEntry(), entry)) {
|
| + metrics.recordUserAction('FolderShortcut.Navigate');
|
| + directoryModel.changeDirectoryEntry(entry);
|
| + }
|
| + }.bind(this);
|
| +
|
| + // For shortcuts we already have an Entry, but it has to be resolved again
|
| + // in case, it points to a non-existing directory.
|
| + webkitResolveLocalFileSystemURL(
|
| + this.entry.toURL(),
|
| + onEntryResolved,
|
| + function() {
|
| + // Error, the entry can't be re-resolved. It may happen for shortcuts
|
| + // which targets got removed after resolving the Entry during
|
| + // initialization.
|
| + this.parentTree_.dataModel.onItemNotFoundError(this.modelItem);
|
| + }.bind(this));
|
| +};
|
| +
|
| ////////////////////////////////////////////////////////////////////////////////
|
| // DirectoryTree
|
|
|
| @@ -464,9 +837,29 @@ DirectoryTree.prototype = {
|
| get metadataCache() {
|
| return this.metadataCache_;
|
| },
|
| +
|
| + set dataModel(dataModel) {
|
| + if (!this.onListContentChangedBound_)
|
| + this.onListContentChangedBound_ = this.onListContentChanged_.bind(this);
|
| +
|
| + if (this.dataModel_) {
|
| + this.dataModel_.removeEventListener(
|
| + 'change', this.onListContentChangedBound_);
|
| + this.dataModel_.removeEventListener(
|
| + 'permuted', this.onListContentChangedBound_);
|
| + }
|
| + this.dataModel_ = dataModel;
|
| + dataModel.addEventListener('change', this.onListContentChangedBound_);
|
| + dataModel.addEventListener('permuted', this.onListContentChangedBound_);
|
| + },
|
| +
|
| + get dataModel() {
|
| + return this.dataModel_;
|
| + }
|
| };
|
|
|
| cr.defineProperty(DirectoryTree, 'contextMenuForSubitems', cr.PropertyKind.JS);
|
| +cr.defineProperty(DirectoryTree, 'contextMenuForRootItems', cr.PropertyKind.JS);
|
|
|
| /**
|
| * Calls DirectoryItemTreeBaseMethods.updateSubElementsFromList().
|
| @@ -476,17 +869,75 @@ cr.defineProperty(DirectoryTree, 'contextMenuForSubitems', cr.PropertyKind.JS);
|
| * only immediate child directories without arrows.
|
| */
|
| DirectoryTree.prototype.updateSubElementsFromList = function(recursive) {
|
| - DirectoryItemTreeBaseMethods.updateSubElementsFromList.call(this, recursive);
|
| + // First, current items which is not included in the models_[] should be
|
| + // removed.
|
| + for (var i = 0; i < this.items.length;) {
|
| + var found = false;
|
| + for (var j = 0; j < this.models_.length; j++) {
|
| + if (util.isSameEntry(this.items[i].entry, this.models_[j].entry)) {
|
| + found = true;
|
| + break;
|
| + }
|
| + }
|
| + if (!found) {
|
| + if (this.items[i].selected)
|
| + this.items[i].selected = false;
|
| + this.remove(this.items[i]);
|
| + } else {
|
| + i++;
|
| + }
|
| + }
|
| +
|
| + // Next, insert items which is in models_[] but not in current items.
|
| + var modelIndex = 0;
|
| + var itemIndex = 0;
|
| + while (modelIndex < this.models_.length) {
|
| + if (itemIndex < this.items.length &&
|
| + util.isSameEntry(this.items[itemIndex].entry,
|
| + this.models_[modelIndex].entry)) {
|
| + if (recursive && this.items[itemIndex] instanceof VolumeItem)
|
| + this.items[itemIndex].updateSubDirectories(true);
|
| + } else {
|
| + var model = this.models_[modelIndex];
|
| + if (model.modelItem.isVolume) {
|
| + this.addAt(new VolumeItem(model.entry, model.modelItem, this),
|
| + itemIndex);
|
| + } else {
|
| + this.addAt(new ShortcutItem(model.entry, model.modelItem, this),
|
| + itemIndex);
|
| + }
|
| + }
|
| + itemIndex++;
|
| + modelIndex++;
|
| + }
|
| +
|
| + if (itemIndex !== 0)
|
| + this.hasChildren = true;
|
| };
|
|
|
| /**
|
| - * Calls DirectoryItemTreeBaseMethods.updateSubElementsFromList().
|
| + * Finds a parent directory of the {@code entry} in {@code this}, and
|
| + * invokes the DirectoryItem.selectByEntry() of the found directory.
|
| *
|
| * @param {DirectoryEntry|Object} entry The entry to be searched for. Can be
|
| * a fake.
|
| * @return {boolean} True if the parent item is found.
|
| */
|
| DirectoryTree.prototype.searchAndSelectByEntry = function(entry) {
|
| + // If the |entry| is same as one of volumes or shortcuts, select it.
|
| + for (var i = 0; i < this.items.length; i++) {
|
| + // Skips the Drive root volume. For Drive entries, one of children of Drive
|
| + // root or shortcuts should be selected.
|
| + var item = this.items[i];
|
| + if (item instanceof VolumeItem && item.isDrive())
|
| + continue;
|
| +
|
| + if (util.isSameEntry(item.entry, entry)) {
|
| + item.selectByEntry(entry);
|
| + return true;
|
| + }
|
| + }
|
| + // Otherwise, search whole tree.
|
| return DirectoryItemTreeBaseMethods.searchAndSelectByEntry.call(this, entry);
|
| };
|
|
|
| @@ -505,8 +956,7 @@ DirectoryTree.prototype.decorate = function(
|
| this.directoryModel_ = directoryModel;
|
| this.volumeManager_ = volumeManager;
|
| this.metadataCache_ = metadataCache;
|
| - this.entries_ = [];
|
| - this.currentVolumeInfo_ = null;
|
| + this.models_ = [];
|
|
|
| this.fileFilter_ = this.directoryModel_.getFileFilter();
|
| this.fileFilter_.addEventListener('changed',
|
| @@ -518,7 +968,7 @@ DirectoryTree.prototype.decorate = function(
|
| // Add a handler for directory change.
|
| this.addEventListener('change', function() {
|
| if (this.selectedItem)
|
| - this.directoryModel_.activateDirectoryEntry(this.selectedItem.entry);
|
| + this.selectedItem.activate();
|
| }.bind(this));
|
|
|
| this.privateOnDirectoryChangedBound_ =
|
| @@ -543,12 +993,6 @@ DirectoryTree.prototype.decorate = function(
|
| * be a fake.
|
| */
|
| DirectoryTree.prototype.selectByEntry = function(entry) {
|
| - // If the target directory is not in the tree, do nothing.
|
| - var locationInfo = this.volumeManager_.getLocationInfo(entry);
|
| - if (!locationInfo || !locationInfo.isDriveBased)
|
| - return;
|
| -
|
| - var volumeInfo = this.volumeManager_.getVolumeInfo(entry);
|
| if (this.selectedItem && util.isSameEntry(entry, this.selectedItem.entry))
|
| return;
|
|
|
| @@ -557,6 +1001,7 @@ DirectoryTree.prototype.selectByEntry = function(entry) {
|
|
|
| this.updateSubDirectories(false /* recursive */);
|
| var currentSequence = ++this.sequence_;
|
| + var volumeInfo = this.volumeManager_.getVolumeInfo(entry);
|
| volumeInfo.resolveDisplayRoot(function() {
|
| if (this.sequence_ !== currentSequence)
|
| return;
|
| @@ -566,6 +1011,18 @@ DirectoryTree.prototype.selectByEntry = function(entry) {
|
| };
|
|
|
| /**
|
| + * Select the volume or the shortcut corresponding to the given index.
|
| + * @param {number} index 0-based index of the target top-level item.
|
| + */
|
| +DirectoryTree.prototype.selectByIndex = function(index) {
|
| + if (index < 0 || index >= this.items.length)
|
| + return false;
|
| +
|
| + this.items[index].selected = true;
|
| + return true;
|
| +};
|
| +
|
| +/**
|
| * Retrieves the latest subdirectories and update them on the tree.
|
| *
|
| * @param {boolean} recursive True if the update is recursively.
|
| @@ -575,33 +1032,32 @@ DirectoryTree.prototype.selectByEntry = function(entry) {
|
| DirectoryTree.prototype.updateSubDirectories = function(
|
| recursive, opt_callback) {
|
| var callback = opt_callback || function() {};
|
| - this.entries_ = [];
|
| -
|
| - var compareEntries = function(a, b) {
|
| - return a.toURL() < b.toURL();
|
| - };
|
|
|
| - // Add fakes (if any).
|
| - if (this.fakeEntriesVisible_) {
|
| - for (var key in this.currentVolumeInfo_.fakeEntries) {
|
| - this.entries_.push(this.currentVolumeInfo_.fakeEntries[key]);
|
| + // Resolves all root entries for model items.
|
| + var itemPromises = [];
|
| + for (var i = 0; i < this.dataModel.length; i++) {
|
| + if (this.dataModel.item(i).isVolume) {
|
| + // Volume's root entries need to be resolved.
|
| + itemPromises.push(new Promise(function(resolve, reject) {
|
| + var modelItem = this.dataModel.item(i);
|
| + modelItem.volumeInfo.resolveDisplayRoot(function(entry) {
|
| + resolve({entry: entry, modelItem: modelItem});
|
| + });
|
| + }.bind(this)));
|
| + } else {
|
| + // Shortcuts' root entries can be obtained immediately.
|
| + itemPromises.push(
|
| + Promise.resolve({entry: this.dataModel.item(i).entry,
|
| + modelItem: this.dataModel.item(i)}));
|
| }
|
| }
|
|
|
| - // If the display root is not available yet, then redraw anyway with what
|
| - // we have. However, concurrently try to resolve the display root and then
|
| - // redraw.
|
| - if (!this.currentVolumeInfo_.displayRoot) {
|
| - this.entries_.sort(compareEntries);
|
| + // Redraws this tree using resolved root entries and volume info.
|
| + Promise.all(itemPromises).then(function(results) {
|
| + this.models_ = results;
|
| this.redraw(recursive);
|
| - }
|
| -
|
| - this.currentVolumeInfo_.resolveDisplayRoot(function(displayRoot) {
|
| - this.entries_.push(this.currentVolumeInfo_.displayRoot);
|
| - this.entries_.sort(compareEntries);
|
| - this.redraw(recursive); // Redraw.
|
| callback();
|
| - }.bind(this), callback /* Ignore errors. */);
|
| + }.bind(this));
|
| };
|
|
|
| /**
|
| @@ -634,13 +1090,10 @@ DirectoryTree.prototype.onDirectoryContentChanged_ = function(event) {
|
| if (event.eventType !== 'changed')
|
| return;
|
|
|
| - var locationInfo = this.volumeManager_.getLocationInfo(event.entry);
|
| - if (!locationInfo || !locationInfo.isDriveBased)
|
| - return;
|
| -
|
| - var myDriveItem = this.items[0];
|
| - if (myDriveItem)
|
| - myDriveItem.updateItemByEntry(event.entry);
|
| + for (var i = 0; i < this.items.length; i++) {
|
| + if (this.items[i] instanceof VolumeItem)
|
| + this.items[i].updateItemByEntry(event.entry);
|
| + }
|
| };
|
|
|
| /**
|
| @@ -649,18 +1102,24 @@ DirectoryTree.prototype.onDirectoryContentChanged_ = function(event) {
|
| * @private
|
| */
|
| DirectoryTree.prototype.onCurrentDirectoryChanged_ = function(event) {
|
| - this.currentVolumeInfo_ =
|
| - this.volumeManager_.getVolumeInfo(event.newDirEntry);
|
| this.selectByEntry(event.newDirEntry);
|
| };
|
|
|
| /**
|
| - * Sets the margin height for the transparent preview panel at the bottom.
|
| - * @param {number} margin Margin to be set in px.
|
| + * Invoked when the volume list or shortcut list is changed.
|
| + * @private
|
| */
|
| -DirectoryTree.prototype.setBottomMarginForPanel = function(margin) {
|
| - this.style.paddingBottom = margin + 'px';
|
| - this.scrollBar_.setBottomMarginForPanel(margin);
|
| +DirectoryTree.prototype.onListContentChanged_ = function() {
|
| + this.updateSubDirectories(false, function() {
|
| + // If no item is selected now, try to select the item corresponding to
|
| + // current directory because the current directory might have been populated
|
| + // in this tree in previous updateSubDirectories().
|
| + if (!this.selectedItem) {
|
| + var currentDir = this.directoryModel_.getCurrentDirEntry();
|
| + if (currentDir)
|
| + this.selectByEntry(currentDir);
|
| + }
|
| + }.bind(this));
|
| };
|
|
|
| /**
|
|
|