Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / ui / file_manager / video_player / js / video_player.js
index 9411822..31f056e 100644 (file)
@@ -25,27 +25,53 @@ function FullWindowVideoControls(
   this.updateStyle();
   window.addEventListener('resize', this.updateStyle.wrap(this));
   document.addEventListener('keydown', function(e) {
-    if (e.keyIdentifier == 'U+0020') {  // Space
-      this.togglePlayStateWithFeedback();
-      e.preventDefault();
-    }
-    if (e.keyIdentifier == 'U+001B') {  // Escape
-      util.toggleFullScreen(
-          chrome.app.window.current(),
-          false);  // Leave the full screen mode.
-      e.preventDefault();
+    switch (e.keyIdentifier) {
+      case 'U+0020': // Space
+      case 'MediaPlayPause':
+        this.togglePlayStateWithFeedback();
+        break;
+      case 'U+001B': // Escape
+        util.toggleFullScreen(
+            chrome.app.window.current(),
+            false);  // Leave the full screen mode.
+        break;
+      case 'Right':
+      case 'MediaNextTrack':
+        player.advance_(1);
+        break;
+      case 'Left':
+      case 'MediaPreviousTrack':
+        player.advance_(0);
+        break;
+      case 'MediaStop':
+        // TODO: Define "Stop" behavior.
+        break;
     }
   }.wrap(this));
 
   // TODO(mtomasz): Simplify. crbug.com/254318.
+  var clickInProgress = false;
   videoContainer.addEventListener('click', function(e) {
-    if (e.ctrlKey) {
-      this.toggleLoopedModeWithFeedback(true);
-      if (!this.isPlaying())
+    if (clickInProgress)
+      return;
+
+    clickInProgress = true;
+    var togglePlayState = function() {
+      clickInProgress = false;
+
+      if (e.ctrlKey) {
+        this.toggleLoopedModeWithFeedback(true);
+        if (!this.isPlaying())
+          this.togglePlayStateWithFeedback();
+      } else {
         this.togglePlayStateWithFeedback();
-    } else {
-      this.togglePlayStateWithFeedback();
-    }
+      }
+    }.wrap(this);
+
+    if (!this.media_)
+      player.reloadCurrentVideo(togglePlayState);
+    else
+      setTimeout(togglePlayState);
   }.wrap(this));
 
   this.inactivityWatcher_ = new MouseInactivityWatcher(playerContainer);
@@ -62,9 +88,8 @@ FullWindowVideoControls.prototype = { __proto__: VideoControls.prototype };
  * Displays error message.
  *
  * @param {string} message Message id.
- * @private
  */
-FullWindowVideoControls.prototype.showErrorMessage_ = function(message) {
+FullWindowVideoControls.prototype.showErrorMessage = function(message) {
   var errorBanner = document.querySelector('#error');
   errorBanner.textContent =
       loadTimeData.getString(message);
@@ -79,7 +104,7 @@ FullWindowVideoControls.prototype.showErrorMessage_ = function(message) {
  * @private
  */
 FullWindowVideoControls.prototype.onPlaybackError_ = function() {
-  this.showErrorMessage_('GALLERY_VIDEO_DECODING_ERROR');
+  this.showErrorMessage('GALLERY_VIDEO_DECODING_ERROR');
   this.decodeErrorOccured = true;
 
   // Disable inactivity watcher, and disable the ui, by hiding tools manually.
@@ -105,6 +130,15 @@ FullWindowVideoControls.prototype.toggleFullScreen_ = function() {
 };
 
 /**
+ * Media completion handler.
+ */
+FullWindowVideoControls.prototype.onMediaComplete = function() {
+  VideoControls.prototype.onMediaComplete.apply(this, arguments);
+  if (!this.getMedia().loop)
+    player.advance_(1);
+};
+
+/**
  * @constructor
  */
 function VideoPlayer() {
@@ -113,6 +147,11 @@ function VideoPlayer() {
   this.videos_ = null;
   this.currentPos_ = 0;
 
+  this.currentSession_ = null;
+  this.currentCast_ = null;
+
+  this.loadQueue_ = new AsyncUtil.Queue();
+
   Object.seal(this);
 }
 
@@ -137,29 +176,46 @@ VideoPlayer.prototype.prepare = function(videos) {
   var maximizeButton = document.querySelector('.maximize-button');
   maximizeButton.addEventListener(
     'click',
-    function() {
+    function(event) {
       var appWindow = chrome.app.window.current();
       if (appWindow.isMaximized())
         appWindow.restore();
       else
         appWindow.maximize();
+      event.stopPropagation();
     }.wrap(null));
   maximizeButton.addEventListener('mousedown', preventDefault);
 
   var minimizeButton = document.querySelector('.minimize-button');
   minimizeButton.addEventListener(
     'click',
-    function() {
+    function(event) {
       chrome.app.window.current().minimize()
+      event.stopPropagation();
     }.wrap(null));
   minimizeButton.addEventListener('mousedown', preventDefault);
 
   var closeButton = document.querySelector('.close-button');
   closeButton.addEventListener(
     'click',
-    function() { close(); }.wrap(null));
+    function(event) {
+      close();
+      event.stopPropagation();
+    }.wrap(null));
   closeButton.addEventListener('mousedown', preventDefault);
 
+  var castButton = document.querySelector('.cast-button');
+  cr.ui.decorate(castButton, cr.ui.MenuButton);
+  castButton.addEventListener(
+    'click',
+    function(event) {
+      event.stopPropagation();
+    }.wrap(null));
+  castButton.addEventListener('mousedown', preventDefault);
+
+  var menu = document.querySelector('#cast-menu');
+  cr.ui.decorate(menu, cr.ui.Menu);
+
   this.controls_ = new FullWindowVideoControls(
       document.querySelector('#video-player'),
       document.querySelector('#video-container'),
@@ -169,7 +225,7 @@ VideoPlayer.prototype.prepare = function(videos) {
     if (this.controls_.decodeErrorOccured &&
         // Ignore shortcut keys
         !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey) {
-      this.reloadCurrentVideo_(function() {
+      this.reloadCurrentVideo(function() {
         this.videoElement_.play();
       }.wrap(this));
       e.preventDefault();
@@ -187,8 +243,8 @@ VideoPlayer.prototype.prepare = function(videos) {
   else
     videoPlayerElement.removeAttribute('multiple');
 
-  document.addEventListener('keydown', reloadVideo, true);
-  document.addEventListener('click', reloadVideo, true);
+  document.addEventListener('keydown', reloadVideo);
+  document.addEventListener('click', reloadVideo);
 };
 
 /**
@@ -204,52 +260,123 @@ function unload() {
 
 /**
  * Loads the video file.
- * @param {string} url URL of the video file.
- * @param {string} title Title of the video file.
+ * @param {Object} video Data of the video file.
  * @param {function()=} opt_callback Completion callback.
  * @private
  */
-VideoPlayer.prototype.loadVideo_ = function(url, title, opt_callback) {
-  this.unloadVideo();
+VideoPlayer.prototype.loadVideo_ = function(video, opt_callback) {
+  this.unloadVideo(true);
+
+  this.loadQueue_.run(function(callback) {
+    document.title = video.title;
+
+    document.querySelector('#title').innerText = video.title;
+
+    var videoPlayerElement = document.querySelector('#video-player');
+    if (this.currentPos_ === (this.videos_.length - 1))
+      videoPlayerElement.setAttribute('last-video', true);
+    else
+      videoPlayerElement.removeAttribute('last-video');
+
+    if (this.currentPos_ === 0)
+      videoPlayerElement.setAttribute('first-video', true);
+    else
+      videoPlayerElement.removeAttribute('first-video');
+
+    // Re-enables ui and hides error message if already displayed.
+    document.querySelector('#video-player').removeAttribute('disabled');
+    document.querySelector('#error').removeAttribute('visible');
+    this.controls.inactivityWatcher.disabled = true;
+    this.controls.decodeErrorOccured = false;
+
+    videoPlayerElement.setAttribute('loading', true);
+
+    var media = new MediaManager(video.entry);
+
+    Promise.all([media.getThumbnail(), media.getToken()]).then(
+        function(results) {
+          var url = results[0];
+          var token = results[1];
+          document.querySelector('#thumbnail').style.backgroundImage =
+              'url(' + url + '&access_token=' + token + ')';
+        }).catch(function() {
+          // Shows no image on error.
+          document.querySelector('#thumbnail').style.backgroundImage = '';
+        });
+
+    var videoElementInitializePromise;
+    if (this.currentCast_) {
+      videoPlayerElement.setAttribute('casting', true);
+
+      document.querySelector('#cast-name-label').textContent =
+          loadTimeData.getString('VIDEO_PLAYER_PLAYING_ON');
+      document.querySelector('#cast-name').textContent =
+          this.currentCast_.friendlyName;
+
+      videoPlayerElement.setAttribute('castable', true);
+
+      videoElementInitializePromise =
+        media.isAvailableForCast().then(function(result) {
+            if (!result)
+              return Promise.reject('No casts are available.');
+
+            return new Promise(function(fulfill, reject) {
+              chrome.cast.requestSession(
+                  fulfill, reject, undefined, this.currentCast_.label);
+            }.bind(this)).then(function(session) {
+              this.currentSession_ = session;
+              this.videoElement_ = new CastVideoElement(media, session);
+              this.controls.attachMedia(this.videoElement_);
+            }.bind(this));
+          }.bind(this));
+    } else {
+      videoPlayerElement.removeAttribute('casting');
 
-  document.title = title;
+      this.videoElement_ = document.createElement('video');
+      document.querySelector('#video-container').appendChild(
+          this.videoElement_);
 
-  document.querySelector('#title').innerText = title;
+      this.controls.attachMedia(this.videoElement_);
+      this.videoElement_.src = video.url;
 
-  var videoPlayerElement = document.querySelector('#video-player');
-  if (this.currentPos_ === (this.videos_.length - 1))
-    videoPlayerElement.setAttribute('last-video', true);
-  else
-    videoPlayerElement.removeAttribute('last-video');
+      media.isAvailableForCast().then(function(result) {
+        if (result)
+          videoPlayerElement.setAttribute('castable', true);
+        else
+          videoPlayerElement.removeAttribute('castable');
+      }).catch(function() {
+        videoPlayerElement.setAttribute('castable', true);
+      });
 
-  if (this.currentPos_ === 0)
-    videoPlayerElement.setAttribute('first-video', true);
-  else
-    videoPlayerElement.removeAttribute('first-video');
-
-  // Re-enables ui and hides error message if already displayed.
-  document.querySelector('#video-player').removeAttribute('disabled');
-  document.querySelector('#error').removeAttribute('visible');
-  this.controls.inactivityWatcher.disabled = false;
-  this.controls.decodeErrorOccured = false;
-
-  this.videoElement_ = document.createElement('video');
-  document.querySelector('#video-container').appendChild(this.videoElement_);
-  this.controls.attachMedia(this.videoElement_);
-
-  this.videoElement_.src = url;
-  this.videoElement_.load();
-
-  if (opt_callback) {
-    var handler = function(currentPos, event) {
-      console.log('loaded: ', currentPos, this.currentPos_);
-      if (currentPos === this.currentPos_)
-        opt_callback();
-      this.videoElement_.removeEventListener('loadedmetadata', handler);
-    }.wrap(this, this.currentPos_);
-
-    this.videoElement_.addEventListener('loadedmetadata', handler);
-  }
+      videoElementInitializePromise = Promise.resolve();
+    }
+
+    videoElementInitializePromise.
+        then(function() {
+          var handler = function(currentPos) {
+            if (currentPos === this.currentPos_) {
+              if (opt_callback)
+                opt_callback();
+              videoPlayerElement.removeAttribute('loading');
+              this.controls.inactivityWatcher.disabled = false;
+            }
+
+            this.videoElement_.removeEventListener('loadedmetadata', handler);
+          }.wrap(this, this.currentPos_);
+
+          this.videoElement_.addEventListener('loadedmetadata', handler);
+          this.videoElement_.load();
+          callback();
+        }.bind(this)).
+        // In case of error.
+        catch(function(error) {
+          videoPlayerElement.removeAttribute('loading');
+          console.error('Failed to initialize the video element.',
+                        error.stack || error);
+          this.controls_.showErrorMessage('GALLERY_VIDEO_ERROR');
+          callback();
+        }.bind(this));
+  }.wrap(this));
 };
 
 /**
@@ -257,17 +384,33 @@ VideoPlayer.prototype.loadVideo_ = function(url, title, opt_callback) {
  */
 VideoPlayer.prototype.playFirstVideo = function() {
   this.currentPos_ = 0;
-  this.reloadCurrentVideo_(this.onFirstVideoReady_.wrap(this));
+  this.reloadCurrentVideo(this.onFirstVideoReady_.wrap(this));
 };
 
 /**
  * Unloads the current video.
+ * @param {boolean=} opt_keepSession If true, keep using the current session.
+ *     Otherwise, discards the session.
  */
-VideoPlayer.prototype.unloadVideo = function() {
-  // Detach the previous video element, if exists.
-  if (this.videoElement_)
-    this.videoElement_.parentNode.removeChild(this.videoElement_);
-  this.videoElement_ = null;
+VideoPlayer.prototype.unloadVideo = function(opt_keepSession) {
+  this.loadQueue_.run(function(callback) {
+    if (this.videoElement_) {
+      // If the element has dispose method, call it (CastVideoElement has it).
+      if (this.videoElement_.dispose)
+        this.videoElement_.dispose();
+      // Detach the previous video element, if exists.
+      if (this.videoElement_.parentNode)
+        this.videoElement_.parentNode.removeChild(this.videoElement_);
+    }
+    this.videoElement_ = null;
+
+    if (!opt_keepSession && this.currentSession_) {
+      this.currentSession_.stop(callback, callback);
+      this.currentSession_ = null;
+    } else {
+      callback();
+    }
+  }.wrap(this));
 };
 
 /**
@@ -275,26 +418,22 @@ VideoPlayer.prototype.unloadVideo = function() {
  * @private
  */
 VideoPlayer.prototype.onFirstVideoReady_ = function() {
-  // TODO: chrome.app.window soon will be able to resize the content area.
-  // Until then use approximate title bar height.
-  var TITLE_HEIGHT = 33;
-
   var videoWidth = this.videoElement_.videoWidth;
   var videoHeight = this.videoElement_.videoHeight;
 
   var aspect = videoWidth / videoHeight;
   var newWidth = videoWidth;
-  var newHeight = videoHeight + TITLE_HEIGHT;
+  var newHeight = videoHeight;
 
   var shrinkX = newWidth / window.screen.availWidth;
   var shrinkY = newHeight / window.screen.availHeight;
   if (shrinkX > 1 || shrinkY > 1) {
     if (shrinkY > shrinkX) {
       newHeight = newHeight / shrinkY;
-      newWidth = (newHeight - TITLE_HEIGHT) * aspect;
+      newWidth = newHeight * aspect;
     } else {
       newWidth = newWidth / shrinkX;
-      newHeight = newWidth / aspect + TITLE_HEIGHT;
+      newHeight = newWidth / aspect;
     }
   }
 
@@ -327,7 +466,7 @@ VideoPlayer.prototype.advance_ = function(direction) {
   var newPos = this.currentPos_ + (direction ? 1 : -1);
   if (0 <= newPos && newPos < this.videos_.length) {
     this.currentPos_ = newPos;
-    this.reloadCurrentVideo_(function() {
+    this.reloadCurrentVideo(function() {
       this.videoElement_.play();
     }.wrap(this));
   }
@@ -337,11 +476,105 @@ VideoPlayer.prototype.advance_ = function(direction) {
  * Reloads the current video.
  *
  * @param {function()=} opt_callback Completion callback.
- * @private
  */
-VideoPlayer.prototype.reloadCurrentVideo_ = function(opt_callback) {
+VideoPlayer.prototype.reloadCurrentVideo = function(opt_callback) {
   var currentVideo = this.videos_[this.currentPos_];
-  this.loadVideo_(currentVideo.fileUrl, currentVideo.entry.name, opt_callback);
+  this.loadVideo_(currentVideo, opt_callback);
+};
+
+/**
+ * Invokes when a menuitem in the cast menu is selected.
+ * @param {Object} cast Selected element in the list of casts.
+ */
+VideoPlayer.prototype.onCastSelected_ = function(cast) {
+  // If the selected item is same as the current item, do nothing.
+  if ((this.currentCast_ && this.currentCast_.label) === (cast && cast.label))
+    return;
+
+  this.currentCast_ = cast || null;
+  this.updateCheckOnCastMenu_();
+  this.reloadCurrentVideo();
+};
+
+/**
+ * Set the list of casts.
+ * @param {Array.<Object>} casts List of casts.
+ */
+VideoPlayer.prototype.setCastList = function(casts) {
+  var videoPlayerElement = document.querySelector('#video-player');
+  var menu = document.querySelector('#cast-menu');
+  menu.innerHTML = '';
+
+  // TODO(yoshiki): Handle the case that the current cast disappears.
+
+  if (casts.length === 0) {
+    videoPlayerElement.removeAttribute('cast-available');
+    if (this.currentCast_)
+      this.onCurrentCastDisappear_();
+    return;
+  }
+
+  if (this.currentCast_) {
+    var currentCastAvailable = casts.some(function(cast) {
+      return this.currentCast_.label === cast.label;
+    }.wrap(this));
+
+    if (!currentCastAvailable)
+      this.onCurrentCastDisappear_();
+  }
+
+  var item = new cr.ui.MenuItem();
+  item.label = loadTimeData.getString('VIDEO_PLAYER_PLAY_THIS_COMPUTER');
+  item.castLabel = '';
+  item.addEventListener('activate', this.onCastSelected_.wrap(this, null));
+  menu.appendChild(item);
+
+  for (var i = 0; i < casts.length; i++) {
+    var item = new cr.ui.MenuItem();
+    item.label = casts[i].friendlyName;
+    item.castLabel = casts[i].label;
+    item.addEventListener('activate',
+                          this.onCastSelected_.wrap(this, casts[i]));
+    menu.appendChild(item);
+  }
+  this.updateCheckOnCastMenu_();
+  videoPlayerElement.setAttribute('cast-available', true);
+};
+
+/**
+ * Updates the check status of the cast menu items.
+ * @private
+ */
+VideoPlayer.prototype.updateCheckOnCastMenu_ = function() {
+  var menu = document.querySelector('#cast-menu');
+  var menuItems = menu.menuItems;
+  for (var i = 0; i < menuItems.length; i++) {
+    var item = menuItems[i];
+    if (this.currentCast_ === null) {
+      // Playing on this computer.
+      if (item.castLabel === '')
+        item.checked = true;
+      else
+        item.checked = false;
+    } else {
+      // Playing on cast device.
+      if (item.castLabel === this.currentCast_.label)
+        item.checked = true;
+      else
+        item.checked = false;
+    }
+  }
+};
+
+/**
+ * Called when the current cast is disappear from the cast list.
+ * @private
+ */
+VideoPlayer.prototype.onCurrentCastDisappear_ = function() {
+  this.currentCast_ = null;
+  this.currentSession_ = null;
+  this.controls.showErrorMessage('GALLERY_VIDEO_DECODING_ERROR');
+  this.unloadVideo();
 };
 
 /**