15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)'use strict';
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLElement} container Container element.
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function AudioPlayer(container) {
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.container_ = container;
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.metadataCache_ = MetadataCache.createFull();
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.currentTrack_ = -1;
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.playlistGeneration_ = 0;
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.volumeManager_ = VolumeManager.getInstance();
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.container_.classList.add('collapsed');
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function createChild(opt_className, opt_tag) {
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var child = container.ownerDocument.createElement(opt_tag || 'div');
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (opt_className)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      child.className = opt_className;
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    container.appendChild(child);
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return child;
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We create two separate containers (for expanded and compact view) and keep
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // two sets of TrackInfo instances. We could fiddle with a single set instead
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // but it would make keeping the list scroll position very tricky.
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.trackList_ = createChild('track-list');
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.trackStack_ = createChild('track-stack');
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  createChild('title-button collapse').addEventListener(
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'click', this.onExpandCollapse_.bind(this));
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.audioControls_ = new FullWindowAudioControls(
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      createChild(), this.advance_.bind(this), this.onError_.bind(this));
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.audioControls_.attachMedia(createChild('', 'audio'));
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  chrome.fileBrowserPrivate.getStrings(function(strings) {
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    container.ownerDocument.title = strings['AUDIO_PLAYER_TITLE'];
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.errorString_ = strings['AUDIO_ERROR'];
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.offlineString_ = strings['AUDIO_OFFLINE'];
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AudioPlayer.TrackInfo.DEFAULT_ARTIST =
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        strings['AUDIO_PLAYER_DEFAULT_ARTIST'];
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }.bind(this));
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.volumeManager_.addEventListener('externally-unmounted',
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.onExternallyUnmounted_.bind(this));
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Key in the local storage for the list of track urls.
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.PLAYLIST_KEY = 'audioPlaylist';
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Key in the local storage for the number of the current track.
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.TRACK_KEY = 'audioTrack';
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Initial load method (static).
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.load = function() {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  document.ondragstart = function(e) { e.preventDefault() };
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the audio player is starting before the first instance of the File
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Manager then it does not have access to filesystem URLs. Request it now.
72868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  chrome.fileBrowserPrivate.requestFileSystem(function() {
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AudioPlayer.instance =
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        new AudioPlayer(document.querySelector('.audio-player'));
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    chrome.mediaPlayerPrivate.onPlaylistChanged.addListener(getPlaylist);
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    reload();
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  });
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)util.addPageLoadHandler(AudioPlayer.load);
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Unload the player.
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function unload() {
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AudioPlayer.instance.audioControls_.cleanup();
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Reload the player.
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function reload() {
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (window.appState) {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Launching/reloading a v2 app.
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    util.saveAppState();
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    AudioPlayer.instance.load(window.appState);
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Lauching/reloading a v1 app.
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (document.location.hash) {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The window is reloading, restore the state.
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AudioPlayer.instance.load(null);
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getPlaylist();
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Get the playlist from Chrome.
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function getPlaylist() {
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  chrome.mediaPlayerPrivate.getPlaylist(
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AudioPlayer.instance.load.bind(AudioPlayer.instance));
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Load a new playlist.
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Playlist} playlist Playlist object passed via mediaPlayerPrivate.
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.prototype.load = function(playlist) {
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!playlist || !playlist.items.length) {
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // playlist is null if the window is being reloaded.
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // playlist is empty if ChromeOS has restarted with the Audio Player open.
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Restore the playlist from the local storage.
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    util.platform.getPreferences(function(prefs) {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      try {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var restoredPlaylist = {
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          items: JSON.parse(prefs[AudioPlayer.PLAYLIST_KEY]),
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          position: Number(prefs[AudioPlayer.TRACK_KEY]),
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          time: true // Force restoring time from document.location.
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        };
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (restoredPlaylist.items.length)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.load(restoredPlaylist);
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } catch (ignore) {}
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }.bind(this));
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!window.appState) {
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Remember the playlist for the restart.
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // App v2 handles that in the background page.
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    util.platform.setPreference(
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        AudioPlayer.PLAYLIST_KEY, JSON.stringify(playlist.items));
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    util.platform.setPreference(
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        AudioPlayer.TRACK_KEY, playlist.position);
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.playlistGeneration_++;
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.audioControls_.pause();
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.currentTrack_ = -1;
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.urls_ = playlist.items;
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.invalidTracks_ = {};
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.cancelAutoAdvance_();
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.urls_.length <= 1)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.container_.classList.add('single-track');
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.container_.classList.remove('single-track');
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.syncHeight_();
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.trackList_.textContent = '';
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.trackStack_.textContent = '';
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.trackListItems_ = [];
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.trackStackItems_ = [];
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.urls_.length == 0)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var i = 0; i != this.urls_.length; i++) {
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var url = this.urls_[i];
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    var onClick = this.select_.bind(this, i, false /* no restore */);
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.trackListItems_.push(
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        new AudioPlayer.TrackInfo(this.trackList_, url, onClick));
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.trackStackItems_.push(
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        new AudioPlayer.TrackInfo(this.trackStack_, url, onClick));
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.select_(playlist.position, !!playlist.time);
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This class will be removed if at least one track has art.
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.container_.classList.add('noart');
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Load the selected track metadata first, then load the rest.
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.loadMetadata_(playlist.position);
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (i = 0; i != this.urls_.length; i++) {
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (i != playlist.position)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.loadMetadata_(i);
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Load metadata for a track.
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {number} track Track number.
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.prototype.loadMetadata_ = function(track) {
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.fetchMetadata_(
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.urls_[track], this.displayMetadata_.bind(this, track));
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Display track's metadata.
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} track Track number.
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {Object} metadata Metadata object.
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {string=} opt_error Error message.
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.prototype.displayMetadata_ = function(track, metadata, opt_error) {
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.trackListItems_[track].
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      setMetadata(metadata, this.container_, opt_error);
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.trackStackItems_[track].
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      setMetadata(metadata, this.container_, opt_error);
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Closes audio player when a volume containing the selected item is unmounted.
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {Event} event The unmount event.
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @private
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AudioPlayer.prototype.onExternallyUnmounted_ = function(event) {
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!this.selectedItemFilesystemPath_)
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (this.selectedItemFilesystemPath_.indexOf(event.mountPath) == 0)
2317d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    close();
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Select a new track to play.
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} newTrack New track number.
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {boolean=} opt_restoreState True if restoring the play state from URL.
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.prototype.select_ = function(newTrack, opt_restoreState) {
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.currentTrack_ == newTrack) return;
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.changeSelectionInList_(this.currentTrack_, newTrack);
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.changeSelectionInStack_(this.currentTrack_, newTrack);
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.currentTrack_ = newTrack;
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (window.appState) {
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    window.appState.position = this.currentTrack_;
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    window.appState.time = 0;
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    util.saveAppState();
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    util.platform.setPreference(AudioPlayer.TRACK_KEY, this.currentTrack_);
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.scrollToCurrent_(false);
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var currentTrack = this.currentTrack_;
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var url = this.urls_[currentTrack];
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.fetchMetadata_(url, function(metadata) {
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (this.currentTrack_ != currentTrack)
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
263c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    var src = url;
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.audioControls_.load(src, opt_restoreState);
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Resolve real filesystem path of the current audio file.
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    this.selectedItemFilesystemPath_ = null;
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    webkitResolveLocalFileSystemURL(src,
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      function(entry) {
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (this.currentTrack_ != currentTrack)
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.selectedItemFilesystemPath_ = entry.fullPath;
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }.bind(this));
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }.bind(this));
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} url Track file url.
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(object)} callback Callback.
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.prototype.fetchMetadata_ = function(url, callback) {
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.metadataCache_.get(url, 'thumbnail|media|streaming',
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function(generation, metadata) {
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Do nothing if another load happened since the metadata request.
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (this.playlistGeneration_ == generation)
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          callback(metadata);
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }.bind(this, this.playlistGeneration_));
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} oldTrack Old track number.
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} newTrack New track number.
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.prototype.changeSelectionInList_ = function(oldTrack, newTrack) {
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.trackListItems_[newTrack].getBox().classList.add('selected');
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (oldTrack >= 0) {
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.trackListItems_[oldTrack].getBox().classList.remove('selected');
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} oldTrack Old track number.
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} newTrack New track number.
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.prototype.changeSelectionInStack_ = function(oldTrack, newTrack) {
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var newBox = this.trackStackItems_[newTrack].getBox();
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  newBox.classList.add('selected');  // Put on top immediately.
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  newBox.classList.add('visible');  // Start fading in.
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (oldTrack >= 0) {
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var oldBox = this.trackStackItems_[oldTrack].getBox();
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    oldBox.classList.remove('selected'); // Put under immediately.
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    setTimeout(function() {
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!oldBox.classList.contains('selected')) {
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // This will start fading out which is not really necessary because
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // oldBox is already completely obscured by newBox.
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        oldBox.classList.remove('visible');
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }, 300);
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Scrolls the current track into the viewport.
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} keepAtBottom If true, make the selected track the last
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *   of the visible (if possible). If false, perform minimal scrolling.
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.prototype.scrollToCurrent_ = function(keepAtBottom) {
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var box = this.trackListItems_[this.currentTrack_].getBox();
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.trackList_.scrollTop = Math.max(
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      keepAtBottom ? 0 : Math.min(box.offsetTop, this.trackList_.scrollTop),
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      box.offsetTop + box.offsetHeight - this.trackList_.clientHeight);
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if the player is be displayed in compact mode.
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.prototype.isCompact_ = function() {
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this.container_.classList.contains('collapsed') ||
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         this.container_.classList.contains('single-track');
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Go to the previous or the next track.
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} forward True if next, false if previous.
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {boolean=} opt_onlyIfValid True if invalid tracks should be selected.
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.prototype.advance_ = function(forward, opt_onlyIfValid) {
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.cancelAutoAdvance_();
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var newTrack = this.currentTrack_ + (forward ? 1 : -1);
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (newTrack < 0) newTrack = this.urls_.length - 1;
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (newTrack == this.urls_.length) newTrack = 0;
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (opt_onlyIfValid && this.invalidTracks_[newTrack])
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.select_(newTrack);
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Media error handler.
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.prototype.onError_ = function() {
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var track = this.currentTrack_;
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.invalidTracks_[track] = true;
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.fetchMetadata_(
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.urls_[track],
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function(metadata) {
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var error = (!navigator.onLine && metadata.streaming) ?
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            this.offlineString_ : this.errorString_;
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.displayMetadata_(track, metadata, error);
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.scheduleAutoAdvance_();
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }.bind(this));
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Schedule automatic advance to the next track after a timeout.
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.prototype.scheduleAutoAdvance_ = function() {
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.cancelAutoAdvance_();
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.autoAdvanceTimer_ = setTimeout(
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function() {
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.autoAdvanceTimer_ = null;
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // We are advancing only if the next track is not known to be invalid.
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // This prevents an endless auto-advancing in the case when all tracks
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // are invalid (we will only visit each track once).
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.advance_(true /* forward */, true /* only if valid */);
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }.bind(this),
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      3000);
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Cancel the scheduled auto advance.
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.prototype.cancelAutoAdvance_ = function() {
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.autoAdvanceTimer_) {
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    clearTimeout(this.autoAdvanceTimer_);
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.autoAdvanceTimer_ = null;
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Expand/collapse button click handler.
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.prototype.onExpandCollapse_ = function() {
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.container_.classList.toggle('collapsed');
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.syncHeight_();
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!this.isCompact_())
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.scrollToCurrent_(true);
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* Keep the below constants in sync with the CSS. */
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Player header height.
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * TODO(kaznacheev): Set to 30 when the audio player is title-less.
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.HEADER_HEIGHT = 0;
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Track height.
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.TRACK_HEIGHT = 58;
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Controls bar height.
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.CONTROLS_HEIGHT = 35;
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Set the correct player window height.
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.prototype.syncHeight_ = function() {
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var expandedListHeight =
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Math.min(this.urls_.length, 3) * AudioPlayer.TRACK_HEIGHT;
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.trackList_.style.height = expandedListHeight + 'px';
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var targetClientHeight = AudioPlayer.CONTROLS_HEIGHT +
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (this.isCompact_() ?
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AudioPlayer.TRACK_HEIGHT :
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AudioPlayer.HEADER_HEIGHT + expandedListHeight);
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4577d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  var appWindow = chrome.app.window.current();
4587d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  var oldHeight = appWindow.contentWindow.outerHeight;
4597d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  var bottom = appWindow.contentWindow.screenY + oldHeight;
4607d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  var newTop = Math.max(0, bottom - targetClientHeight);
4617d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  appWindow.moveTo(appWindow.contentWindow.screenX, newTop);
4627d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  appWindow.resizeTo(appWindow.contentWindow.outerWidth,
4637d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      oldHeight + targetClientHeight - this.container_.clientHeight);
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Create a TrackInfo object encapsulating the information about one track.
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLElement} container Container element.
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} url Track url.
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} onClick Click handler.
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.TrackInfo = function(container, url, onClick) {
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.url_ = url;
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var doc = container.ownerDocument;
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.box_ = doc.createElement('div');
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.box_.className = 'track';
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.box_.addEventListener('click', onClick);
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  container.appendChild(this.box_);
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.art_ = doc.createElement('div');
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.art_.className = 'art blank';
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.box_.appendChild(this.art_);
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.img_ = doc.createElement('img');
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.art_.appendChild(this.img_);
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.data_ = doc.createElement('div');
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.data_.className = 'data';
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.box_.appendChild(this.data_);
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.title_ = doc.createElement('div');
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.title_.className = 'data-title';
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.data_.appendChild(this.title_);
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.artist_ = doc.createElement('div');
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.artist_.className = 'data-artist';
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.data_.appendChild(this.artist_);
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {HTMLDivElement} The wrapper element for the track.
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.TrackInfo.prototype.getBox = function() { return this.box_ };
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} Default track title (file name extracted from the url).
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.TrackInfo.prototype.getDefaultTitle = function() {
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var title = this.url_.split('/').pop();
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var dotIndex = title.lastIndexOf('.');
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (dotIndex >= 0) title = title.substr(0, dotIndex);
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  title = decodeURIComponent(title);
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return title;
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * TODO(kaznacheev): Localize.
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.TrackInfo.DEFAULT_ARTIST = 'Unknown Artist';
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} 'Unknown artist' string.
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.TrackInfo.prototype.getDefaultArtist = function() {
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return AudioPlayer.TrackInfo.DEFAULT_ARTIST;
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} metadata The metadata object.
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLElement} container The container for the tracks.
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} error Error string.
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioPlayer.TrackInfo.prototype.setMetadata = function(
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    metadata, container, error) {
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (error) {
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.art_.classList.add('blank');
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.art_.classList.add('error');
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    container.classList.remove('noart');
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (metadata.thumbnail && metadata.thumbnail.url) {
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.img_.onload = function() {
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Only display the image if the thumbnail loaded successfully.
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.art_.classList.remove('blank');
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      container.classList.remove('noart');
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }.bind(this);
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.img_.src = metadata.thumbnail.url;
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.title_.textContent = (metadata.media && metadata.media.title) ||
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.getDefaultTitle();
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.artist_.textContent = error ||
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (metadata.media && metadata.media.artist) || this.getDefaultArtist();
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Audio controls specific for the Audio Player.
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLElement} container Parent container.
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(boolean)} advanceTrack Parameter: true=forward.
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} onError Error handler.
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function FullWindowAudioControls(container, advanceTrack, onError) {
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AudioControls.apply(this, arguments);
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  document.addEventListener('keydown', function(e) {
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (e.keyIdentifier == 'U+0020') {
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.togglePlayState();
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      e.preventDefault();
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }.bind(this));
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FullWindowAudioControls.prototype = { __proto__: AudioControls.prototype };
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Enable play state restore from the location hash.
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} src Source URL.
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} restore True if need to restore the play state.
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FullWindowAudioControls.prototype.load = function(src, restore) {
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.media_.src = src;
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.media_.load();
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.restoreWhenLoaded_ = restore;
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Save the current state so that it survives page/app reload.
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FullWindowAudioControls.prototype.onPlayStateChanged = function() {
5942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.encodeState();
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Restore the state after page/app reload.
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FullWindowAudioControls.prototype.restorePlayState = function() {
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.restoreWhenLoaded_) {
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.restoreWhenLoaded_ = false;  // This should only work once.
6032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (this.decodeState())
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.play();
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
608