Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 /** | 5 /** |
| 6 * @fileoverview MediaControls class implements media playback controls | 6 * @fileoverview MediaControls class implements media playback controls |
| 7 * that exist outside of the audio/video HTML element. | 7 * that exist outside of the audio/video HTML element. |
| 8 */ | 8 */ |
| 9 | 9 |
| 10 /** | 10 /** |
| 11 * @param {HTMLElement} containerElement The container for the controls. | 11 * @param {HTMLElement} containerElement The container for the controls. |
| 12 * @param {function} onMediaError Function to display an error message. | 12 * @param {function} onMediaError Function to display an error message. |
| 13 * @constructor | 13 * @constructor |
| 14 */ | 14 */ |
| 15 function MediaControls(containerElement, onMediaError) { | 15 function MediaControls(containerElement, onMediaError) { |
| 16 this.container_ = containerElement; | 16 this.container_ = containerElement; |
| 17 this.document_ = this.container_.ownerDocument; | 17 this.document_ = this.container_.ownerDocument; |
| 18 this.media_ = null; | 18 this.media_ = null; |
| 19 | 19 |
| 20 this.onMediaPlayBound_ = this.onMediaPlay_.bind(this, true); | 20 this.onMediaPlayBound_ = this.onMediaPlay_.bind(this, true); |
| 21 this.onMediaPauseBound_ = this.onMediaPlay_.bind(this, false); | 21 this.onMediaPauseBound_ = this.onMediaPlay_.bind(this, false); |
| 22 this.onMediaDurationBound_ = this.onMediaDuration_.bind(this); | 22 this.onMediaDurationBound_ = this.onMediaDuration_.bind(this); |
| 23 this.onMediaProgressBound_ = this.onMediaProgress_.bind(this); | 23 this.onMediaProgressBound_ = this.onMediaProgress_.bind(this); |
| 24 this.onMediaError_ = onMediaError || function() {}; | 24 this.onMediaError_ = onMediaError || function() {}; |
| 25 } | 25 } |
| 26 | 26 |
| 27 /** | 27 /** |
| 28 * Button's state types. Values are used as CSS class names. | |
| 29 * @enum {string} | |
| 30 */ | |
| 31 MediaControls.ButtonStateType = { | |
| 32 DEFAULT: 'default', | |
| 33 PLAYING: 'playing', | |
| 34 ENDED: 'ended' | |
| 35 }; | |
| 36 | |
| 37 /** | |
| 28 * @return {HTMLAudioElement|HTMLVideoElement} The media element. | 38 * @return {HTMLAudioElement|HTMLVideoElement} The media element. |
| 29 */ | 39 */ |
| 30 MediaControls.prototype.getMedia = function() { return this.media_ }; | 40 MediaControls.prototype.getMedia = function() { return this.media_ }; |
| 31 | 41 |
| 32 /** | 42 /** |
| 33 * Format the time in hh:mm:ss format (omitting redundant leading zeros). | 43 * Format the time in hh:mm:ss format (omitting redundant leading zeros). |
| 34 * | 44 * |
| 35 * @param {number} timeInSec Time in seconds. | 45 * @param {number} timeInSec Time in seconds. |
| 36 * @return {string} Formatted time string. | 46 * @return {string} Formatted time string. |
| 37 * @private | 47 * @private |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 63 parent.appendChild(control); | 73 parent.appendChild(control); |
| 64 return control; | 74 return control; |
| 65 }; | 75 }; |
| 66 | 76 |
| 67 /** | 77 /** |
| 68 * Create a custom button. | 78 * Create a custom button. |
| 69 * | 79 * |
| 70 * @param {string} className Class name. | 80 * @param {string} className Class name. |
| 71 * @param {function(Event)} handler Click handler. | 81 * @param {function(Event)} handler Click handler. |
| 72 * @param {HTMLElement=} opt_parent Parent element or container if undefined. | 82 * @param {HTMLElement=} opt_parent Parent element or container if undefined. |
| 73 * @param {boolean=} opt_toggle True if the button has toggle state. | 83 * @param {number=} opt_numStates Number of states, default: 1. |
|
yoshiki
2013/03/11 04:42:45
Could you change the type of the last argument to
mtomasz
2013/03/11 04:48:15
I think it would make the code worse. The enum con
yoshiki
2013/03/11 05:17:03
Sorry, I misunderstood. |number=| is ok
| |
| 74 * @return {HTMLElement} The new button element. | 84 * @return {HTMLElement} The new button element. |
| 75 */ | 85 */ |
| 76 MediaControls.prototype.createButton = function( | 86 MediaControls.prototype.createButton = function( |
| 77 className, handler, opt_parent, opt_toggle) { | 87 className, handler, opt_parent, opt_numStates) { |
| 88 opt_numStates = opt_numStates || 1; | |
| 89 | |
| 78 var button = this.createControl(className, opt_parent); | 90 var button = this.createControl(className, opt_parent); |
| 79 button.classList.add('media-button'); | 91 button.classList.add('media-button'); |
| 80 button.addEventListener('click', handler); | 92 button.addEventListener('click', handler); |
| 81 | 93 |
| 82 var numStates = opt_toggle ? 2 : 1; | 94 var stateTypes = Object.keys(MediaControls.ButtonStateType); |
| 83 for (var state = 0; state != numStates; state++) { | 95 for (var state = 0; state != opt_numStates; state++) { |
| 84 var stateClass = 'state' + state; | 96 var stateClass = MediaControls.ButtonStateType[stateTypes[state]]; |
| 85 this.createControl('normal ' + stateClass, button); | 97 this.createControl('normal ' + stateClass, button); |
| 86 this.createControl('hover ' + stateClass, button); | 98 this.createControl('hover ' + stateClass, button); |
| 87 this.createControl('active ' + stateClass, button); | 99 this.createControl('active ' + stateClass, button); |
| 88 } | 100 } |
| 89 this.createControl('disabled', button); | 101 this.createControl('disabled', button); |
| 90 | 102 |
| 91 button.setAttribute('state', 0); | 103 button.setAttribute('state', MediaControls.ButtonStateType.DEFAULT); |
| 92 button.addEventListener('click', handler); | 104 button.addEventListener('click', handler); |
| 93 return button; | 105 return button; |
| 94 }; | 106 }; |
| 95 | 107 |
| 96 /** | 108 /** |
| 97 * Enable/disable controls matching a given selector. | 109 * Enable/disable controls matching a given selector. |
| 98 * | 110 * |
| 99 * @param {string} selector CSS selector. | 111 * @param {string} selector CSS selector. |
| 100 * @param {boolean} on True if enable, false if disable. | 112 * @param {boolean} on True if enable, false if disable. |
| 101 * @private | 113 * @private |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 144 this.pause(); | 156 this.pause(); |
| 145 else | 157 else |
| 146 this.play(); | 158 this.play(); |
| 147 }; | 159 }; |
| 148 | 160 |
| 149 /** | 161 /** |
| 150 * @param {HTMLElement=} opt_parent Parent container. | 162 * @param {HTMLElement=} opt_parent Parent container. |
| 151 */ | 163 */ |
| 152 MediaControls.prototype.initPlayButton = function(opt_parent) { | 164 MediaControls.prototype.initPlayButton = function(opt_parent) { |
| 153 this.playButton_ = this.createButton('play media-control', | 165 this.playButton_ = this.createButton('play media-control', |
| 154 this.togglePlayState.bind(this), opt_parent, true /* toggle */); | 166 this.togglePlayState.bind(this), opt_parent, 3 /* States. */); |
|
yoshiki
2013/03/11 04:42:45
Could you change "3" to MediaControls.ButtonStateT
mtomasz
2013/03/11 04:48:15
It is not out of array. This is not an index, but
yoshiki
2013/03/11 05:17:03
ditto, "3" is ok
| |
| 155 }; | 167 }; |
| 156 | 168 |
| 157 /* | 169 /* |
| 158 * Time controls | 170 * Time controls |
| 159 */ | 171 */ |
| 160 | 172 |
| 161 /** | 173 /** |
| 162 * The default range of 100 is too coarse for the media progress slider. | 174 * The default range of 100 is too coarse for the media progress slider. |
| 163 */ | 175 */ |
| 164 MediaControls.PROGRESS_RANGE = 5000; | 176 MediaControls.PROGRESS_RANGE = 5000; |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 224 if (on) { | 236 if (on) { |
| 225 this.resumeAfterDrag_ = this.isPlaying(); | 237 this.resumeAfterDrag_ = this.isPlaying(); |
| 226 this.media_.pause(); | 238 this.media_.pause(); |
| 227 } else { | 239 } else { |
| 228 if (this.resumeAfterDrag_) { | 240 if (this.resumeAfterDrag_) { |
| 229 if (this.media_.ended) | 241 if (this.media_.ended) |
| 230 this.onMediaPlay_(false); | 242 this.onMediaPlay_(false); |
| 231 else | 243 else |
| 232 this.media_.play(); | 244 this.media_.play(); |
| 233 } | 245 } |
| 246 this.updatePlayButtonState_(this.isPlaying()); | |
| 234 } | 247 } |
| 235 }; | 248 }; |
| 236 | 249 |
| 237 /* | 250 /* |
| 238 * Volume controls | 251 * Volume controls |
| 239 */ | 252 */ |
| 240 | 253 |
| 241 /** | 254 /** |
| 242 * @param {HTMLElement=} opt_parent Parent element for the controls. | 255 * @param {HTMLElement=} opt_parent Parent element for the controls. |
| 243 */ | 256 */ |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 358 | 371 |
| 359 /** | 372 /** |
| 360 * 'play' and 'pause' event handler. | 373 * 'play' and 'pause' event handler. |
| 361 * @param {boolean} playing True if playing. | 374 * @param {boolean} playing True if playing. |
| 362 * @private | 375 * @private |
| 363 */ | 376 */ |
| 364 MediaControls.prototype.onMediaPlay_ = function(playing) { | 377 MediaControls.prototype.onMediaPlay_ = function(playing) { |
| 365 if (this.progressSlider_.isDragging()) | 378 if (this.progressSlider_.isDragging()) |
| 366 return; | 379 return; |
| 367 | 380 |
| 368 this.playButton_.setAttribute('state', playing ? '1' : '0'); | 381 this.updatePlayButtonState_(playing); |
| 369 this.onPlayStateChanged(); | 382 this.onPlayStateChanged(); |
| 370 }; | 383 }; |
| 371 | 384 |
| 372 /** | 385 /** |
| 373 * 'durationchange' event handler. | 386 * 'durationchange' event handler. |
| 374 * @private | 387 * @private |
| 375 */ | 388 */ |
| 376 MediaControls.prototype.onMediaDuration_ = function() { | 389 MediaControls.prototype.onMediaDuration_ = function() { |
| 377 if (!this.media_.duration) { | 390 if (!this.media_.duration) { |
| 378 this.enableControls_('.media-control', false); | 391 this.enableControls_('.media-control', false); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 429 */ | 442 */ |
| 430 MediaControls.prototype.onMediaComplete = function() {}; | 443 MediaControls.prototype.onMediaComplete = function() {}; |
| 431 | 444 |
| 432 /** | 445 /** |
| 433 * Called when play/pause state is changed or on playback progress. | 446 * Called when play/pause state is changed or on playback progress. |
| 434 * This is the right moment to save the play state. | 447 * This is the right moment to save the play state. |
| 435 */ | 448 */ |
| 436 MediaControls.prototype.onPlayStateChanged = function() {}; | 449 MediaControls.prototype.onPlayStateChanged = function() {}; |
| 437 | 450 |
| 438 /** | 451 /** |
| 452 * Updates the play button state. | |
| 453 * @param {boolean} playing If the video is playing. | |
| 454 * @private | |
| 455 */ | |
| 456 MediaControls.prototype.updatePlayButtonState_ = function(playing) { | |
| 457 if (playing) { | |
| 458 this.playButton_.setAttribute('state', | |
| 459 MediaControls.ButtonStateType.PLAYING); | |
| 460 } else if (!this.media_.ended) { | |
| 461 this.playButton_.setAttribute('state', | |
| 462 MediaControls.ButtonStateType.DEFAULT); | |
| 463 } else { | |
| 464 this.playButton_.setAttribute('state', | |
| 465 MediaControls.ButtonStateType.ENDED); | |
| 466 } | |
| 467 }; | |
| 468 | |
| 469 /** | |
| 439 * Restore play state. Base implementation is empty. | 470 * Restore play state. Base implementation is empty. |
| 440 */ | 471 */ |
| 441 MediaControls.prototype.restorePlayState = function() {}; | 472 MediaControls.prototype.restorePlayState = function() {}; |
| 442 | 473 |
| 443 /** | 474 /** |
| 444 * Encode current state into the page URL or the app state. | 475 * Encode current state into the page URL or the app state. |
| 445 */ | 476 */ |
| 446 MediaControls.prototype.encodeState = function() { | 477 MediaControls.prototype.encodeState = function() { |
| 447 if (!this.media_.duration) | 478 if (!this.media_.duration) |
| 448 return; | 479 return; |
| (...skipping 683 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1132 AudioControls.prototype.onAdvanceClick_ = function(forward) { | 1163 AudioControls.prototype.onAdvanceClick_ = function(forward) { |
| 1133 if (!forward && | 1164 if (!forward && |
| 1134 (this.getMedia().currentTime > AudioControls.TRACK_RESTART_THRESHOLD)) { | 1165 (this.getMedia().currentTime > AudioControls.TRACK_RESTART_THRESHOLD)) { |
| 1135 // We are far enough from the beginning of the current track. | 1166 // We are far enough from the beginning of the current track. |
| 1136 // Restart it instead of than skipping to the previous one. | 1167 // Restart it instead of than skipping to the previous one. |
| 1137 this.getMedia().currentTime = 0; | 1168 this.getMedia().currentTime = 0; |
| 1138 } else { | 1169 } else { |
| 1139 this.advanceTrack_(forward); | 1170 this.advanceTrack_(forward); |
| 1140 } | 1171 } |
| 1141 }; | 1172 }; |
| OLD | NEW |