| Index: chrome/browser/resources/file_manager/js/media/audio_player.js
|
| diff --git a/chrome/browser/resources/file_manager/js/media/audio_player.js b/chrome/browser/resources/file_manager/js/media/audio_player.js
|
| deleted file mode 100644
|
| index 1e8b776bc341d679ee1b18393c0c6bcbfeeefb10..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/resources/file_manager/js/media/audio_player.js
|
| +++ /dev/null
|
| @@ -1,624 +0,0 @@
|
| -// Copyright (c) 2012 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';
|
| -
|
| -/**
|
| - * TODO(mtomasz): Rewrite the entire audio player.
|
| - *
|
| - * @param {HTMLElement} container Container element.
|
| - * @constructor
|
| - */
|
| -function AudioPlayer(container) {
|
| - this.container_ = container;
|
| - this.metadataCache_ = MetadataCache.createFull();
|
| - this.currentTrack_ = -1;
|
| - this.playlistGeneration_ = 0;
|
| - this.volumeManager_ = new VolumeManagerWrapper(
|
| - VolumeManagerWrapper.DriveEnabledStatus.DRIVE_ENABLED);
|
| -
|
| - this.container_.classList.add('collapsed');
|
| -
|
| - function createChild(opt_className, opt_tag) {
|
| - var child = container.ownerDocument.createElement(opt_tag || 'div');
|
| - if (opt_className)
|
| - child.className = opt_className;
|
| - container.appendChild(child);
|
| - return child;
|
| - }
|
| -
|
| - // We create two separate containers (for expanded and compact view) and keep
|
| - // two sets of TrackInfo instances. We could fiddle with a single set instead
|
| - // but it would make keeping the list scroll position very tricky.
|
| - this.trackList_ = createChild('track-list');
|
| - this.trackStack_ = createChild('track-stack');
|
| -
|
| - createChild('title-button collapse').addEventListener(
|
| - 'click', this.onExpandCollapse_.bind(this));
|
| -
|
| - this.audioControls_ = new FullWindowAudioControls(
|
| - createChild(), this.advance_.bind(this), this.onError_.bind(this));
|
| -
|
| - this.audioControls_.attachMedia(createChild('', 'audio'));
|
| -
|
| - chrome.fileBrowserPrivate.getStrings(function(strings) {
|
| - container.ownerDocument.title = strings['AUDIO_PLAYER_TITLE'];
|
| - this.errorString_ = strings['AUDIO_ERROR'];
|
| - this.offlineString_ = strings['AUDIO_OFFLINE'];
|
| - AudioPlayer.TrackInfo.DEFAULT_ARTIST =
|
| - strings['AUDIO_PLAYER_DEFAULT_ARTIST'];
|
| - }.bind(this));
|
| -
|
| - this.volumeManager_.addEventListener('externally-unmounted',
|
| - this.onExternallyUnmounted_.bind(this));
|
| -
|
| - window.addEventListener('resize', this.onResize_.bind(this));
|
| -
|
| - // Show the window after DOM is processed.
|
| - var currentWindow = chrome.app.window.current();
|
| - setTimeout(currentWindow.show.bind(currentWindow), 0);
|
| -}
|
| -
|
| -/**
|
| - * Initial load method (static).
|
| - */
|
| -AudioPlayer.load = function() {
|
| - document.ondragstart = function(e) { e.preventDefault() };
|
| -
|
| - // TODO(mtomasz): Consider providing an exact size icon, instead of relying
|
| - // on downsampling by ash.
|
| - chrome.app.window.current().setIcon('images/media/2x/audio_player.png');
|
| -
|
| - AudioPlayer.instance =
|
| - new AudioPlayer(document.querySelector('.audio-player'));
|
| - reload();
|
| -};
|
| -
|
| -util.addPageLoadHandler(AudioPlayer.load);
|
| -
|
| -/**
|
| - * Unload the player.
|
| - */
|
| -function unload() {
|
| - if (AudioPlayer.instance)
|
| - AudioPlayer.instance.onUnload();
|
| -}
|
| -
|
| -/**
|
| - * Reload the player.
|
| - */
|
| -function reload() {
|
| - if (window.appState) {
|
| - util.saveAppState();
|
| - AudioPlayer.instance.load(window.appState);
|
| - return;
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Load a new playlist.
|
| - * @param {Playlist} playlist Playlist object passed via mediaPlayerPrivate.
|
| - */
|
| -AudioPlayer.prototype.load = function(playlist) {
|
| - this.playlistGeneration_++;
|
| - this.audioControls_.pause();
|
| - this.currentTrack_ = -1;
|
| - this.urls_ = playlist.items;
|
| -
|
| - this.invalidTracks_ = {};
|
| - this.cancelAutoAdvance_();
|
| -
|
| - if (this.urls_.length <= 1)
|
| - this.container_.classList.add('single-track');
|
| - else
|
| - this.container_.classList.remove('single-track');
|
| -
|
| - this.syncHeight_();
|
| -
|
| - this.trackList_.textContent = '';
|
| - this.trackStack_.textContent = '';
|
| -
|
| - this.trackListItems_ = [];
|
| - this.trackStackItems_ = [];
|
| -
|
| - if (this.urls_.length == 0)
|
| - return;
|
| -
|
| - for (var i = 0; i != this.urls_.length; i++) {
|
| - var url = this.urls_[i];
|
| - var onClick = this.select_.bind(this, i, false /* no restore */);
|
| - this.trackListItems_.push(
|
| - new AudioPlayer.TrackInfo(this.trackList_, url, onClick));
|
| - this.trackStackItems_.push(
|
| - new AudioPlayer.TrackInfo(this.trackStack_, url, onClick));
|
| - }
|
| -
|
| - this.select_(playlist.position, !!playlist.time);
|
| -
|
| - // This class will be removed if at least one track has art.
|
| - this.container_.classList.add('noart');
|
| -
|
| - // Load the selected track metadata first, then load the rest.
|
| - this.loadMetadata_(playlist.position);
|
| - for (i = 0; i != this.urls_.length; i++) {
|
| - if (i != playlist.position)
|
| - this.loadMetadata_(i);
|
| - }
|
| -};
|
| -
|
| -/**
|
| - * Load metadata for a track.
|
| - * @param {number} track Track number.
|
| - * @private
|
| - */
|
| -AudioPlayer.prototype.loadMetadata_ = function(track) {
|
| - this.fetchMetadata_(
|
| - this.urls_[track], this.displayMetadata_.bind(this, track));
|
| -};
|
| -
|
| -/**
|
| - * Display track's metadata.
|
| - * @param {number} track Track number.
|
| - * @param {Object} metadata Metadata object.
|
| - * @param {string=} opt_error Error message.
|
| - * @private
|
| - */
|
| -AudioPlayer.prototype.displayMetadata_ = function(track, metadata, opt_error) {
|
| - this.trackListItems_[track].
|
| - setMetadata(metadata, this.container_, opt_error);
|
| - this.trackStackItems_[track].
|
| - setMetadata(metadata, this.container_, opt_error);
|
| -};
|
| -
|
| -/**
|
| - * Closes audio player when a volume containing the selected item is unmounted.
|
| - * @param {Event} event The unmount event.
|
| - * @private
|
| - */
|
| -AudioPlayer.prototype.onExternallyUnmounted_ = function(event) {
|
| - if (!this.selectedItemFilesystemPath_)
|
| - return;
|
| - if (this.selectedItemFilesystemPath_.indexOf(event.mountPath) == 0)
|
| - close();
|
| -};
|
| -
|
| -/**
|
| - * Called on window is being unloaded.
|
| - */
|
| -AudioPlayer.prototype.onUnload = function() {
|
| - this.audioControls_.cleanup();
|
| - this.volumeManager_.dispose();
|
| -};
|
| -
|
| -/**
|
| - * Select a new track to play.
|
| - * @param {number} newTrack New track number.
|
| - * @param {boolean=} opt_restoreState True if restoring the play state from URL.
|
| - * @private
|
| - */
|
| -AudioPlayer.prototype.select_ = function(newTrack, opt_restoreState) {
|
| - if (this.currentTrack_ == newTrack) return;
|
| -
|
| - this.changeSelectionInList_(this.currentTrack_, newTrack);
|
| - this.changeSelectionInStack_(this.currentTrack_, newTrack);
|
| -
|
| - this.currentTrack_ = newTrack;
|
| -
|
| - if (window.appState) {
|
| - window.appState.position = this.currentTrack_;
|
| - window.appState.time = 0;
|
| - util.saveAppState();
|
| - } else {
|
| - util.platform.setPreference(AudioPlayer.TRACK_KEY, this.currentTrack_);
|
| - }
|
| -
|
| - this.scrollToCurrent_(false);
|
| -
|
| - var currentTrack = this.currentTrack_;
|
| - var url = this.urls_[currentTrack];
|
| - this.fetchMetadata_(url, function(metadata) {
|
| - if (this.currentTrack_ != currentTrack)
|
| - return;
|
| - var src = url;
|
| - this.audioControls_.load(src, opt_restoreState);
|
| -
|
| - // Resolve real filesystem path of the current audio file.
|
| - this.selectedItemFilesystemPath_ = null;
|
| - webkitResolveLocalFileSystemURL(src,
|
| - function(entry) {
|
| - if (this.currentTrack_ != currentTrack)
|
| - return;
|
| - this.selectedItemFilesystemPath_ = entry.fullPath;
|
| - }.bind(this));
|
| - }.bind(this));
|
| -};
|
| -
|
| -/**
|
| - * @param {string} url Track file url.
|
| - * @param {function(object)} callback Callback.
|
| - * @private
|
| - */
|
| -AudioPlayer.prototype.fetchMetadata_ = function(url, callback) {
|
| - this.metadataCache_.get(url, 'thumbnail|media|streaming',
|
| - function(generation, metadata) {
|
| - // Do nothing if another load happened since the metadata request.
|
| - if (this.playlistGeneration_ == generation)
|
| - callback(metadata);
|
| - }.bind(this, this.playlistGeneration_));
|
| -};
|
| -
|
| -/**
|
| - * @param {number} oldTrack Old track number.
|
| - * @param {number} newTrack New track number.
|
| - * @private
|
| - */
|
| -AudioPlayer.prototype.changeSelectionInList_ = function(oldTrack, newTrack) {
|
| - this.trackListItems_[newTrack].getBox().classList.add('selected');
|
| -
|
| - if (oldTrack >= 0) {
|
| - this.trackListItems_[oldTrack].getBox().classList.remove('selected');
|
| - }
|
| -};
|
| -
|
| -/**
|
| - * @param {number} oldTrack Old track number.
|
| - * @param {number} newTrack New track number.
|
| - * @private
|
| - */
|
| -AudioPlayer.prototype.changeSelectionInStack_ = function(oldTrack, newTrack) {
|
| - var newBox = this.trackStackItems_[newTrack].getBox();
|
| - newBox.classList.add('selected'); // Put on top immediately.
|
| - newBox.classList.add('visible'); // Start fading in.
|
| -
|
| - if (oldTrack >= 0) {
|
| - var oldBox = this.trackStackItems_[oldTrack].getBox();
|
| - oldBox.classList.remove('selected'); // Put under immediately.
|
| - setTimeout(function() {
|
| - if (!oldBox.classList.contains('selected')) {
|
| - // This will start fading out which is not really necessary because
|
| - // oldBox is already completely obscured by newBox.
|
| - oldBox.classList.remove('visible');
|
| - }
|
| - }, 300);
|
| - }
|
| -};
|
| -
|
| -/**
|
| - * Scrolls the current track into the viewport.
|
| - *
|
| - * @param {boolean} keepAtBottom If true, make the selected track the last
|
| - * of the visible (if possible). If false, perform minimal scrolling.
|
| - * @private
|
| - */
|
| -AudioPlayer.prototype.scrollToCurrent_ = function(keepAtBottom) {
|
| - var box = this.trackListItems_[this.currentTrack_].getBox();
|
| - this.trackList_.scrollTop = Math.max(
|
| - keepAtBottom ? 0 : Math.min(box.offsetTop, this.trackList_.scrollTop),
|
| - box.offsetTop + box.offsetHeight - this.trackList_.clientHeight);
|
| -};
|
| -
|
| -/**
|
| - * @return {boolean} True if the player is be displayed in compact mode.
|
| - * @private
|
| - */
|
| -AudioPlayer.prototype.isCompact_ = function() {
|
| - return this.container_.classList.contains('collapsed') ||
|
| - this.container_.classList.contains('single-track');
|
| -};
|
| -
|
| -/**
|
| - * Go to the previous or the next track.
|
| - * @param {boolean} forward True if next, false if previous.
|
| - * @param {boolean=} opt_onlyIfValid True if invalid tracks should be selected.
|
| - * @private
|
| - */
|
| -AudioPlayer.prototype.advance_ = function(forward, opt_onlyIfValid) {
|
| - this.cancelAutoAdvance_();
|
| -
|
| - var newTrack = this.currentTrack_ + (forward ? 1 : -1);
|
| - if (newTrack < 0) newTrack = this.urls_.length - 1;
|
| - if (newTrack == this.urls_.length) newTrack = 0;
|
| - if (opt_onlyIfValid && this.invalidTracks_[newTrack])
|
| - return;
|
| - this.select_(newTrack);
|
| -};
|
| -
|
| -/**
|
| - * Media error handler.
|
| - * @private
|
| - */
|
| -AudioPlayer.prototype.onError_ = function() {
|
| - var track = this.currentTrack_;
|
| -
|
| - this.invalidTracks_[track] = true;
|
| -
|
| - this.fetchMetadata_(
|
| - this.urls_[track],
|
| - function(metadata) {
|
| - var error = (!navigator.onLine && metadata.streaming) ?
|
| - this.offlineString_ : this.errorString_;
|
| - this.displayMetadata_(track, metadata, error);
|
| - this.scheduleAutoAdvance_();
|
| - }.bind(this));
|
| -};
|
| -
|
| -/**
|
| - * Schedule automatic advance to the next track after a timeout.
|
| - * @private
|
| - */
|
| -AudioPlayer.prototype.scheduleAutoAdvance_ = function() {
|
| - this.cancelAutoAdvance_();
|
| - this.autoAdvanceTimer_ = setTimeout(
|
| - function() {
|
| - this.autoAdvanceTimer_ = null;
|
| - // We are advancing only if the next track is not known to be invalid.
|
| - // This prevents an endless auto-advancing in the case when all tracks
|
| - // are invalid (we will only visit each track once).
|
| - this.advance_(true /* forward */, true /* only if valid */);
|
| - }.bind(this),
|
| - 3000);
|
| -};
|
| -
|
| -/**
|
| - * Cancel the scheduled auto advance.
|
| - * @private
|
| - */
|
| -AudioPlayer.prototype.cancelAutoAdvance_ = function() {
|
| - if (this.autoAdvanceTimer_) {
|
| - clearTimeout(this.autoAdvanceTimer_);
|
| - this.autoAdvanceTimer_ = null;
|
| - }
|
| -};
|
| -
|
| -/**
|
| - * Expand/collapse button click handler. Toggles the mode and updates the
|
| - * height of the window.
|
| - *
|
| - * @private
|
| - */
|
| -AudioPlayer.prototype.onExpandCollapse_ = function() {
|
| - if (!this.isCompact_()) {
|
| - this.setExpanded_(false);
|
| - this.lastExpandedHeight_ = window.innerHeight;
|
| - } else {
|
| - this.setExpanded_(true);
|
| - }
|
| - this.syncHeight_();
|
| -};
|
| -
|
| -/**
|
| - * Toggles the current expand mode.
|
| - *
|
| - * @param {boolean} on True if on, false otherwise.
|
| - * @private
|
| - */
|
| -AudioPlayer.prototype.setExpanded_ = function(on) {
|
| - if (on) {
|
| - this.container_.classList.remove('collapsed');
|
| - this.scrollToCurrent_(true);
|
| - } else {
|
| - this.container_.classList.add('collapsed');
|
| - }
|
| -};
|
| -
|
| -/**
|
| - * Toggles the expanded mode when resizing.
|
| - *
|
| - * @param {Event} event Resize event.
|
| - * @private
|
| - */
|
| -AudioPlayer.prototype.onResize_ = function(event) {
|
| - if (this.isCompact_() &&
|
| - window.innerHeight >= AudioPlayer.EXPANDED_MODE_MIN_HEIGHT) {
|
| - this.setExpanded_(true);
|
| - } else if (!this.isCompact_() &&
|
| - window.innerHeight < AudioPlayer.EXPANDED_MODE_MIN_HEIGHT) {
|
| - this.setExpanded_(false);
|
| - }
|
| -};
|
| -
|
| -/* Keep the below constants in sync with the CSS. */
|
| -
|
| -/**
|
| - * Window header size in pixels.
|
| - * @type {number}
|
| - * @const
|
| - */
|
| -AudioPlayer.HEADER_HEIGHT = 28;
|
| -
|
| -/**
|
| - * Track height in pixels.
|
| - * @type {number}
|
| - * @const
|
| - */
|
| -AudioPlayer.TRACK_HEIGHT = 58;
|
| -
|
| -/**
|
| - * Controls bar height in pixels.
|
| - * @type {number}
|
| - * @const
|
| - */
|
| -AudioPlayer.CONTROLS_HEIGHT = 35;
|
| -
|
| -/**
|
| - * Default number of items in the expanded mode.
|
| - * @type {number}
|
| - * @const
|
| - */
|
| -AudioPlayer.DEFAULT_EXPANDED_ITEMS = 5;
|
| -
|
| -/**
|
| - * Minimum size of the window in the expanded mode in pixels.
|
| - * @type {number}
|
| - * @const
|
| - */
|
| -AudioPlayer.EXPANDED_MODE_MIN_HEIGHT = AudioPlayer.CONTROLS_HEIGHT +
|
| - AudioPlayer.TRACK_HEIGHT * 2;
|
| -
|
| -/**
|
| - * Set the correct player window height.
|
| - * @private
|
| - */
|
| -AudioPlayer.prototype.syncHeight_ = function() {
|
| - var targetHeight;
|
| -
|
| - if (!this.isCompact_()) {
|
| - // Expanded.
|
| - if (this.lastExpandedHeight_) {
|
| - targetHeight = this.lastExpandedHeight_;
|
| - } else {
|
| - var expandedListHeight =
|
| - Math.min(this.urls_.length, AudioPlayer.DEFAULT_EXPANDED_ITEMS) *
|
| - AudioPlayer.TRACK_HEIGHT;
|
| - targetHeight = AudioPlayer.CONTROLS_HEIGHT + expandedListHeight;
|
| - }
|
| - } else {
|
| - // Not expaned.
|
| - targetHeight = AudioPlayer.CONTROLS_HEIGHT + AudioPlayer.TRACK_HEIGHT;
|
| - }
|
| -
|
| - window.resizeTo(window.innerWidth, targetHeight + AudioPlayer.HEADER_HEIGHT);
|
| -};
|
| -
|
| -/**
|
| - * Create a TrackInfo object encapsulating the information about one track.
|
| - *
|
| - * @param {HTMLElement} container Container element.
|
| - * @param {string} url Track url.
|
| - * @param {function} onClick Click handler.
|
| - * @constructor
|
| - */
|
| -AudioPlayer.TrackInfo = function(container, url, onClick) {
|
| - this.url_ = url;
|
| -
|
| - var doc = container.ownerDocument;
|
| -
|
| - this.box_ = doc.createElement('div');
|
| - this.box_.className = 'track';
|
| - this.box_.addEventListener('click', onClick);
|
| - container.appendChild(this.box_);
|
| -
|
| - this.art_ = doc.createElement('div');
|
| - this.art_.className = 'art blank';
|
| - this.box_.appendChild(this.art_);
|
| -
|
| - this.img_ = doc.createElement('img');
|
| - this.art_.appendChild(this.img_);
|
| -
|
| - this.data_ = doc.createElement('div');
|
| - this.data_.className = 'data';
|
| - this.box_.appendChild(this.data_);
|
| -
|
| - this.title_ = doc.createElement('div');
|
| - this.title_.className = 'data-title';
|
| - this.data_.appendChild(this.title_);
|
| -
|
| - this.artist_ = doc.createElement('div');
|
| - this.artist_.className = 'data-artist';
|
| - this.data_.appendChild(this.artist_);
|
| -};
|
| -
|
| -/**
|
| - * @return {HTMLDivElement} The wrapper element for the track.
|
| - */
|
| -AudioPlayer.TrackInfo.prototype.getBox = function() { return this.box_ };
|
| -
|
| -/**
|
| - * @return {string} Default track title (file name extracted from the url).
|
| - */
|
| -AudioPlayer.TrackInfo.prototype.getDefaultTitle = function() {
|
| - var title = this.url_.split('/').pop();
|
| - var dotIndex = title.lastIndexOf('.');
|
| - if (dotIndex >= 0) title = title.substr(0, dotIndex);
|
| - title = decodeURIComponent(title);
|
| - return title;
|
| -};
|
| -
|
| -/**
|
| - * TODO(kaznacheev): Localize.
|
| - */
|
| -AudioPlayer.TrackInfo.DEFAULT_ARTIST = 'Unknown Artist';
|
| -
|
| -/**
|
| - * @return {string} 'Unknown artist' string.
|
| - */
|
| -AudioPlayer.TrackInfo.prototype.getDefaultArtist = function() {
|
| - return AudioPlayer.TrackInfo.DEFAULT_ARTIST;
|
| -};
|
| -
|
| -/**
|
| - * @param {Object} metadata The metadata object.
|
| - * @param {HTMLElement} container The container for the tracks.
|
| - * @param {string} error Error string.
|
| - */
|
| -AudioPlayer.TrackInfo.prototype.setMetadata = function(
|
| - metadata, container, error) {
|
| - if (error) {
|
| - this.art_.classList.add('blank');
|
| - this.art_.classList.add('error');
|
| - container.classList.remove('noart');
|
| - } else if (metadata.thumbnail && metadata.thumbnail.url) {
|
| - this.img_.onload = function() {
|
| - // Only display the image if the thumbnail loaded successfully.
|
| - this.art_.classList.remove('blank');
|
| - container.classList.remove('noart');
|
| - }.bind(this);
|
| - this.img_.src = metadata.thumbnail.url;
|
| - }
|
| - this.title_.textContent = (metadata.media && metadata.media.title) ||
|
| - this.getDefaultTitle();
|
| - this.artist_.textContent = error ||
|
| - (metadata.media && metadata.media.artist) || this.getDefaultArtist();
|
| -};
|
| -
|
| -/**
|
| - * Audio controls specific for the Audio Player.
|
| - *
|
| - * @param {HTMLElement} container Parent container.
|
| - * @param {function(boolean)} advanceTrack Parameter: true=forward.
|
| - * @param {function} onError Error handler.
|
| - * @constructor
|
| - */
|
| -function FullWindowAudioControls(container, advanceTrack, onError) {
|
| - AudioControls.apply(this, arguments);
|
| -
|
| - document.addEventListener('keydown', function(e) {
|
| - if (e.keyIdentifier == 'U+0020') {
|
| - this.togglePlayState();
|
| - e.preventDefault();
|
| - }
|
| - }.bind(this));
|
| -}
|
| -
|
| -FullWindowAudioControls.prototype = { __proto__: AudioControls.prototype };
|
| -
|
| -/**
|
| - * Enable play state restore from the location hash.
|
| - * @param {string} src Source URL.
|
| - * @param {boolean} restore True if need to restore the play state.
|
| - */
|
| -FullWindowAudioControls.prototype.load = function(src, restore) {
|
| - this.media_.src = src;
|
| - this.media_.load();
|
| - this.restoreWhenLoaded_ = restore;
|
| -};
|
| -
|
| -/**
|
| - * Save the current state so that it survives page/app reload.
|
| - */
|
| -FullWindowAudioControls.prototype.onPlayStateChanged = function() {
|
| - this.encodeState();
|
| -};
|
| -
|
| -/**
|
| - * Restore the state after page/app reload.
|
| - */
|
| -FullWindowAudioControls.prototype.restorePlayState = function() {
|
| - if (this.restoreWhenLoaded_) {
|
| - this.restoreWhenLoaded_ = false; // This should only work once.
|
| - if (this.decodeState())
|
| - return;
|
| - }
|
| - this.play();
|
| -};
|
|
|