Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(708)

Side by Side Diff: chrome/browser/resources/file_manager/js/media/media_controls.js

Issue 17153007: Enable looping of videos in Files.app. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 'use strict'; 5 'use strict';
6 6
7 /** 7 /**
8 * @fileoverview MediaControls class implements media playback controls 8 * @fileoverview MediaControls class implements media playback controls
9 * that exist outside of the audio/video HTML element. 9 * that exist outside of the audio/video HTML element.
10 */ 10 */
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after
158 this.pause(); 158 this.pause();
159 else 159 else
160 this.play(); 160 this.play();
161 }; 161 };
162 162
163 /** 163 /**
164 * @param {HTMLElement=} opt_parent Parent container. 164 * @param {HTMLElement=} opt_parent Parent container.
165 */ 165 */
166 MediaControls.prototype.initPlayButton = function(opt_parent) { 166 MediaControls.prototype.initPlayButton = function(opt_parent) {
167 this.playButton_ = this.createButton('play media-control', 167 this.playButton_ = this.createButton('play media-control',
168 this.togglePlayState.bind(this), opt_parent, 3 /* States. */); 168 this.onPlayButtonClicked.bind(this), opt_parent, 3 /* States. */);
169 }; 169 };
170 170
171 /* 171 /*
172 * Time controls 172 * Time controls
173 */ 173 */
174 174
175 /** 175 /**
176 * The default range of 100 is too coarse for the media progress slider. 176 * The default range of 100 is too coarse for the media progress slider.
177 */ 177 */
178 MediaControls.PROGRESS_RANGE = 5000; 178 MediaControls.PROGRESS_RANGE = 5000;
(...skipping 775 matching lines...) Expand 10 before | Expand all | Expand 10 after
954 this.getValue(), MediaControls.PreciseSlider.HIDE_AFTER_DRAG_DELAY); 954 this.getValue(), MediaControls.PreciseSlider.HIDE_AFTER_DRAG_DELAY);
955 this.latestMouseUpTime_ = Date.now(); 955 this.latestMouseUpTime_ = Date.now();
956 } 956 }
957 }; 957 };
958 958
959 /** 959 /**
960 * Create video controls. 960 * Create video controls.
961 * 961 *
962 * @param {HTMLElement} containerElement The container for the controls. 962 * @param {HTMLElement} containerElement The container for the controls.
963 * @param {function} onMediaError Function to display an error message. 963 * @param {function} onMediaError Function to display an error message.
964 * @param {function(string):string} stringFunction Function providing localized
965 * strings.
964 * @param {function=} opt_fullScreenToggle Function to toggle fullscreen mode. 966 * @param {function=} opt_fullScreenToggle Function to toggle fullscreen mode.
965 * @param {HTMLElement=} opt_stateIconParent The parent for the icon that 967 * @param {HTMLElement=} opt_stateIconParent The parent for the icon that
966 * gives visual feedback when the playback state changes. 968 * gives visual feedback when the playback state changes.
967 * @constructor 969 * @constructor
968 */ 970 */
969 function VideoControls(containerElement, onMediaError, 971 function VideoControls(containerElement, onMediaError, stringFunction,
970 opt_fullScreenToggle, opt_stateIconParent) { 972 opt_fullScreenToggle, opt_stateIconParent) {
971 MediaControls.call(this, containerElement, onMediaError); 973 MediaControls.call(this, containerElement, onMediaError);
974 this.stringFunction_ = stringFunction;
972 975
973 this.container_.classList.add('video-controls'); 976 this.container_.classList.add('video-controls');
974
975 this.initPlayButton(); 977 this.initPlayButton();
976
977 this.initTimeControls(true /* show seek mark */); 978 this.initTimeControls(true /* show seek mark */);
978
979 this.initVolumeControls(); 979 this.initVolumeControls();
980 980
981 if (opt_fullScreenToggle) { 981 if (opt_fullScreenToggle) {
982 this.fullscreenButton_ = 982 this.fullscreenButton_ =
983 this.createButton('fullscreen', opt_fullScreenToggle); 983 this.createButton('fullscreen', opt_fullScreenToggle);
984 } 984 }
985 985
986 if (opt_stateIconParent) { 986 if (opt_stateIconParent) {
987 this.stateIcon_ = this.createControl( 987 this.stateIcon_ = this.createControl(
988 'playback-state-icon', opt_stateIconParent); 988 'playback-state-icon', opt_stateIconParent);
989 this.textBanner_ = this.createControl('text-banner', opt_stateIconParent);
989 } 990 }
990 991
991 var videoControls = this; 992 var videoControls = this;
992 chrome.mediaPlayerPrivate.onTogglePlayState.addListener( 993 chrome.mediaPlayerPrivate.onTogglePlayState.addListener(
993 function() { videoControls.togglePlayStateWithFeedback(); }); 994 function() { videoControls.togglePlayStateWithFeedback(); });
994 } 995 }
995 996
996 /** 997 /**
997 * No resume if we are withing this margin from the start or the end. 998 * No resume if we are withing this margin from the start or the end.
998 */ 999 */
999 VideoControls.RESUME_MARGIN = 0.03; 1000 VideoControls.RESUME_MARGIN = 0.03;
1000 1001
1001 /** 1002 /**
1002 * No resume for videos shorter than this. 1003 * No resume for videos shorter than this.
1003 */ 1004 */
1004 VideoControls.RESUME_THRESHOLD = 5 * 60; // 5 min. 1005 VideoControls.RESUME_THRESHOLD = 5 * 60; // 5 min.
1005 1006
1006 /** 1007 /**
1007 * When resuming rewind back this much. 1008 * When resuming rewind back this much.
1008 */ 1009 */
1009 VideoControls.RESUME_REWIND = 5; // seconds. 1010 VideoControls.RESUME_REWIND = 5; // seconds.
1010 1011
1011 VideoControls.prototype = { __proto__: MediaControls.prototype }; 1012 VideoControls.prototype = { __proto__: MediaControls.prototype };
1012 1013
1013 /** 1014 /**
1015 * Shows icon feedback for the current state of the video player.
1016 * @private
1017 */
1018 VideoControls.prototype.showIconFeedback_ = function() {
1019 this.stateIcon_.removeAttribute('state');
1020 setTimeout(function() {
1021 this.stateIcon_.setAttribute('state', this.isPlaying() ? 'play' : 'pause');
1022 }.bind(this), 0);
1023 };
1024
1025 /**
1026 * Shows a text banner.
1027 *
1028 * @param {string} identifier String identifier.
1029 * @private
1030 */
1031 VideoControls.prototype.showTextBanner_ = function(identifier) {
1032 this.textBanner_.removeAttribute('visible');
1033 this.textBanner_.textContent = this.stringFunction_(identifier);
1034 setTimeout(function() {
1035 this.textBanner_.setAttribute('visible', 'true');
1036 }.bind(this), 0);
1037 };
1038
1039 /**
1040 * Toggle play/pause state on a mouse click on the play/pause button. Can be
1041 * called externally.
1042 *
1043 * @param {Event} event Mouse click event.
1044 */
1045 VideoControls.prototype.onPlayButtonClicked = function(event) {
1046 if (event.ctrlKey) {
1047 this.toggleLoopedModeWithFeedback(true);
1048 if (!this.isPlaying())
1049 this.togglePlayState();
1050 } else {
1051 this.togglePlayState();
1052 }
1053 };
1054
1055 /**
1014 * Media completion handler. 1056 * Media completion handler.
1015 */ 1057 */
1016 VideoControls.prototype.onMediaComplete = function() { 1058 VideoControls.prototype.onMediaComplete = function() {
1017 this.onMediaPlay_(false); // Just update the UI. 1059 this.onMediaPlay_(false); // Just update the UI.
1018 this.savePosition(); // This will effectively forget the position. 1060 this.savePosition(); // This will effectively forget the position.
1019 }; 1061 };
1020 1062
1021 /** 1063 /**
1022 * Toggle play/pause state and flash an icon over the video. 1064 * Toggles the looped mode with feedback.
1065 * @param {boolean} on Whether enabled or not.
1066 */
1067 VideoControls.prototype.toggleLoopedModeWithFeedback = function(on) {
1068 if (!this.getMedia().duration)
1069 return;
1070
1071 this.toggleLoopedMode(on);
1072 if (on)
1073 this.showTextBanner_('GALLERY_VIDEO_LOOPED_MODE');
1074 };
1075
1076 /**
1077 * Toggles the looped mode.
1078 * @param {boolean} on Whether enabled or not.
1079 */
1080 VideoControls.prototype.toggleLoopedMode = function(on) {
1081 this.getMedia().loop = on;
1082 };
1083
1084 /**
1085 * Toggles play/pause state and flash an icon over the video.
1023 */ 1086 */
1024 VideoControls.prototype.togglePlayStateWithFeedback = function() { 1087 VideoControls.prototype.togglePlayStateWithFeedback = function() {
1025 if (!this.getMedia().duration) 1088 if (!this.getMedia().duration)
1026 return; 1089 return;
1027 1090
1028 this.togglePlayState(); 1091 this.togglePlayState();
1029 1092 this.showIconFeedback_();
1030 this.stateIcon_.removeAttribute('state');
1031 setTimeout(function() {
1032 this.stateIcon_.setAttribute('state', this.isPlaying() ? 'play' : 'pause');
1033 }.bind(this), 0);
1034 }; 1093 };
1035 1094
1036 /** 1095 /**
1037 * Toggle play/pause state. 1096 * Toggles play/pause state.
1038 */ 1097 */
1039 VideoControls.prototype.togglePlayState = function() { 1098 VideoControls.prototype.togglePlayState = function() {
1040 if (this.isPlaying()) { 1099 if (this.isPlaying()) {
1041 // User gave the Pause command. 1100 // User gave the Pause command. Save the state and reset the loop mode.
1101 this.toggleLoopedMode(false);
1042 this.savePosition(); 1102 this.savePosition();
1043 } 1103 }
1044 MediaControls.prototype.togglePlayState.apply(this, arguments); 1104 MediaControls.prototype.togglePlayState.apply(this, arguments);
1045 }; 1105 };
1046 1106
1047 /** 1107 /**
1048 * Save the playback position to the persistent storage. 1108 * Saves the playback position to the persistent storage.
1049 * @param {boolean=} opt_sync True if the position must be saved synchronously 1109 * @param {boolean=} opt_sync True if the position must be saved synchronously
1050 * (required when closing app windows). 1110 * (required when closing app windows).
1051 */ 1111 */
1052 VideoControls.prototype.savePosition = function(opt_sync) { 1112 VideoControls.prototype.savePosition = function(opt_sync) {
1053 if (!this.media_.duration || 1113 if (!this.media_.duration ||
1054 this.media_.duration < VideoControls.RESUME_THRESHOLD) { 1114 this.media_.duration < VideoControls.RESUME_THRESHOLD) {
1055 return; 1115 return;
1056 } 1116 }
1057 1117
1058 var ratio = this.media_.currentTime / this.media_.duration; 1118 var ratio = this.media_.currentTime / this.media_.duration;
(...skipping 13 matching lines...) Expand all
1072 // Pass the data to the background page. 1132 // Pass the data to the background page.
1073 if (!window.saveOnExit) 1133 if (!window.saveOnExit)
1074 window.saveOnExit = []; 1134 window.saveOnExit = [];
1075 window.saveOnExit.push({ key: this.media_.src, value: position }); 1135 window.saveOnExit.push({ key: this.media_.src, value: position });
1076 } else { 1136 } else {
1077 util.AppCache.update(this.media_.src, position); 1137 util.AppCache.update(this.media_.src, position);
1078 } 1138 }
1079 }; 1139 };
1080 1140
1081 /** 1141 /**
1082 * Resume the playback position saved in the persistent storage. 1142 * Resumes the playback position saved in the persistent storage.
1083 */ 1143 */
1084 VideoControls.prototype.restorePlayState = function() { 1144 VideoControls.prototype.restorePlayState = function() {
1085 if (this.media_.duration >= VideoControls.RESUME_THRESHOLD) { 1145 if (this.media_.duration >= VideoControls.RESUME_THRESHOLD) {
1086 util.AppCache.getValue(this.media_.src, function(position) { 1146 util.AppCache.getValue(this.media_.src, function(position) {
1087 if (position) 1147 if (position)
1088 this.media_.currentTime = position; 1148 this.media_.currentTime = position;
1089 }.bind(this)); 1149 }.bind(this));
1090 } 1150 }
1091 }; 1151 };
1092 1152
1093 /** 1153 /**
1094 * Update style to best fit the size of the container. 1154 * Updates style to best fit the size of the container.
1095 */ 1155 */
1096 VideoControls.prototype.updateStyle = function() { 1156 VideoControls.prototype.updateStyle = function() {
1097 // We assume that the video controls element fills the parent container. 1157 // We assume that the video controls element fills the parent container.
1098 // This is easier than adding margins to this.container_.clientWidth. 1158 // This is easier than adding margins to this.container_.clientWidth.
1099 var width = this.container_.parentNode.clientWidth; 1159 var width = this.container_.parentNode.clientWidth;
1100 1160
1101 // Set the margin to 5px for width >= 400, 0px for width < 160, 1161 // Set the margin to 5px for width >= 400, 0px for width < 160,
1102 // interpolate linearly in between. 1162 // interpolate linearly in between.
1103 this.container_.style.margin = 1163 this.container_.style.margin =
1104 Math.ceil((Math.max(160, Math.min(width, 400)) - 160) / 48) + 'px'; 1164 Math.ceil((Math.max(160, Math.min(width, 400)) - 160) / 48) + 'px';
1105 1165
1106 var hideBelow = function(selector, limit) { 1166 var hideBelow = function(selector, limit) {
1107 this.container_.querySelector(selector).style.display = 1167 this.container_.querySelector(selector).style.display =
1108 width < limit ? 'none' : '-webkit-box'; 1168 width < limit ? 'none' : '-webkit-box';
1109 }.bind(this); 1169 }.bind(this);
1110 1170
1111 hideBelow('.time', 350); 1171 hideBelow('.time', 350);
1112 hideBelow('.volume', 275); 1172 hideBelow('.volume', 275);
1113 hideBelow('.volume-controls', 210); 1173 hideBelow('.volume-controls', 210);
1114 hideBelow('.fullscreen', 150); 1174 hideBelow('.fullscreen', 150);
1115 }; 1175 };
1116 1176
1117 /** 1177 /**
1118 * Create audio controls. 1178 * Creates audio controls.
1119 * 1179 *
1120 * @param {HTMLElement} container Parent container. 1180 * @param {HTMLElement} container Parent container.
1121 * @param {function(boolean)} advanceTrack Parameter: true=forward. 1181 * @param {function(boolean)} advanceTrack Parameter: true=forward.
1122 * @param {function} onError Error handler. 1182 * @param {function} onError Error handler.
1123 * @constructor 1183 * @constructor
1124 */ 1184 */
1125 function AudioControls(container, advanceTrack, onError) { 1185 function AudioControls(container, advanceTrack, onError) {
1126 MediaControls.call(this, container, onError); 1186 MediaControls.call(this, container, onError);
1127 1187
1128 this.container_.classList.add('audio-controls'); 1188 this.container_.classList.add('audio-controls');
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
1165 AudioControls.prototype.onAdvanceClick_ = function(forward) { 1225 AudioControls.prototype.onAdvanceClick_ = function(forward) {
1166 if (!forward && 1226 if (!forward &&
1167 (this.getMedia().currentTime > AudioControls.TRACK_RESTART_THRESHOLD)) { 1227 (this.getMedia().currentTime > AudioControls.TRACK_RESTART_THRESHOLD)) {
1168 // We are far enough from the beginning of the current track. 1228 // We are far enough from the beginning of the current track.
1169 // Restart it instead of than skipping to the previous one. 1229 // Restart it instead of than skipping to the previous one.
1170 this.getMedia().currentTime = 0; 1230 this.getMedia().currentTime = 0;
1171 } else { 1231 } else {
1172 this.advanceTrack_(forward); 1232 this.advanceTrack_(forward);
1173 } 1233 }
1174 }; 1234 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698