Index: ui/file_manager/file_manager/foreground/js/ui/directory_tree.js |
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js |
deleted file mode 100644 |
index 49b103525fe71ba3a821f930ec3c426eb0ed847a..0000000000000000000000000000000000000000 |
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js |
+++ /dev/null |
@@ -1,1134 +0,0 @@ |
-// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-'use strict'; |
- |
-//////////////////////////////////////////////////////////////////////////////// |
-// DirectoryTreeBase |
- |
-/** |
- * Implementation of methods for DirectoryTree and DirectoryItem. These classes |
- * inherits cr.ui.Tree/TreeItem so we can't make them inherit this class. |
- * Instead, we separate their implementations to this separate object and call |
- * it with setting 'this' from DirectoryTree/Item. |
- */ |
-var DirectoryItemTreeBaseMethods = {}; |
- |
-/** |
- * Updates sub-elements of {@code this} reading {@code DirectoryEntry}. |
- * The list of {@code DirectoryEntry} are not updated by this method. |
- * |
- * @param {boolean} recursive True if the all visible sub-directories are |
- * updated recursively including left arrows. If false, the update walks |
- * only immediate child directories without arrows. |
- * @this {DirectoryItem} |
- */ |
-DirectoryItemTreeBaseMethods.updateSubElementsFromList = function(recursive) { |
- var index = 0; |
- var tree = this.parentTree_ || this; // If no parent, 'this' itself is tree. |
- while (this.entries_[index]) { |
- var currentEntry = this.entries_[index]; |
- var currentElement = this.items[index]; |
- var label = util.getEntryLabel(tree.volumeManager_, currentEntry); |
- |
- if (index >= this.items.length) { |
- var item = new DirectoryItem(label, currentEntry, this, tree); |
- this.add(item); |
- index++; |
- } else if (util.isSameEntry(currentEntry, currentElement.entry)) { |
- currentElement.updateSharedStatusIcon(); |
- if (recursive && this.expanded) |
- currentElement.updateSubDirectories(true /* recursive */); |
- |
- index++; |
- } else if (currentEntry.toURL() < currentElement.entry.toURL()) { |
- var item = new DirectoryItem(label, currentEntry, this, tree); |
- this.addAt(item, index); |
- index++; |
- } else if (currentEntry.toURL() > currentElement.entry.toURL()) { |
- this.remove(currentElement); |
- } |
- } |
- |
- var removedChild; |
- while (removedChild = this.items[index]) { |
- this.remove(removedChild); |
- } |
- |
- if (index === 0) { |
- this.hasChildren = false; |
- this.expanded = false; |
- } else { |
- this.hasChildren = true; |
- } |
-}; |
- |
-/** |
- * 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. |
- * @this {(DirectoryItem|VolumeItem|DirectoryTree)} |
- */ |
-DirectoryItemTreeBaseMethods.searchAndSelectByEntry = function(entry) { |
- for (var i = 0; i < this.items.length; i++) { |
- var item = this.items[i]; |
- if (util.isDescendantEntry(item.entry, entry) || |
- util.isSameEntry(item.entry, entry)) { |
- item.selectByEntry(entry); |
- return true; |
- } |
- } |
- return false; |
-}; |
- |
-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 |
- |
-/** |
- * A directory in the tree. Each element represents one directory. |
- * |
- * @param {string} label Label for this item. |
- * @param {DirectoryEntry} dirEntry DirectoryEntry of this item. |
- * @param {DirectoryItem|VolumeItem|ShortcutItem|DirectoryTree} parentDirItem |
- * Parent of this item. |
- * @param {DirectoryTree} tree Current tree, which contains this item. |
- * @extends {cr.ui.TreeItem} |
- * @constructor |
- */ |
-function DirectoryItem(label, dirEntry, parentDirItem, tree) { |
- var item = new cr.ui.TreeItem(); |
- DirectoryItem.decorate(item, label, dirEntry, parentDirItem, tree); |
- return item; |
-} |
- |
-/** |
- * @param {HTMLElement} el Element to be DirectoryItem. |
- * @param {string} label Label for this item. |
- * @param {DirectoryEntry} dirEntry DirectoryEntry of this item. |
- * @param {DirectoryItem|VolumeItem|ShortcutItem|DirectoryTree} parentDirItem |
- * Parent of this item. |
- * @param {DirectoryTree} tree Current tree, which contains this item. |
- */ |
-DirectoryItem.decorate = |
- function(el, label, dirEntry, parentDirItem, tree) { |
- el.__proto__ = DirectoryItem.prototype; |
- /** @type {DirectoryItem} */ (el).decorate( |
- label, dirEntry, parentDirItem, tree); |
-}; |
- |
-DirectoryItem.prototype = { |
- __proto__: cr.ui.TreeItem.prototype, |
- |
- /** |
- * The DirectoryEntry corresponding to this DirectoryItem. This may be |
- * a dummy DirectoryEntry. |
- * @type {DirectoryEntry|Object} |
- */ |
- get entry() { |
- return this.dirEntry_; |
- }, |
- |
- /** |
- * The element containing the label text and the icon. |
- * @type {!HTMLElement} |
- * @override |
- */ |
- get labelElement() { |
- return this.firstElementChild.querySelector('.label'); |
- } |
-}; |
- |
-/** |
- * Calls DirectoryItemTreeBaseMethods.updateSubElementsFromList(). |
- * |
- * @param {boolean} recursive True if the all visible sub-directories are |
- * updated recursively including left arrows. If false, the update walks |
- * only immediate child directories without arrows. |
- */ |
-DirectoryItem.prototype.updateSubElementsFromList = function(recursive) { |
- DirectoryItemTreeBaseMethods.updateSubElementsFromList.call(this, recursive); |
-}; |
- |
-/** |
- * 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. |
- */ |
-DirectoryItem.prototype.searchAndSelectByEntry = function(entry) { |
- return DirectoryItemTreeBaseMethods.searchAndSelectByEntry.call(this, entry); |
-}; |
- |
-/** |
- * @param {string} label Localized label for this item. |
- * @param {DirectoryEntry} dirEntry DirectoryEntry of this item. |
- * @param {DirectoryItem|VolumeItem|ShortcutItem|DirectoryTree} parentDirItem |
- * Parent of this item. |
- * @param {DirectoryTree} tree Current tree, which contains this item. |
- */ |
-DirectoryItem.prototype.decorate = function( |
- label, dirEntry, parentDirItem, tree) { |
- this.innerHTML = TREE_ITEM_INNTER_HTML; |
- this.parentTree_ = tree; |
- this.directoryModel_ = tree.directoryModel; |
- this.parent_ = parentDirItem; |
- this.label = label; |
- this.dirEntry_ = dirEntry; |
- this.fileFilter_ = this.directoryModel_.getFileFilter(); |
- |
- // Sets hasChildren=false tentatively. This will be overridden after |
- // scanning sub-directories in updateSubElementsFromList(). |
- this.hasChildren = false; |
- |
- this.addEventListener('expand', this.onExpand_.bind(this), false); |
- var icon = this.querySelector('.icon'); |
- icon.classList.add('volume-icon'); |
- var location = tree.volumeManager.getLocationInfo(dirEntry); |
- if (location && location.rootType && location.isRootEntry) { |
- icon.setAttribute('volume-type-icon', location.rootType); |
- } else { |
- icon.setAttribute('file-type-icon', 'folder'); |
- this.updateSharedStatusIcon(); |
- } |
- |
- if (this.parentTree_.contextMenuForSubitems) |
- this.setContextMenu(this.parentTree_.contextMenuForSubitems); |
- // Adds handler for future change. |
- this.parentTree_.addEventListener( |
- 'contextMenuForSubitemsChange', |
- function(e) { this.setContextMenu(e.newValue); }.bind(this)); |
- |
- if (parentDirItem.expanded) |
- this.updateSubDirectories(false /* recursive */); |
-}; |
- |
-/** |
- * Overrides WebKit's scrollIntoViewIfNeeded, which doesn't work well with |
- * a complex layout. This call is not necessary, so we are ignoring it. |
- * |
- * @param {boolean} unused Unused. |
- * @override |
- */ |
-DirectoryItem.prototype.scrollIntoViewIfNeeded = function(unused) { |
-}; |
- |
-/** |
- * Removes the child node, but without selecting the parent item, to avoid |
- * unintended changing of directories. Removing is done externally, and other |
- * code will navigate to another directory. |
- * |
- * @param {!cr.ui.TreeItem} child The tree item child to remove. |
- * @override |
- */ |
-DirectoryItem.prototype.remove = function(child) { |
- this.lastElementChild.removeChild(child); |
- if (this.items.length == 0) |
- this.hasChildren = false; |
-}; |
- |
-/** |
- * Invoked when the item is being expanded. |
- * @param {!Event} e Event. |
- * @private |
- **/ |
-DirectoryItem.prototype.onExpand_ = function(e) { |
- this.updateSubDirectories( |
- true /* recursive */, |
- function() {}, |
- function() { |
- this.expanded = false; |
- }.bind(this)); |
- |
- e.stopPropagation(); |
-}; |
- |
-/** |
- * Invoked when the tree item is clicked. |
- * |
- * @param {Event} e Click event. |
- * @override |
- */ |
-DirectoryItem.prototype.handleClick = function(e) { |
- cr.ui.TreeItem.prototype.handleClick.call(this, e); |
- if (!e.target.classList.contains('expand-icon')) |
- this.directoryModel_.activateDirectoryEntry(this.entry); |
-}; |
- |
-/** |
- * Retrieves the latest subdirectories and update them on the tree. |
- * @param {boolean} recursive True if the update is recursively. |
- * @param {function()=} opt_successCallback Callback called on success. |
- * @param {function()=} opt_errorCallback Callback called on error. |
- */ |
-DirectoryItem.prototype.updateSubDirectories = function( |
- recursive, opt_successCallback, opt_errorCallback) { |
- if (util.isFakeEntry(this.entry)) { |
- if (opt_errorCallback) |
- opt_errorCallback(); |
- return; |
- } |
- |
- var sortEntries = function(fileFilter, entries) { |
- entries.sort(util.compareName); |
- return entries.filter(fileFilter.filter.bind(fileFilter)); |
- }; |
- |
- var onSuccess = function(entries) { |
- this.entries_ = entries; |
- this.redrawSubDirectoryList_(recursive); |
- opt_successCallback && opt_successCallback(); |
- }.bind(this); |
- |
- var reader = this.entry.createReader(); |
- var entries = []; |
- var readEntry = function() { |
- reader.readEntries(function(results) { |
- if (!results.length) { |
- onSuccess(sortEntries(this.fileFilter_, entries)); |
- return; |
- } |
- |
- for (var i = 0; i < results.length; i++) { |
- var entry = results[i]; |
- if (entry.isDirectory) |
- entries.push(entry); |
- } |
- readEntry(); |
- }.bind(this)); |
- }.bind(this); |
- readEntry(); |
-}; |
- |
-/** |
- * 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. |
- */ |
-DirectoryItem.prototype.updateItemByEntry = function(changedDirectoryEntry) { |
- if (util.isSameEntry(changedDirectoryEntry, this.entry)) { |
- this.updateSubDirectories(false /* recursive */); |
- return; |
- } |
- |
- // Traverse the entire subtree to find the changed element. |
- for (var i = 0; i < this.items.length; i++) { |
- var item = this.items[i]; |
- if (util.isDescendantEntry(item.entry, changedDirectoryEntry) || |
- util.isSameEntry(item.entry, changedDirectoryEntry)) { |
- item.updateItemByEntry(changedDirectoryEntry); |
- break; |
- } |
- } |
-}; |
- |
-/** |
- * Update the icon based on whether the folder is shared on Drive. |
- */ |
-DirectoryItem.prototype.updateSharedStatusIcon = function() { |
- var icon = this.querySelector('.icon'); |
- this.parentTree_.metadataCache.getLatest( |
- [this.dirEntry_], |
- 'external', |
- function(metadata) { |
- icon.classList.toggle('shared', metadata[0] && metadata[0].shared); |
- }); |
-}; |
- |
-/** |
- * Redraw subitems with the latest information. The items are sorted in |
- * alphabetical order, case insensitive. |
- * @param {boolean} recursive True if the update is recursively. |
- * @private |
- */ |
-DirectoryItem.prototype.redrawSubDirectoryList_ = function(recursive) { |
- this.updateSubElementsFromList(recursive); |
-}; |
- |
-/** |
- * Select the item corresponding to the given {@code entry}. |
- * @param {DirectoryEntry|Object} entry The entry to be selected. Can be a fake. |
- */ |
-DirectoryItem.prototype.selectByEntry = function(entry) { |
- if (util.isSameEntry(entry, this.entry)) { |
- this.selected = true; |
- return; |
- } |
- |
- if (this.searchAndSelectByEntry(entry)) |
- return; |
- |
- // If the entry doesn't exist, updates sub directories and tries again. |
- this.updateSubDirectories( |
- false /* recursive */, |
- this.searchAndSelectByEntry.bind(this, entry)); |
-}; |
- |
-/** |
- * Executes the assigned action as a drop target. |
- */ |
-DirectoryItem.prototype.doDropTargetAction = function() { |
- this.expanded = true; |
-}; |
- |
-/** |
- * Sets the context menu for directory tree. |
- * @param {!cr.ui.Menu} menu Menu to be set. |
- */ |
-DirectoryItem.prototype.setContextMenu = function(menu) { |
- var tree = this.parentTree_ || this; // If no parent, 'this' itself is tree. |
- var locationInfo = tree.volumeManager_.getLocationInfo(this.entry); |
- if (locationInfo && locationInfo.isEligibleForFolderShortcut) |
- cr.ui.contextMenuHandler.setContextMenu(this, menu); |
-}; |
- |
-/** |
- * Change current directory to the entry of this item. |
- */ |
-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 {NavigationModelItem} modelItem NavigationModelItem of this volume. |
- * @param {DirectoryTree} tree Current tree, which contains this item. |
- * @extends {cr.ui.TreeItem} |
- * @constructor |
- */ |
-function VolumeItem(modelItem, tree) { |
- var item = new cr.ui.TreeItem(); |
- item.__proto__ = VolumeItem.prototype; |
- item.decorate(modelItem, tree); |
- return item; |
-} |
- |
-VolumeItem.prototype = { |
- __proto__: cr.ui.TreeItem.prototype, |
- get entry() { |
- return this.volumeInfo_.displayRoot; |
- }, |
- 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 {NavigationModelItem} modelItem NavigationModelItem of this volume. |
- * @param {DirectoryTree} tree Current tree, which contains this item. |
- */ |
-VolumeItem.prototype.decorate = function(modelItem, tree) { |
- this.innerHTML = TREE_ITEM_INNTER_HTML; |
- this.parentTree_ = tree; |
- 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); |
- |
- // Populate children of this volume using resolved display root. |
- this.volumeInfo_.resolveDisplayRoot(function(displayRoot) { |
- this.updateSubDirectories(false /* recursive */); |
- }.bind(this)); |
-}; |
- |
-/** |
- * Invoked when the tree item is clicked. |
- * |
- * @param {Event} e Click event. |
- * @override |
- */ |
-VolumeItem.prototype.handleClick = function(e) { |
- // If the currently selected volume is clicked, change current directory to |
- // the volume's root. |
- if (this.selected) |
- this.activate(); |
- |
- 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.volumeInfo_.resolveDisplayRoot(function(displayRoot) { |
- this.searchAndSelectByEntry(displayRoot); |
- }.bind(this)); |
- } |
-}; |
- |
-/** |
- * 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() && this.entry && !this.hasChildren) { |
- var entries = [this.entry]; |
- if (this.parentTree_.fakeEntriesVisible_) { |
- for (var key in this.volumeInfo.fakeEntries) |
- entries.push(this.volumeInfo.fakeEntries[key]); |
- } |
- // This list is sorted by URL on purpose. |
- entries.sort(function(a, b) { |
- if (a.toURL() === b.toURL()) |
- return 0; |
- return b.toURL() > a.toURL() ? 1 : -1; |
- }); |
- |
- 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(this.entry, entry) || |
- util.isDescendantEntry(this.entry, 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); |
- } |
- // In case of failure in resolveDisplayRoot() in the volume's decorate(), |
- // update the volume's children here. |
- this.updateSubDirectories(false); |
- }.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 {Element} 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 {NavigationModelItem} modelItem NavigationModelItem of this volume. |
- * @param {DirectoryTree} tree Current tree, which contains this item. |
- * @extends {cr.ui.TreeItem} |
- * @constructor |
- */ |
-function ShortcutItem(modelItem, tree) { |
- var item = new cr.ui.TreeItem(); |
- item.__proto__ = ShortcutItem.prototype; |
- item.decorate(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 {NavigationModelItem} modelItem NavigationModelItem of this volume. |
- * @param {DirectoryTree} tree Current tree, which contains this item. |
- */ |
-ShortcutItem.prototype.decorate = function(modelItem, tree) { |
- this.innerHTML = TREE_ITEM_INNTER_HTML; |
- this.parentTree_ = tree; |
- this.label = modelItem.entry.name; |
- this.dirEntry_ = modelItem.entry; |
- 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 |
- |
-/** |
- * Tree of directories on the middle bar. This element is also the root of |
- * items, in other words, this is the parent of the top-level items. |
- * |
- * @constructor |
- * @extends {cr.ui.Tree} |
- */ |
-function DirectoryTree() {} |
- |
-/** |
- * Decorates an element. |
- * @param {HTMLElement} el Element to be DirectoryTree. |
- * @param {DirectoryModel} directoryModel Current DirectoryModel. |
- * @param {VolumeManagerWrapper} volumeManager VolumeManager of the system. |
- * @param {MetadataCache} metadataCache Shared MetadataCache instance. |
- * @param {boolean} fakeEntriesVisible True if it should show the fakeEntries. |
- */ |
-DirectoryTree.decorate = function( |
- el, directoryModel, volumeManager, metadataCache, fakeEntriesVisible) { |
- el.__proto__ = DirectoryTree.prototype; |
- /** @type {DirectoryTree} */ (el).decorate( |
- directoryModel, volumeManager, metadataCache, fakeEntriesVisible); |
-}; |
- |
-DirectoryTree.prototype = { |
- __proto__: cr.ui.Tree.prototype, |
- |
- // DirectoryTree is always expanded. |
- get expanded() { return true; }, |
- /** |
- * @param {boolean} value Not used. |
- */ |
- set expanded(value) {}, |
- |
- /** |
- * The DirectoryEntry corresponding to this DirectoryItem. This may be |
- * a dummy DirectoryEntry. |
- * @type {DirectoryEntry|Object} |
- * @override |
- **/ |
- get entry() { |
- return this.dirEntry_; |
- }, |
- |
- /** |
- * The DirectoryModel this tree corresponds to. |
- * @type {DirectoryModel} |
- */ |
- get directoryModel() { |
- return this.directoryModel_; |
- }, |
- |
- /** |
- * The VolumeManager instance of the system. |
- * @type {VolumeManager} |
- */ |
- get volumeManager() { |
- return this.volumeManager_; |
- }, |
- |
- /** |
- * The reference to shared MetadataCache instance. |
- * @type {MetadataCache} |
- */ |
- 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(). |
- * |
- * @param {boolean} recursive True if the all visible sub-directories are |
- * updated recursively including left arrows. If false, the update walks |
- * only immediate child directories without arrows. |
- */ |
-DirectoryTree.prototype.updateSubElementsFromList = function(recursive) { |
- // First, current items which is not included in the dataModel should be |
- // removed. |
- for (var i = 0; i < this.items.length;) { |
- var found = false; |
- for (var j = 0; j < this.dataModel.length; j++) { |
- if (NavigationModelItem.isSame(this.items[i].modelItem, |
- this.dataModel.item(j))) { |
- 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 dataModel but not in current items. |
- var modelIndex = 0; |
- var itemIndex = 0; |
- while (modelIndex < this.dataModel.length) { |
- if (itemIndex < this.items.length && |
- NavigationModelItem.isSame(this.items[itemIndex].modelItem, |
- this.dataModel.item(modelIndex))) { |
- if (recursive && this.items[itemIndex] instanceof VolumeItem) |
- this.items[itemIndex].updateSubDirectories(true); |
- } else { |
- var modelItem = this.dataModel.item(modelIndex); |
- if (modelItem.isVolume) |
- this.addAt(new VolumeItem(modelItem, this), itemIndex); |
- else |
- this.addAt(new ShortcutItem(modelItem, this), itemIndex); |
- } |
- itemIndex++; |
- modelIndex++; |
- } |
- |
- if (itemIndex !== 0) |
- this.hasChildren = true; |
-}; |
- |
-/** |
- * 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)) { |
- this.dontHandleChangeEvent_ = true; |
- item.selectByEntry(entry); |
- this.dontHandleChangeEvent_ = false; |
- return true; |
- } |
- } |
- // Otherwise, search whole tree. |
- this.dontHandleChangeEvent_ = true; |
- var found = DirectoryItemTreeBaseMethods.searchAndSelectByEntry.call( |
- this, entry); |
- this.dontHandleChangeEvent_ = false; |
- return found; |
-}; |
- |
-/** |
- * Decorates an element. |
- * @param {DirectoryModel} directoryModel Current DirectoryModel. |
- * @param {VolumeManagerWrapper} volumeManager VolumeManager of the system. |
- * @param {MetadataCache} metadataCache Shared MetadataCache instance. |
- * @param {boolean} fakeEntriesVisible True if it should show the fakeEntries. |
- */ |
-DirectoryTree.prototype.decorate = function( |
- directoryModel, volumeManager, metadataCache, fakeEntriesVisible) { |
- cr.ui.Tree.prototype.decorate.call(this); |
- |
- this.sequence_ = 0; |
- this.directoryModel_ = directoryModel; |
- this.volumeManager_ = volumeManager; |
- this.metadataCache_ = metadataCache; |
- this.models_ = []; |
- |
- this.fileFilter_ = this.directoryModel_.getFileFilter(); |
- this.fileFilter_.addEventListener('changed', |
- this.onFilterChanged_.bind(this)); |
- |
- this.directoryModel_.addEventListener('directory-changed', |
- this.onCurrentDirectoryChanged_.bind(this)); |
- |
- // Add a handler for directory change. |
- this.addEventListener('change', function() { |
- if (this.selectedItem && !this.dontHandleChangeEvent_) |
- this.selectedItem.activate(); |
- }.bind(this)); |
- |
- this.privateOnDirectoryChangedBound_ = |
- this.onDirectoryContentChanged_.bind(this); |
- chrome.fileManagerPrivate.onDirectoryChanged.addListener( |
- this.privateOnDirectoryChangedBound_); |
- |
- this.scrollBar_ = new MainPanelScrollBar(); |
- this.scrollBar_.initialize(this.parentElement, this); |
- |
- /** |
- * Flag to show fake entries in the tree. |
- * @type {boolean} |
- * @private |
- */ |
- this.fakeEntriesVisible_ = fakeEntriesVisible; |
-}; |
- |
-/** |
- * Select the item corresponding to the given entry. |
- * @param {DirectoryEntry|Object} entry The directory entry to be selected. Can |
- * be a fake. |
- */ |
-DirectoryTree.prototype.selectByEntry = function(entry) { |
- if (this.selectedItem && util.isSameEntry(entry, this.selectedItem.entry)) |
- return; |
- |
- if (this.searchAndSelectByEntry(entry)) |
- return; |
- |
- this.updateSubDirectories(false /* recursive */); |
- var currentSequence = ++this.sequence_; |
- var volumeInfo = this.volumeManager_.getVolumeInfo(entry); |
- if (!volumeInfo) |
- return; |
- volumeInfo.resolveDisplayRoot(function() { |
- if (this.sequence_ !== currentSequence) |
- return; |
- if (!this.searchAndSelectByEntry(entry)) |
- this.selectedItem = null; |
- }.bind(this)); |
-}; |
- |
-/** |
- * Select the volume or the shortcut corresponding to the given index. |
- * @param {number} index 0-based index of the target top-level item. |
- * @return {boolean} True if one of the volume items is selected. |
- */ |
-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. |
- * @param {function()=} opt_callback Called when subdirectories are fully |
- * updated. |
- */ |
-DirectoryTree.prototype.updateSubDirectories = function( |
- recursive, opt_callback) { |
- this.redraw(recursive); |
- if (opt_callback) |
- opt_callback(); |
-}; |
- |
-/** |
- * Redraw the list. |
- * @param {boolean} recursive True if the update is recursively. False if the |
- * only root items are updated. |
- */ |
-DirectoryTree.prototype.redraw = function(recursive) { |
- this.updateSubElementsFromList(recursive); |
-}; |
- |
-/** |
- * Invoked when the filter is changed. |
- * @private |
- */ |
-DirectoryTree.prototype.onFilterChanged_ = function() { |
- // Returns immediately, if the tree is hidden. |
- if (this.hidden) |
- return; |
- |
- this.redraw(true /* recursive */); |
-}; |
- |
-/** |
- * Invoked when a directory is changed. |
- * @param {!Event} event Event. |
- * @private |
- */ |
-DirectoryTree.prototype.onDirectoryContentChanged_ = function(event) { |
- if (event.eventType !== 'changed') |
- return; |
- |
- for (var i = 0; i < this.items.length; i++) { |
- if (this.items[i] instanceof VolumeItem) |
- this.items[i].updateItemByEntry(event.entry); |
- } |
-}; |
- |
-/** |
- * Invoked when the current directory is changed. |
- * @param {!Event} event Event. |
- * @private |
- */ |
-DirectoryTree.prototype.onCurrentDirectoryChanged_ = function(event) { |
- this.selectByEntry(event.newDirEntry); |
-}; |
- |
-/** |
- * Invoked when the volume list or shortcut list is changed. |
- * @private |
- */ |
-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)); |
-}; |
- |
-/** |
- * Updates the UI after the layout has changed. |
- */ |
-DirectoryTree.prototype.relayout = function() { |
- cr.dispatchSimpleEvent(this, 'relayout'); |
-}; |