Commit 86cc6307 authored by oka's avatar oka Committed by Commit bot

Remove details panel.

Details panel was being developed under a flag, but has been replaced
with Quick View and is no longer needed.

BUG=672767
TEST=Manually tested that FileManager successfully launches using
Linux. git cl try.
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:closure_compilation

Review-Url: https://codereview.chromium.org/2565333002
Cr-Commit-Position: refs/heads/master@{#437836}
parent 44e98909
......@@ -1157,9 +1157,6 @@ Press any key to continue exploring.
<message name="IDS_FILE_BROWSER_SORT_BUTTON_TOOLTIP" desc="Tooltip for the button which provides sort options in Files.app.">
Sort options
</message>
<message name="IDS_FILE_BROWSER_DETAIL_BUTTON_TOOLTIP" desc="Tooltip for the detals button to show the details panel which provides information of selected files in Files.app.">
View details
</message>
<message name="IDS_FILE_BROWSER_GEAR_BUTTON_TOOLTIP" desc="Tooltip for the gear button in Files.app.">
Settings
</message>
......
......@@ -1932,9 +1932,6 @@ const FeatureEntry kFeatureEntries[] = {
{"enable-native-cups", IDS_FLAGS_ENABLE_NATIVE_CUPS_NAME,
IDS_FLAGS_ENABLE_NATIVE_CUPS_DESCRIPTION, kOsCrOS,
SINGLE_VALUE_TYPE(switches::kEnableNativeCups)},
{"enable-files-details-panel", IDS_FLAGS_ENABLE_FILES_DETAILS_PANEL_NAME,
IDS_FLAGS_ENABLE_FILES_DETAILS_PANEL_DESCRIPTION, kOsCrOS,
SINGLE_VALUE_TYPE(chromeos::switches::kEnableFilesDetailsPanel)},
#endif // OS_CHROMEOS
#if !defined(OS_ANDROID) && !defined(OS_IOS) && defined(GOOGLE_CHROME_BUILD)
{"enable-google-branded-context-menu",
......
......@@ -475,7 +475,6 @@ ExtensionFunction::ResponseAction FileManagerPrivateGetStringsFunction::Run() {
SET_STRING("FORMAT_DEVICE_BUTTON_LABEL",
IDS_FILE_BROWSER_FORMAT_DEVICE_BUTTON_LABEL);
SET_STRING("SORT_BUTTON_TOOLTIP", IDS_FILE_BROWSER_SORT_BUTTON_TOOLTIP);
SET_STRING("DETAIL_BUTTON_TOOLTIP", IDS_FILE_BROWSER_DETAIL_BUTTON_TOOLTIP);
SET_STRING("GEAR_BUTTON_TOOLTIP", IDS_FILE_BROWSER_GEAR_BUTTON_TOOLTIP);
SET_STRING("GET_INFO_BUTTON_LABEL", IDS_FILE_BROWSER_GET_INFO_BUTTON_LABEL);
SET_STRING("HOSTED_OFFLINE_MESSAGE", IDS_FILE_BROWSER_HOSTED_OFFLINE_MESSAGE);
......
......@@ -54,19 +54,6 @@ IN_PROC_BROWSER_TEST_P(FileManagerBrowserTestWithLegacyEventDispatch, Test) {
StartTest();
}
// Test fixture class for details panel.
// TODO(ryoh): remove after we release details panel feature.
class FileManagerDetailsPanelBrowserTest : public FileManagerBrowserTest {
void SetUpCommandLine(base::CommandLine* command_line) override {
FileManagerBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch("--enable-files-details-panel");
}
};
IN_PROC_BROWSER_TEST_P(FileManagerDetailsPanelBrowserTest, Test) {
StartTest();
}
// Unlike TEST/TEST_F, which are macros that expand to further macros,
// INSTANTIATE_TEST_CASE_P is a macro that expands directly to code that
// stringizes the arguments. As a result, macros passed as parameters (such as
......@@ -203,14 +190,6 @@ WRAPPED_INSTANTIATE_TEST_CASE_P(
FileManagerBrowserTest,
::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "openQuickView")));
WRAPPED_INSTANTIATE_TEST_CASE_P(
DetailsPanel,
FileManagerDetailsPanelBrowserTest,
::testing::Values(
TestParameter(NOT_IN_GUEST_MODE, "openDetailsPanel"),
TestParameter(NOT_IN_GUEST_MODE, "openDetailsPanelForSingleFile"),
TestParameter(NOT_IN_GUEST_MODE, "openSingleFileAndSeeDetailsPanel")));
#if defined(DISABLE_SLOW_FILESAPP_TESTS)
#define MAYBE_DirectoryTreeContextMenu DISABLED_DirectoryTreeContextMenu
#else
......
......@@ -231,9 +231,6 @@ const char kEnableExperimentalAccessibilityFeatures[] =
// Enables sharing assets for installed default apps.
const char kEnableExtensionAssetsSharing[] = "enable-extension-assets-sharing";
// Enables details panel in Files app.
const char kEnableFilesDetailsPanel[] = "enable-files-details-panel";
// Enables quick view in Files app.
const char kEnableFilesQuickView[] = "enable-files-quick-view";
......
......@@ -81,7 +81,6 @@ CHROMEOS_EXPORT extern const char kEnableConsumerKiosk[];
CHROMEOS_EXPORT extern const char kEnableDataSaverPrompt[];
CHROMEOS_EXPORT extern const char kEnableExperimentalAccessibilityFeatures[];
CHROMEOS_EXPORT extern const char kEnableExtensionAssetsSharing[];
CHROMEOS_EXPORT extern const char kEnableFilesDetailsPanel[];
CHROMEOS_EXPORT extern const char kEnableFilesQuickView[];
CHROMEOS_EXPORT extern const char kEnableFirstRunUITransitions[];
CHROMEOS_EXPORT extern const char kEnableKioskMode[];
......
......@@ -80,129 +80,6 @@ a:focus {
flex-direction: column;
}
/* Details pane */
.details-container {
background-color: rgb(250, 250, 250);
display: flex;
flex: none;
max-width: 30%;
min-width: 100px;
overflow: hidden;
position: relative;
width: 240px;
}
#list-details-splitter:not([activated]) {
display: none;
}
.details-container:not([activated]) {
display: none;
}
.details-container > div:not([activated]) {
display: none !important;
}
/* Filetype icons in details panel */
.details-container .thumbnail-container {
box-sizing: border-box;
padding: 6px;
position: relative;
width: 100%;
}
.details-container .thumbnail-container:before {
content: "";
display: block;
padding-top: 100%;
}
.details-container .thumbnail-container > .thumbnail {
background-color: rgb(230, 230, 230);
background-position: center;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
/* File thumbnails/quick previews in details panel */
.details-container .thumbnail > img {
height: 100%;
object-fit: contain;
width: 100%;
}
.details-container .thumbnail > video {
height: 100%;
object-fit: contain;
width: 100%;
}
.details-container .thumbnail > audio {
bottom: 0px;
left: 0px;
height: auto;
position: absolute;
width: 100%;
}
/* Details panel for single file */
.details-container > #single-file-details {
display: flex;
flex: auto;
flex-direction: column;
width: 100%;
}
#single-file-details > .filename-container {
align-items: center;
display: flex;
flex-direction: row;
height: 40px;
padding: 0 6px;
width: 100%;
}
#single-file-details > .filename-container > .filename {
-webkit-padding-end: 6px;
flex: auto;
font-weight: 500;
overflow: hidden;
}
#single-file-details > .thumbnail-container > .thumbnail.loaded {
background-image: none;
}
#single-file-details > .details-list {
flex: auto;
width: 100%;
}
#single-file-details > .details-list > li:not(.available) {
display: none;
}
/* Filetype icon of details panel for multiple files */
.details-container > #multi-file-details {
display: flex;
flex: auto;
flex-direction: column;
width: 100%;
}
#multi-file-details > .details-list {
flex: auto;
width: 100%;
}
/* Directory tree at the left. */
.dialog-navigation-list {
-webkit-border-end: 1px solid rgba(0, 0, 0, 0.15);
......@@ -537,24 +414,6 @@ body.check-select #sort-button {
display: none;
}
#details-button {
/**
* TODO(ryoh): This should be removed after we finally implement details
* panel.
*/
display: none;
}
#details-button > .icon {
background-image: -webkit-image-set(
url(../images/files/ui/info_white.png) 1x,
url(../images/files/ui/2x/info_white.png) 2x);
}
body.check-select #details-button {
display: none;
}
#gear-button > .icon {
background-image: -webkit-image-set(
url(../images/files/ui/menu_white.png) 1x,
......
......@@ -79,7 +79,6 @@ AppStateController.prototype.initialize = function(ui, directoryModel) {
// Restore preferences.
this.ui_.setCurrentListType(
this.viewOptions_.listType || ListContainer.ListType.DETAIL);
this.ui_.setDetailsVisibility(!!this.viewOptions_.detailsVisibility);
this.directoryModel_.getFileList().sort(
this.viewOptions_.sortField || 'modificationTime',
this.viewOptions_.sortDirection || 'desc');
......@@ -99,11 +98,6 @@ AppStateController.prototype.saveViewOptions = function() {
sortDirection: sortStatus.direction,
columnConfig: {},
listType: this.ui_.listContainer.currentListType,
/**
* TODO(ryoh): Simplify this line after we finally implement details panel.
*/
detailsVisibility: this.ui_.detailsContainer &&
this.ui_.detailsContainer.visible || false
};
var cm = this.ui_.listContainer.table.columnModel;
prefs.columnConfig = cm.exportColumnConfig();
......
......@@ -132,7 +132,6 @@
'./ui/actions_submenu.js',
'./ui/banners.js',
'./ui/default_task_dialog.js',
'./ui/details_container.js',
'./ui/dialog_footer.js',
'./ui/directory_tree.js',
'./ui/drag_selector.js',
......@@ -150,13 +149,11 @@
'./ui/gear_menu.js',
'./ui/list_container.js',
'./ui/location_line.js',
'./ui/multi_file_details.js',
'./ui/multi_profile_share_dialog.js',
'./ui/progress_center_panel.js',
'./ui/providers_menu.js',
'./ui/search_box.js',
'./ui/share_dialog.js',
'./ui/single_file_details.js',
'./ui/suggest_apps_dialog.js',
'./main_window_component.js',
'./volume_manager_wrapper.js',
......
......@@ -890,23 +890,11 @@ FileManager.prototype = /** @struct */ {
this.volumeManager_,
this.historyLoader_);
var singlePanel = queryRequiredElement('#single-file-details', dom);
SingleFileDetailsPanel.decorate(
assertInstanceof(singlePanel, HTMLDivElement),
this.metadataModel_);
var multiPanel = queryRequiredElement('#multi-file-details', dom);
MultiFileDetailsPanel.decorate(
assertInstanceof(multiPanel, HTMLDivElement),
this.metadataModel_);
this.addHistoryObserver_();
this.ui_.initAdditionalUI(
assertInstanceof(table, FileTable),
assertInstanceof(grid, FileGrid),
assertInstanceof(singlePanel, SingleFileDetailsPanel),
assertInstanceof(multiPanel, MultiFileDetailsPanel),
new LocationLine(
queryRequiredElement('#location-breadcrumbs', dom),
this.volumeManager_));
......@@ -1043,7 +1031,6 @@ FileManager.prototype = /** @struct */ {
// Create metadata update controller.
this.metadataUpdateController_ = new MetadataUpdateController(
this.ui_.listContainer,
assert(this.ui_.detailsContainer),
this.directoryModel_,
this.metadataModel_,
this.fileMetadataFormatter_);
......
......@@ -146,7 +146,6 @@
//<include src="ui/actions_submenu.js">
//<include src="ui/banners.js" >
//<include src="ui/default_task_dialog.js">
//<include src="ui/details_container.js">
//<include src="ui/dialog_footer.js">
//<include src="ui/directory_tree.js">
//<include src="ui/drag_selector.js">
......@@ -164,13 +163,11 @@
//<include src="ui/gear_menu.js">
//<include src="ui/list_container.js">
//<include src="ui/location_line.js">
//<include src="ui/multi_file_details.js">
//<include src="ui/multi_profile_share_dialog.js">
//<include src="ui/progress_center_panel.js">
//<include src="ui/providers_menu.js">
//<include src="ui/search_box.js">
//<include src="ui/share_dialog.js">
//<include src="ui/single_file_details.js">
//<include src="ui/suggest_apps_dialog.js">
//<include src="main_window_component.js">
//<include src="volume_manager_wrapper.js">
......
......@@ -124,8 +124,6 @@ function MainWindowComponent(
'pathclick', this.onBreadcrumbClick_.bind(this));
ui.toggleViewButton.addEventListener(
'click', this.onToggleViewButtonClick_.bind(this));
ui.detailsButton.addEventListener(
'click', this.onDetailsButtonClick_.bind(this));
directoryModel.addEventListener(
'directory-changed', this.onDirectoryChanged_.bind(this));
volumeManager.addEventListener(
......@@ -134,8 +132,6 @@ function MainWindowComponent(
this.onDriveConnectionChanged_();
document.addEventListener('keydown', this.onKeyDown_.bind(this));
document.addEventListener('keyup', this.onKeyUp_.bind(this));
selectionHandler.addEventListener('change',
this.onFileSelectionChanged_.bind(this));
}
/**
......@@ -162,17 +158,6 @@ MainWindowComponent.prototype.onFileListFocus_ = function() {
}
};
/**
* Handles file selection event.
*
* @private
*/
MainWindowComponent.prototype.onFileSelectionChanged_ = function(event) {
if (this.ui_.detailsContainer) {
this.ui_.detailsContainer.onFileSelectionChanged(event);
}
};
/**
* Handles mouse click or tap.
*
......@@ -250,18 +235,6 @@ MainWindowComponent.prototype.onToggleViewButtonClick_ = function(event) {
this.ui_.listContainer.focus();
};
/**
* Handles click event on the toggle-view button.
* @param {Event} event Click event.
* @private
*/
MainWindowComponent.prototype.onDetailsButtonClick_ = function(event) {
var visible = this.ui_.detailsContainer.visible;
this.ui_.setDetailsVisibility(!visible);
this.appStateController_.saveViewOptions();
this.ui_.listContainer.focus();
};
/**
* KeyDown event handler for the document.
* @param {Event} event Key event.
......
......@@ -5,7 +5,6 @@
/**
* Controller for list contents update.
* @param {!ListContainer} listContainer
* @param {!DetailsContainer} detailsContainer
* @param {!DirectoryModel} directoryModel
* @param {!MetadataModel} metadataModel
* @param {!FileMetadataFormatter} fileMetadataFormatter
......@@ -13,7 +12,6 @@
* @struct
*/
function MetadataUpdateController(listContainer,
detailsContainer,
directoryModel,
metadataModel,
fileMetadataFormatter) {
......@@ -35,12 +33,6 @@ function MetadataUpdateController(listContainer,
*/
this.listContainer_ = listContainer;
/**
* @private {!DetailsContainer}
* @const
*/
this.detailsContainer_ = detailsContainer;
/**
* @private {!FileMetadataFormatter}
* @const
......@@ -129,7 +121,6 @@ MetadataUpdateController.prototype.onPreferencesChanged_ = function() {
// TODO(oka): Remove these two lines, and add fileMetadataFormatter to
// constructor for each field instead.
this.listContainer_.table.setDateTimeFormat(use12hourClock);
this.detailsContainer_.setDateTimeFormat(use12hourClock);
this.refreshCurrentDirectoryMetadata();
}.bind(this));
};
......@@ -28,10 +28,6 @@
# 'includes': ['../../../../compile_js2.gypi'],
# },
# {
# 'target_name': 'details_container',
# 'includes': ['../../../../compile_js2.gypi'],
# },
# {
# 'target_name': 'dialog_footer',
# 'includes': ['../../../../compile_js2.gypi'],
# },
......@@ -112,10 +108,6 @@
# 'includes': ['../../../../compile_js2.gypi'],
# },
# {
# 'target_name': 'multi_file_details',
# 'includes': ['../../../../compile_js2.gypi'],
# },
# {
# 'target_name': 'multi_profile_share_dialog',
# 'includes': ['../../../../compile_js2.gypi'],
# },
......@@ -140,10 +132,6 @@
# 'includes': ['../../../../compile_js2.gypi'],
# },
# {
# 'target_name': 'single_file_details',
# 'includes': ['../../../../compile_js2.gypi'],
# },
# {
# 'target_name': 'suggest_apps_dialog',
# 'includes': ['../../../../compile_js2.gypi'],
# },
......
// Copyright 2016 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.
/**
* @param {!HTMLElement} element
* @param {!SingleFileDetailsPanel} singlePanel
* @param {!MultiFileDetailsPanel} multiPanel
* @param {!Element} splitter
* @param {!Element} button
* @param {!FilesToggleRipple} toggleRipple
* @constructor
* @struct
*/
function DetailsContainer(element, singlePanel, multiPanel, splitter, button,
toggleRipple) {
/**
* Container element.
* @private {!HTMLElement}
* @const
*/
this.element_ = element;
/**
* Splitter element between the file list and the details panel.
* @private {!Element}
* @const
*/
this.splitter_ = splitter;
/**
* "View details" button element.
* @private {!Element}
* @const
*/
this.button_ = button;
/**
* Ripple element of "View details" button.
* @private {!Element}
* @const
*/
this.toggleRipple_ = toggleRipple;
/**
* Details panel for a single file.
* @private {!SingleFileDetailsPanel}
* @const
*/
this.singlePanel_ = singlePanel;
/**
* Details panel for a multiple files.
* @private {!MultiFileDetailsPanel}
* @const
*/
this.multiPanel_ = multiPanel;
/**
* @type {boolean}
*/
this.visible = false;
/**
* @private {Array<!FileEntry>}
*/
this.pendingEntries_ = null;
this.setVisibility(false);
}
DetailsContainer.prototype.onFileSelectionChanged = function(event) {
var entries = event.target.selection.entries;
if (this.visible) {
this.pendingEntries_ = null;
this.display_(entries);
} else {
this.pendingEntries_ = entries;
}
};
/**
* Disply details of entries
* @param {!Array<!FileEntry>} entries
*/
DetailsContainer.prototype.display_ = function(entries) {
if (entries.length === 0) {
this.singlePanel_.removeAttribute('activated');
this.multiPanel_.removeAttribute('activated');
// TODO(ryoh): make a panel for empty selection
} else if (entries.length === 1) {
this.singlePanel_.setAttribute('activated', '');
this.multiPanel_.removeAttribute('activated');
this.singlePanel_.onFileSelectionChanged(entries[0]);
this.multiPanel_.cancelLoading();
} else {
this.singlePanel_.removeAttribute('activated');
this.multiPanel_.setAttribute('activated', '');
this.multiPanel_.onFileSelectionChanged(entries);
this.singlePanel_.cancelLoading();
}
};
/**
* Sets the details panel visibility
* @param {boolean} visibility True if the details panel is visible.
*/
DetailsContainer.prototype.setVisibility = function(visibility) {
this.visible = visibility;
if (visibility) {
this.splitter_.setAttribute('activated', '');
this.element_.setAttribute('activated', '');
if (this.pendingEntries_) {
this.display_(this.pendingEntries_);
}
} else {
this.splitter_.removeAttribute('activated');
this.element_.removeAttribute('activated');
}
this.toggleRipple_.activated = visibility;
this.singlePanel_.onVisibilityChanged(visibility);
};
/**
* Sets date and time format.
* @param {boolean} use12hourClock True if 12 hours clock, False if 24 hours.
*/
DetailsContainer.prototype.setDateTimeFormat = function(use12hourClock) {
this.singlePanel_.setDateTimeFormat(use12hourClock);
};
......@@ -161,23 +161,6 @@ function FileManagerUI(providersModel, element, launchParam) {
this.sortButton = util.queryDecoratedElement(
'#sort-button', cr.ui.MenuButton);
/**
* The button to open the details panel.
* @type {!Element}
* @const
*/
this.detailsButton = queryRequiredElement(
'#details-button', this.element);
/**
* Ripple effect of details button.
* @private {!FilesToggleRipple}
* @const
*/
this.detailsButtonToggleRipple_ =
/** @type {!FilesToggleRipple} */ (queryRequiredElement(
'files-toggle-ripple', this.detailsButton));
/**
* Ripple effect of sort button.
* @private {!FilesToggleRipple}
......@@ -230,12 +213,6 @@ function FileManagerUI(providersModel, element, launchParam) {
*/
this.listContainer = null;
/**
* Details container.
* @type {DetailsContainer}
*/
this.detailsContainer = null;
/**
* @type {!HTMLElement}
*/
......@@ -337,12 +314,9 @@ function FileManagerUI(providersModel, element, launchParam) {
*
* @param {!FileTable} table
* @param {!FileGrid} grid
* @param {!SingleFileDetailsPanel} singlePanel
* @param {!MultiFileDetailsPanel} multiPanel
* @param {!LocationLine} locationLine
*/
FileManagerUI.prototype.initAdditionalUI = function(
table, grid, singlePanel, multiPanel, locationLine) {
FileManagerUI.prototype.initAdditionalUI = function(table, grid, locationLine) {
// List container.
this.listContainer = new ListContainer(
queryRequiredElement('#list-container', this.element), table, grid);
......@@ -351,25 +325,6 @@ FileManagerUI.prototype.initAdditionalUI = function(
this.decorateSplitter_(
queryRequiredElement('#navigation-list-splitter', this.element));
// Details container.
var listDetailsSplitter =
queryRequiredElement('#list-details-splitter', this.element);
this.decorateSplitter_(listDetailsSplitter, true);
this.detailsContainer = new DetailsContainer(
queryRequiredElement('#details-container', this.element),
singlePanel,
multiPanel,
listDetailsSplitter,
this.detailsButton,
this.detailsButtonToggleRipple_);
chrome.commandLinePrivate.hasSwitch('enable-files-details-panel',
function(enabled) {
if (enabled) {
this.detailsButton.style.display = 'block';
}
}.bind(this));
// Location line.
this.locationLine = locationLine;
......@@ -489,17 +444,6 @@ FileManagerUI.prototype.setCurrentListType = function(listType) {
this.relayout();
};
/**
* Sets the details panel visibility
* @param {boolean} visibility True if the details panel is visible.
*/
FileManagerUI.prototype.setDetailsVisibility = function(visibility) {
if (this.detailsContainer) {
this.detailsContainer.setVisibility(visibility);
this.relayout();
}
};
/**
* Overrides default handling for clicks on hyperlinks.
* In a packaged apps links with targer='_blank' open in a new tab by
......
// Copyright 2016 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.
/**
* MultiFileDetailsPanel constructor.
*
* Represents grid for the details panel for a single file in Files app.
* @constructor
* @extends {HTMLDivElement}
*/
function MultiFileDetailsPanel() {
throw new Error('Use MultiFileDetailsPanel.decorate');
}
/**
* Inherits from HTMLDivElement.
*/
MultiFileDetailsPanel.prototype = {
__proto__: HTMLDivElement.prototype,
/**
* @param {!Array<!FileEntry>} entries
*/
onFileSelectionChanged: function(entries) {
this.ticket_++;
this.lastTargetEntries_ = entries;
this.aggregateRateLimitter_.run();
},
startAggregation: function() {
var aggregator = new MultiFileDetailsPanel.Aggregator(this.ticket_,
this.metadataModel_,
this.onAggregated_.bind(this));
aggregator.enqueue(this.lastTargetEntries_);
},
/**
* @param {number} ticket
* @param {number} totalCount Total file count.
* @param {number} totalSize Sum of file size.
* @return {boolean} Whether should we continue the aggregation or not.
*/
onAggregated_: function(ticket, totalCount, totalSize) {
if (ticket !== this.ticket_) {
return false;
}
this.lastTotalSize_ = totalSize;
this.lastTotalCount_ = totalCount;
this.viewUpdateRateLimitter_.run();
return true;
},
/**
* @private
*/
updateView_: function() {
queryRequiredElement('.file-size > .content', this.list_).textContent =
this.formatter_.formatSize(this.lastTotalSize_);
queryRequiredElement('.file-count > .content', this.list_).textContent =
this.lastTotalCount_;
},
/**
* Cancel loading task.
*/
cancelLoading: function() {
this.ticket_++;
}
};
/**
* Aggregator class. That count files and calculate a sum of file size.
* @param {number} ticket
* @param {!MetadataModel} metadataModel
* @param {function(number, number, number)} callback Callback to update views.
* @constructor
*/
MultiFileDetailsPanel.Aggregator = function(ticket, metadataModel, callback) {
this.queue_ = [];
this.totalCount_ = 0;
this.totalSize_ = 0;
this.ticket_ = ticket;
this.metadataModel_ = metadataModel;
this.callback_ = callback;
};
/**
* Aggregates data of given files and enqueue directories to queue.
* @param {!Array<!FileEntry>} entries
* @private
*/
MultiFileDetailsPanel.Aggregator.prototype.enqueue = function(entries) {
var files = [];
var dirs = [];
var self = this;
for (var i = 0; i < entries.length; i++) {
var entry = entries[i];
if (entry.isFile) {
files.push(entry);
} else {
dirs.push(entry);
}
}
Array.prototype.push.apply(this.queue_, dirs);
this.metadataModel_.get(files, ['size'])
.then(function(metadatas) {
for (var i = 0; i < metadatas.length; i++) {
var metadata = metadatas[i];
self.totalCount_++;
self.totalSize_ += metadata.size;
}
if (self.update_()) {
self.dequeue_();
}
}, function(err) {
console.error(err);
}).then(function () {
if (self.update_()) {
self.dequeue_();
}
});
};
/**
* Updates views with current aggregate results.
* @return {boolean} Whether we should continue the aggregation or not.
* @private
*/
MultiFileDetailsPanel.Aggregator.prototype.update_ = function() {
return this.callback_(this.ticket_, this.totalCount_, this.totalSize_);
};
/**
* Gets one directory from queue and fetch metadata
* @private
*/
MultiFileDetailsPanel.Aggregator.prototype.dequeue_ = function() {
if (this.queue_.length === 0) {
return;
}
var self = this;
var next = this.queue_.shift();
var reader = next.createReader();
reader.readEntries(function(results) {
self.enqueue(results);
});
};
/**
* Decorates an HTML element to be a MultiFileDetailsPanel.
* @param {!HTMLDivElement} self The grid to decorate.
* @param {!MetadataModel} metadataModel File system metadata.
*/
MultiFileDetailsPanel.decorate = function(self, metadataModel) {
self.__proto__ = MultiFileDetailsPanel.prototype;
self.formatter_ = new FileMetadataFormatter();
self.metadataModel_ = metadataModel;
self.ticket_ = 0;
self.lastTotalSize_ = 0;
self.lastTotalCount_ = 0;
self.list_ = queryRequiredElement('.details-list', self);
self.aggregateRateLimitter_ =
new AsyncUtil.RateLimiter(self.startAggregation.bind(self));
self.viewUpdateRateLimitter_ =
new AsyncUtil.RateLimiter(self.updateView_.bind(self));
};
// Copyright 2016 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.
/**
* SingleFileDetailsPanel constructor.
*
* Represents grid for the details panel for a single file in Files app.
* @constructor
* @extends {HTMLDivElement}
*/
function SingleFileDetailsPanel() {
throw new Error('Use SingleFileDetailsPanel.decorate');
}
/**
* Inherits from HTMLDivElement.
*/
SingleFileDetailsPanel.prototype = {
__proto__: HTMLDivElement.prototype,
onFileSelectionChanged: function(entry) {
this.setFileName_(entry);
this.setGenericThumbnail_(entry);
this.loadMetadata_(entry);
},
/**
* Display filename for the filename.
* @param {!FileEntry} entry
* @private
*/
setFileName_: function(entry) {
this.filenameIcon_.setAttribute('file-type-icon', FileType.getIcon(entry));
this.filename_.textContent = entry.name;
},
/**
* Display generic thumbnail for the entry.
* @param {!FileEntry} entry
* @private
*/
setGenericThumbnail_: function(entry) {
if (entry.isDirectory) {
this.thumbnail_.setAttribute('generic-thumbnail', 'folder');
} else {
var mediaType = FileType.getMediaType(entry);
this.thumbnail_.setAttribute('generic-thumbnail', mediaType);
}
},
/**
* Load metadata for the entry.
* @param {!FileEntry} entry
* @private
*/
loadMetadata_: function(entry) {
this.ticket_++;
var ticket = this.ticket_;
this.thumbnail_.innerHTML = '';
this.preview_ = null;
this.thumbnail_.classList.toggle('loaded', false);
this.metadataModel_.get([entry], SingleFileDetailsPanel.LOADING_ITEMS)
.then(this.onMetadataLoaded_.bind(this, ticket, entry));
},
/**
* Called when a metadata is fetched.
* @param {number} ticket Ticket number.
* @param {!FileEntry} entry
* @param {!Array<!MetadataItem>} items metadata items
* @private
*/
onMetadataLoaded_: function(ticket, entry, items) {
if (this.ticket_ !== ticket) {
return;
}
var item = items[0];
this.setPreview_(ticket, entry, item);
this.setDetails_(entry, item);
},
/**
* Display preview for the file entry.
* @param {number} ticket Ticket number.
* @param {!FileEntry} entry
* @param {!MetadataItem} item metadata
* @private
*/
setPreview_: function(ticket, entry, item) {
var type = FileType.getType(entry);
var thumbnailUrl = item.thumbnailUrl || item.croppedThumbnailUrl;
if (type.type === 'image') {
if (item.externalFileUrl) {
// it's in Google Drive. Use ImageLoader.
if (item.thumbnailUrl) {
this.loadThumbnailFromDrive_(item.thumbnailUrl,
function (result) {
if (ticket !== this.ticket_) {
return;
}
if (result.status !== 'success') {
return;
}
var url = result.data;
var img = document.createElement('img');
this.thumbnail_.appendChild(img);
img.src = url;
this.thumbnail_.classList.toggle('loaded', true);
}.bind(this));
}
} else {
var img = document.createElement('img');
this.thumbnail_.appendChild(img);
img.src = thumbnailUrl || entry.toURL();
this.thumbnail_.classList.toggle('loaded', true);
}
} else if (type.type === 'video') {
var video = document.createElement('video');
video.controls = true;
this.thumbnail_.appendChild(video);
this.thumbnail_.classList.toggle('loaded', true);
video.src = entry.toURL();
if (item.externalFileUrl) {
// it's in google drive.
if (item.thumbnailUrl) {
this.loadThumbnailFromDrive_(item.thumbnailUrl,
function (result) {
if (ticket !== this.ticket_) {
return;
}
if (result.status !== 'success') {
return;
}
video.poster = result.data;
}.bind(this));
}
} else if (thumbnailUrl) {
video.poster = thumbnailUrl;
}
this.preview_ = video;
} else if (type.type === 'audio') {
if (item.externalFileUrl) {
// it's in google drive.
if (item.thumbnailUrl) {
this.loadThumbnailFromDrive_(item.thumbnailUrl,
function (result) {
if (ticket !== this.ticket_) {
return;
}
if (result.status !== 'success') {
return;
}
var url = result.data;
var img = document.createElement('img');
this.thumbnail_.appendChild(img);
img.src = url;
this.thumbnail_.classList.toggle('loaded', true);
}.bind(this));
}
} else {
this.loadContentMetadata_(entry, function(entry, items) {
if (ticket !== this.ticket_) {
return;
}
var item = items[0];
if (!item.contentThumbnailUrl) {
return;
}
var img = document.createElement('img');
this.thumbnail_.appendChild(img);
img.src = item.contentThumbnailUrl;
this.thumbnail_.classList.toggle('loaded', true);
}.bind(this));
}
var audio = document.createElement('audio');
audio.controls = true;
this.thumbnail_.appendChild(audio);
audio.src = entry.toURL();
this.preview_ = audio;
}
},
/**
* Load content metadata
* @param {!FileEntry} entry
* @param {function(!FileEntry, !Array<!MetadataItem>)} callback
* @private
*/
loadContentMetadata_: function(entry, callback) {
this.metadataModel_.get([entry], SingleFileDetailsPanel.CONTENT_ITEMS)
.then(callback.bind(null, entry));
},
/**
* Load thumbnails from Drive.
* @param {string} url Thumbnail url
* @param {function({status: string, data:string, width:number,
* height:number})} callback
* @private
*/
loadThumbnailFromDrive_: function (url, callback) {
ImageLoaderClient.getInstance().load(url, callback);
},
/**
* Display detailed information from metadata item.
* @param {!FileEntry} entry
* @param {!MetadataItem} item metadata
* @private
*/
setDetails_: function(entry, item) {
var elem;
var self = this;
var update = function(query, cond, thunk) {
var elem = queryRequiredElement(query, self.list_);
if (cond) {
elem.classList.toggle('available', true);
queryRequiredElement('.content', elem).textContent = thunk();
} else {
elem.classList.toggle('available', false);
}
};
update('.modification-time', item.modificationTime, function() {
return self.formatter_.formatModDate(item.modificationTime);
});
update('.file-size', item.size, function() {
return self.formatter_.formatSize(item.size, item.hosted);
});
update('.image-size', item.imageWidth && item.imageHeight, function() {
return item.imageWidth.toString()+"x"+item.imageHeight;
});
update('.media-title', item.mediaTitle, function() {
return item.mediaTitle;
});
update('.media-artist', item.mediaArtist, function() {
return item.mediaArtist;
});
// TODO(ryoh): Should we display more and more items?
},
/**
* Called when visibility of this panel is changed.
* @param {boolean} visibility True if the details panel is visible.
*/
onVisibilityChanged: function(visibility) {
if (!visibility) {
if (this.preview_) {
this.preview_.pause();
}
}
},
/**
* Cancel loading task.
*/
cancelLoading: function() {
this.ticket_++;
}
};
/**
* Metadata items to display in details panel.
* @const
*/
SingleFileDetailsPanel.LOADING_ITEMS = [
'croppedThumbnailUrl',
'customIconUrl',
'dirty',
'externalFileUrl',
'hosted',
'imageHeight',
'imageRotation',
'imageWidth',
'mediaArtist',
'mediaMimeType',
'mediaTitle',
'modificationTime',
'size',
'thumbnailUrl'
];
/**
* Metadata items to display content metadatas in details panel.
* @const
*/
SingleFileDetailsPanel.CONTENT_ITEMS = [
'contentThumbnailUrl'
];
/**
* Decorates an HTML element to be a SingleFileDetailsPanel.
* @param {!HTMLDivElement} self The grid to decorate.
* @param {!MetadataModel} metadataModel File system metadata.
*/
SingleFileDetailsPanel.decorate = function(self, metadataModel) {
self.__proto__ = SingleFileDetailsPanel.prototype;
self.metadataModel_ = metadataModel;
self.formatter_ = new FileMetadataFormatter();
self.filename_ = assertInstanceof(queryRequiredElement('.filename', self),
HTMLDivElement);
self.filenameIcon_ = assertInstanceof(
queryRequiredElement('.filename-icon', self), HTMLDivElement);
self.thumbnail_ = assertInstanceof(
queryRequiredElement('.thumbnail', self), HTMLDivElement);
self.list_ = queryRequiredElement('.details-list', self);
/**
* Preview element. Video or Audio element.
* @private {HTMLMediaElement}
*/
self.preview_ = null;
/**
* A ticket to display metadata.
* It strictly increases as the user selects files.
* Only the task that has the latest ticket can update the view.
* @private {number}
*/
self.ticket_ = 0;
};
/**
* Sets date and time format.
* @param {boolean} use12hourClock True if 12 hours clock, False if 24 hours.
*/
SingleFileDetailsPanel.prototype.setDateTimeFormat = function(use12hourClock) {
this.formatter_.setDateTimeFormat(use12hourClock);
};
......@@ -344,12 +344,6 @@
<files-toggle-ripple></files-toggle-ripple>
<div class="icon"></div>
</button>
<button id="details-button" class="icon-button" tabindex="18"
i18n-values="aria-label:DETAIL_BUTTON_TOOLTIP"
has-tooltip>
<files-toggle-ripple></files-toggle-ripple>
<div class="icon"></div>
</button>
<button id="gear-button" class="icon-button" tabindex="19"
menu="#gear-menu"
i18n-values="aria-label:GEAR_BUTTON_TOOLTIP"
......@@ -443,55 +437,6 @@
</div>
</div>
</div>
<div class="splitter" id="list-details-splitter"></div>
<div id="details-container" class="details-container">
<div id="single-file-details">
<div class="filename-container">
<div class="filename-icon detail-icon"></div>
<div class="filename"></div>
</div>
<div class="thumbnail-container">
<div class="thumbnail"></div>
</div>
<ul class="details-list">
<li class='modification-time'>
<span i18n-content="DATE_COLUMN_LABEL"></span>:
<span class='content'></span>
</li>
<li class='file-size'>
<span i18n-content="SIZE_COLUMN_LABEL"></span>:
<span class='content'></span>
</li>
<li class='image-size'>
<span i18n-content="IMAGE_RESOLUTION_COLUMN_LABEL"></span>:
<span class='content'></span>
</li>
<li class='media-title'>
<span i18n-content="MEDIA_TITLE_COLUMN_LABEL"></span>:
<span class='content'></span>
</li>
<li class='media-artist'>
<span i18n-content="MEDIA_ARTIST_COLUMN_LABEL"></span>:
<span class='content'></span>
</li>
</ul>
</div>
<div id="multi-file-details">
<div class="thumbnail-container">
<div class="thumbnail" generic-thumbnail="folder"></div>
</div>
<ul class="details-list">
<li class='file-count'>
<span i18n-content="TOTAL_FILE_COUNT"></span>:
<span class='content'></span>
</li>
<li class='file-size'>
<span i18n-content="TOTAL_FILE_SIZE"></span>:
<span class='content'></span>
</li>
</ul>
</div>
</div>
</div>
<div class="dialog-footer progressable" tabindex="-1"
visibleif="saveas-file open-file open-multi-file folder upload-folder">
......
// Copyright 2016 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';
testcase.openDetailsPanel = function() {
var appId;
StepsRunner.run([
function() {
setupAndWaitUntilReady(null, RootPath.DOWNLOADS, this.next);
},
function(results) {
appId = results.windowId;
remoteCall.callRemoteTestUtil('fakeEvent',
appId,
['#details-button', 'click'],
this.next);
},
function(result) {
remoteCall.waitForElement(appId, "#details-container").then(this.next);
},
function(result) {
chrome.test.assertFalse(result.hidden);
checkIfNoErrorsOccured(this.next);
}
]);
};
testcase.openDetailsPanelForSingleFile = function() {
var appId;
StepsRunner.run([
function() {
setupAndWaitUntilReady(null, RootPath.DOWNLOADS, this.next);
},
function(results) {
appId = results.windowId;
remoteCall.callRemoteTestUtil('fakeEvent',
appId,
['#details-button', 'click'],
this.next);
},
function(result) {
remoteCall.waitForElement(appId, "#details-container").then(this.next);
},
function(result) {
chrome.test.assertFalse(result.hidden);
remoteCall.waitForAFile('downloads', 'hello.txt').then(this.next);
},
function(result) {
remoteCall.waitForElement(appId, "#single-file-details").then(this.next);
},
function(result) {
chrome.test.assertFalse(result.hidden);
checkIfNoErrorsOccured(this.next);
}
]);
};
testcase.openSingleFileAndSeeDetailsPanel = function() {
var appId;
StepsRunner.run([
function() {
setupAndWaitUntilReady(null, RootPath.DOWNLOADS, this.next);
},
function(results) {
appId = results.windowId;
remoteCall.callRemoteTestUtil('fakeEvent',
appId,
['#details-button', 'click'],
this.next);
},
function(result) {
remoteCall.waitForElement(appId, "#details-container").then(this.next);
},
function(result) {
chrome.test.assertFalse(result.hidden);
remoteCall.callRemoteTestUtil('selectFile', appId, ['hello.txt'],
this.next);
},
function(result) {
remoteCall.waitForElement(appId, "#single-file-details .filename")
.then(this.next);
},
function(result) {
chrome.test.assertEq('hello.txt', result.text);
checkIfNoErrorsOccured(this.next);
}
]);
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment