| Index: ui/file_manager/file_manager/foreground/js/ui/single_file_details.js
|
| diff --git a/ui/file_manager/file_manager/foreground/js/ui/single_file_details.js b/ui/file_manager/file_manager/foreground/js/ui/single_file_details.js
|
| index 5329e1c3325c27da73d146a9bf5a3ee79ed93d01..6a191a82962f9be7d8da695988d0172de115febe 100644
|
| --- a/ui/file_manager/file_manager/foreground/js/ui/single_file_details.js
|
| +++ b/ui/file_manager/file_manager/foreground/js/ui/single_file_details.js
|
| @@ -20,111 +20,299 @@ function SingleFileDetailsPanel() {
|
| 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;
|
| - }
|
| -};
|
| + },
|
|
|
| -/**
|
| - * Decorates an HTML element to be a SingleFileDetailsList.
|
| - * @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;
|
| /**
|
| - * Data model of detail infos.
|
| - * @private {!SingleFileDetailsDataModel}
|
| - * @const
|
| + * Display generic thumbnail for the entry.
|
| + * @param {!FileEntry} entry
|
| + * @private
|
| */
|
| - self.model_ = new SingleFileDetailsDataModel();
|
| - self.filename_ = assertInstanceof(queryRequiredElement('.filename', self),
|
| - HTMLDivElement);
|
| - var list = queryRequiredElement('.details-list', self);
|
| - SingleFileDetailsList.decorate(list, metadataModel);
|
| - self.list_ = assertInstanceof(list, SingleFileDetailsList);
|
| - self.list_.dataModel = self.model_;
|
| - self.list_.autoExpands = true;
|
| -};
|
| + setGenericThumbnail_: function(entry) {
|
| + if (entry.isDirectory) {
|
| + this.thumbnail_.setAttribute('generic-thumbnail', 'folder');
|
| + } else {
|
| + var mediaType = FileType.getMediaType(entry);
|
| + this.thumbnail_.setAttribute('generic-thumbnail', mediaType);
|
| + }
|
| + },
|
|
|
| -/**
|
| - * SingleFileDetailsList constructor.
|
| - *
|
| - * Represents grid for the details list for a single file in Files app.
|
| - * @constructor
|
| - * @extends {cr.ui.List}
|
| - */
|
| -function SingleFileDetailsList() {
|
| - throw new Error('Use SingleFileDetailsList.decorate');
|
| -}
|
| + /**
|
| + * 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));
|
| + },
|
|
|
| -/**
|
| - * Inherits from cr.ui.List.
|
| - */
|
| -SingleFileDetailsList.prototype = {
|
| - __proto__: cr.ui.List.prototype,
|
| - onFileSelectionChanged: function(entry) {
|
| - console.log(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);
|
| + },
|
|
|
| -/**
|
| - * Decorates an HTML element to be a SingleFileDetailsList.
|
| - * @param {!Element} self The grid to decorate.
|
| - * @param {!MetadataModel} metadataModel File system metadata.
|
| - */
|
| -SingleFileDetailsList.decorate = function(self, metadataModel) {
|
| - cr.ui.Grid.decorate(self);
|
| - self.__proto__ = SingleFileDetailsList.prototype;
|
| - self.metadataModel_ = metadataModel;
|
| + /**
|
| + * 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.externalFileUrl;
|
| + 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;
|
| + }
|
| + },
|
|
|
| - self.scrollBar_ = new ScrollBar();
|
| - self.scrollBar_.initialize(self.parentElement, self);
|
| + /**
|
| + * 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);
|
| + },
|
|
|
| - self.itemConstructor = function(entry) {
|
| - var item = self.ownerDocument.createElement('li');
|
| - SingleFileDetailsList.Item.decorate(
|
| - item,
|
| - entry,
|
| - /** @type {FileGrid} */ (self));
|
| - return item;
|
| - };
|
| + /**
|
| + * Display detailed information from metadata item.
|
| + * @param {!FileEntry} entry
|
| + * @param {!MetadataItem} item metadata
|
| + * @private
|
| + */
|
| + setDetails_: function(entry, item) {
|
| + var elem;
|
| + // Modification Time
|
| + elem = queryRequiredElement('.modification-time', this.list_);
|
| + if (item.modificationTime) {
|
| + elem.classList.toggle('available', true);
|
| + queryRequiredElement('.content', elem).textContent =
|
| + this.formatter_.formatModDate(item.modificationTime);
|
| + } else {
|
| + elem.classList.toggle('available', false);
|
| + }
|
| + // Filesize
|
| + elem = queryRequiredElement('.file-size', this.list_);
|
| + if (item.size) {
|
| + elem.classList.toggle('available', true);
|
| + queryRequiredElement('.content', elem).textContent =
|
| + this.formatter_.formatSize(item.size, item.hosted);
|
| + } else {
|
| + elem.classList.toggle('available', false);
|
| + }
|
| + // TODO(ryoh): Display more and more details...
|
| + },
|
| + /**
|
| + * 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();
|
| + }
|
| + }
|
| + }
|
| };
|
|
|
| /**
|
| - * Item for the Grid View.
|
| - * @constructor
|
| - * @extends {cr.ui.ListItem}
|
| + * Metadata items to display in details panel.
|
| + * @const
|
| */
|
| -SingleFileDetailsList.Item = function() {
|
| - throw new Error();
|
| -};
|
| +SingleFileDetailsPanel.LOADING_ITEMS = [
|
| + 'availableOffline',
|
| + 'availableWhenMetered',
|
| + 'croppedThumbnailUrl',
|
| + 'customIconUrl',
|
| + 'dirty',
|
| + 'externalFileUrl',
|
| + 'hosted',
|
| + 'imageHeight',
|
| + 'imageRotation',
|
| + 'imageWidth',
|
| + 'mediaArtist',
|
| + 'mediaMimeType',
|
| + 'mediaTitle',
|
| + 'modificationTime',
|
| + 'pinned',
|
| + 'present',
|
| + 'shared',
|
| + 'sharedWithMe',
|
| + 'size',
|
| + 'thumbnailUrl'
|
| +];
|
|
|
| /**
|
| - * Inherits from cr.ui.DetailsItem.
|
| + * Metadata items to display content metadatas in details panel.
|
| + * @const
|
| */
|
| -SingleFileDetailsList.Item.prototype.__proto__ = cr.ui.ListItem.prototype;
|
| +SingleFileDetailsPanel.CONTENT_ITEMS = [
|
| + 'contentThumbnailUrl'
|
| +];
|
|
|
| /**
|
| - * @param {Element} li List item element.
|
| - * @param {!Entry} entry File entry.
|
| - * @param {FileGrid} grid Owner.
|
| + * Decorates an HTML element to be a SingleFileDetailsList.
|
| + * @param {!HTMLDivElement} self The grid to decorate.
|
| + * @param {!MetadataModel} metadataModel File system metadata.
|
| */
|
| -SingleFileDetailsList.Item.decorate = function(li, entry, grid) {
|
| - li.__proto__ = SingleFileDetailsList.Item.prototype;
|
| +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;
|
| };
|
|
|
| /**
|
| - * Data model for details panel.
|
| - *
|
| - * @constructor
|
| - * @extends {cr.ui.ArrayDataModel}
|
| + * Sets date and time format.
|
| + * @param {boolean} use12hourClock True if 12 hours clock, False if 24 hours.
|
| */
|
| -function SingleFileDetailsDataModel() {
|
| - cr.ui.ArrayDataModel.call(this, []);
|
| -}
|
| -
|
| -SingleFileDetailsDataModel.prototype = {
|
| - __proto__: cr.ui.ArrayDataModel.prototype,
|
| -
|
| +SingleFileDetailsPanel.prototype.setDateTimeFormat = function(use12hourClock) {
|
| + this.formatter_.setDateTimeFormat(use12hourClock);
|
| };
|
|
|