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. |
|
hirono
2014/12/24 01:43:22
nit: !HTMLElement
yawano
2014/12/24 02:39:55
Done.
| |
| 12 * @param {function} onMediaError Function to display an error message. | 12 * @param {function(Event)} onMediaError Function to display an error message. |
| 13 * @constructor | 13 * @constructor |
| 14 * @struct | |
| 14 */ | 15 */ |
| 15 function MediaControls(containerElement, onMediaError) { | 16 function MediaControls(containerElement, onMediaError) { |
| 16 this.container_ = containerElement; | 17 this.container_ = containerElement; |
| 17 this.document_ = this.container_.ownerDocument; | 18 this.document_ = this.container_.ownerDocument; |
| 18 this.media_ = null; | 19 this.media_ = null; |
| 19 | 20 |
| 20 this.onMediaPlayBound_ = this.onMediaPlay_.bind(this, true); | 21 this.onMediaPlayBound_ = this.onMediaPlay_.bind(this, true); |
| 21 this.onMediaPauseBound_ = this.onMediaPlay_.bind(this, false); | 22 this.onMediaPauseBound_ = this.onMediaPlay_.bind(this, false); |
| 22 this.onMediaDurationBound_ = this.onMediaDuration_.bind(this); | 23 this.onMediaDurationBound_ = this.onMediaDuration_.bind(this); |
| 23 this.onMediaProgressBound_ = this.onMediaProgress_.bind(this); | 24 this.onMediaProgressBound_ = this.onMediaProgress_.bind(this); |
| 24 this.onMediaError_ = onMediaError || function() {}; | 25 this.onMediaError_ = onMediaError || function() {}; |
| 25 | 26 |
| 26 this.savedVolume_ = 1; // 100% volume. | 27 this.savedVolume_ = 1; // 100% volume. |
| 28 | |
| 29 /** | |
| 30 * @type {HTMLElement} | |
| 31 * @private | |
| 32 */ | |
| 33 this.playButton_ = null; | |
| 34 | |
| 35 /** | |
| 36 * @type {MediaControls.Slider} | |
| 37 * @private | |
| 38 */ | |
| 39 this.progressSlider_ = null; | |
| 40 | |
| 41 /** | |
| 42 * @type {HTMLElement} | |
| 43 * @private | |
| 44 */ | |
| 45 this.duration_ = null; | |
| 46 | |
| 47 /** | |
| 48 * @type {MediaControls.AnimatedSlider} | |
| 49 * @private | |
| 50 */ | |
| 51 this.volume_ = null; | |
| 52 | |
| 53 /** | |
| 54 * @type {HTMLElement} | |
| 55 * @private | |
| 56 */ | |
| 57 this.textBanner_ = null; | |
| 58 | |
| 59 /** | |
| 60 * @type {HTMLElement} | |
| 61 * @private | |
| 62 */ | |
| 63 this.soundButton_ = null; | |
| 64 | |
| 65 /** | |
| 66 * @type {boolean} | |
| 67 * @private | |
| 68 */ | |
| 69 this.resumeAfterDrag_ = false; | |
| 70 | |
| 71 /** | |
| 72 * @type {HTMLElement} | |
| 73 * @private | |
| 74 */ | |
| 75 this.currentTime_ = null; | |
| 27 } | 76 } |
| 28 | 77 |
| 29 /** | 78 /** |
| 30 * Button's state types. Values are used as CSS class names. | 79 * Button's state types. Values are used as CSS class names. |
| 31 * @enum {string} | 80 * @enum {string} |
| 32 */ | 81 */ |
| 33 MediaControls.ButtonStateType = { | 82 MediaControls.ButtonStateType = { |
| 34 DEFAULT: 'default', | 83 DEFAULT: 'default', |
| 35 PLAYING: 'playing', | 84 PLAYING: 'playing', |
| 36 ENDED: 'ended' | 85 ENDED: 'ended' |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 59 if (seconds < 10) result += '0'; | 108 if (seconds < 10) result += '0'; |
| 60 result += seconds; | 109 result += seconds; |
| 61 return result; | 110 return result; |
| 62 }; | 111 }; |
| 63 | 112 |
| 64 /** | 113 /** |
| 65 * Create a custom control. | 114 * Create a custom control. |
| 66 * | 115 * |
| 67 * @param {string} className Class name. | 116 * @param {string} className Class name. |
| 68 * @param {HTMLElement=} opt_parent Parent element or container if undefined. | 117 * @param {HTMLElement=} opt_parent Parent element or container if undefined. |
| 69 * @return {HTMLElement} The new control element. | 118 * @return {!HTMLElement} The new control element. |
| 70 */ | 119 */ |
| 71 MediaControls.prototype.createControl = function(className, opt_parent) { | 120 MediaControls.prototype.createControl = function(className, opt_parent) { |
| 72 var parent = opt_parent || this.container_; | 121 var parent = opt_parent || this.container_; |
| 73 var control = this.document_.createElement('div'); | 122 var control = assertInstanceof(this.document_.createElement('div'), |
| 123 HTMLDivElement); | |
| 74 control.className = className; | 124 control.className = className; |
| 75 parent.appendChild(control); | 125 parent.appendChild(control); |
| 76 return control; | 126 return control; |
| 77 }; | 127 }; |
| 78 | 128 |
| 79 /** | 129 /** |
| 80 * Create a custom button. | 130 * Create a custom button. |
| 81 * | 131 * |
| 82 * @param {string} className Class name. | 132 * @param {string} className Class name. |
| 83 * @param {function(Event)} handler Click handler. | 133 * @param {function(Event)=} opt_handler Click handler. |
| 84 * @param {HTMLElement=} opt_parent Parent element or container if undefined. | 134 * @param {HTMLElement=} opt_parent Parent element or container if undefined. |
| 85 * @param {number=} opt_numStates Number of states, default: 1. | 135 * @param {number=} opt_numStates Number of states, default: 1. |
| 86 * @return {HTMLElement} The new button element. | 136 * @return {!HTMLElement} The new button element. |
| 87 */ | 137 */ |
| 88 MediaControls.prototype.createButton = function( | 138 MediaControls.prototype.createButton = function( |
| 89 className, handler, opt_parent, opt_numStates) { | 139 className, opt_handler, opt_parent, opt_numStates) { |
| 90 opt_numStates = opt_numStates || 1; | 140 opt_numStates = opt_numStates || 1; |
| 91 | 141 |
| 92 var button = this.createControl(className, opt_parent); | 142 var button = this.createControl(className, opt_parent); |
| 93 button.classList.add('media-button'); | 143 button.classList.add('media-button'); |
| 94 button.addEventListener('click', handler); | |
| 95 | 144 |
| 96 var stateTypes = Object.keys(MediaControls.ButtonStateType); | 145 var stateTypes = Object.keys(MediaControls.ButtonStateType); |
| 97 for (var state = 0; state != opt_numStates; state++) { | 146 for (var state = 0; state != opt_numStates; state++) { |
| 98 var stateClass = MediaControls.ButtonStateType[stateTypes[state]]; | 147 var stateClass = MediaControls.ButtonStateType[stateTypes[state]]; |
| 99 this.createControl('normal ' + stateClass, button); | 148 this.createControl('normal ' + stateClass, button); |
| 100 this.createControl('hover ' + stateClass, button); | 149 this.createControl('hover ' + stateClass, button); |
| 101 this.createControl('active ' + stateClass, button); | 150 this.createControl('active ' + stateClass, button); |
| 102 } | 151 } |
| 103 this.createControl('disabled', button); | 152 this.createControl('disabled', button); |
| 104 | 153 |
| 105 button.setAttribute('state', MediaControls.ButtonStateType.DEFAULT); | 154 button.setAttribute('state', MediaControls.ButtonStateType.DEFAULT); |
| 106 button.addEventListener('click', handler); | 155 |
| 156 if (opt_handler) | |
| 157 button.addEventListener('click', opt_handler); | |
| 158 | |
| 107 return button; | 159 return button; |
| 108 }; | 160 }; |
| 109 | 161 |
| 110 /** | 162 /** |
| 111 * Enable/disable controls matching a given selector. | 163 * Enable/disable controls matching a given selector. |
| 112 * | 164 * |
| 113 * @param {string} selector CSS selector. | 165 * @param {string} selector CSS selector. |
| 114 * @param {boolean} on True if enable, false if disable. | 166 * @param {boolean} on True if enable, false if disable. |
| 115 * @private | 167 * @private |
| 116 */ | 168 */ |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 146 if (!this.media_) | 198 if (!this.media_) |
| 147 return; // Media is detached. | 199 return; // Media is detached. |
| 148 | 200 |
| 149 this.media_.pause(); | 201 this.media_.pause(); |
| 150 }; | 202 }; |
| 151 | 203 |
| 152 /** | 204 /** |
| 153 * @return {boolean} True if the media is currently playing. | 205 * @return {boolean} True if the media is currently playing. |
| 154 */ | 206 */ |
| 155 MediaControls.prototype.isPlaying = function() { | 207 MediaControls.prototype.isPlaying = function() { |
| 156 return this.media_ && !this.media_.paused && !this.media_.ended; | 208 return !!this.media_ && !this.media_.paused && !this.media_.ended; |
| 157 }; | 209 }; |
| 158 | 210 |
| 159 /** | 211 /** |
| 160 * Toggle play/pause. | 212 * Toggle play/pause. |
| 161 */ | 213 */ |
| 162 MediaControls.prototype.togglePlayState = function() { | 214 MediaControls.prototype.togglePlayState = function() { |
| 163 if (this.isPlaying()) | 215 if (this.isPlaying()) |
| 164 this.pause(); | 216 this.pause(); |
| 165 else | 217 else |
| 166 this.play(); | 218 this.play(); |
| 167 }; | 219 }; |
| 168 | 220 |
| 169 /** | 221 /** |
| 170 * Toggle play/pause state on a mouse click on the play/pause button. Can be | 222 * Toggle play/pause state on a mouse click on the play/pause button. |
|
hirono
2014/12/24 01:43:22
nit: Toggle -> Toggles
yawano
2014/12/24 02:39:55
Done.
| |
| 171 * called externally. TODO(mtomasz): Remove it. http://www.crbug.com/254318. | |
| 172 * | 223 * |
| 173 * @param {Event=} opt_event Mouse click event. | 224 * @param {Event} event Mouse click event. |
| 174 */ | 225 */ |
| 175 MediaControls.prototype.onPlayButtonClicked = function(opt_event) { | 226 MediaControls.prototype.onPlayButtonClicked = function(event) { |
| 176 this.togglePlayState(); | 227 this.togglePlayState(); |
| 177 }; | 228 }; |
| 178 | 229 |
| 179 /** | 230 /** |
| 180 * @param {HTMLElement=} opt_parent Parent container. | 231 * @param {HTMLElement=} opt_parent Parent container. |
| 181 */ | 232 */ |
| 182 MediaControls.prototype.initPlayButton = function(opt_parent) { | 233 MediaControls.prototype.initPlayButton = function(opt_parent) { |
| 183 this.playButton_ = this.createButton('play media-control', | 234 this.playButton_ = this.createButton('play media-control', |
| 184 this.onPlayButtonClicked.bind(this), opt_parent, 3 /* States. */); | 235 this.onPlayButtonClicked.bind(this), opt_parent, 3 /* States. */); |
| 185 }; | 236 }; |
| (...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 434 else | 485 else |
| 435 sliderContainer.classList.add('readonly'); | 486 sliderContainer.classList.add('readonly'); |
| 436 | 487 |
| 437 var valueToString = function(value) { | 488 var valueToString = function(value) { |
| 438 var duration = this.media_ ? this.media_.duration : 0; | 489 var duration = this.media_ ? this.media_.duration : 0; |
| 439 return MediaControls.formatTime_(this.media_.duration * value); | 490 return MediaControls.formatTime_(this.media_.duration * value); |
| 440 }.bind(this); | 491 }.bind(this); |
| 441 | 492 |
| 442 this.duration_.textContent = valueToString(1); | 493 this.duration_.textContent = valueToString(1); |
| 443 | 494 |
| 444 if (this.progressSlider_.setValueToStringFunction) | 495 this.progressSlider_.setValueToStringFunction(valueToString); |
| 445 this.progressSlider_.setValueToStringFunction(valueToString); | |
| 446 | 496 |
| 447 if (this.media_.seekable) | 497 if (this.media_.seekable) |
| 448 this.restorePlayState(); | 498 this.restorePlayState(); |
| 449 }; | 499 }; |
| 450 | 500 |
| 451 /** | 501 /** |
| 452 * 'timeupdate' event handler. | 502 * 'timeupdate' event handler. |
| 453 * @private | 503 * @private |
| 454 */ | 504 */ |
| 455 MediaControls.prototype.onMediaProgress_ = function() { | 505 MediaControls.prototype.onMediaProgress_ = function() { |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 549 | 599 |
| 550 /** | 600 /** |
| 551 * Create a customized slider control. | 601 * Create a customized slider control. |
| 552 * | 602 * |
| 553 * @param {HTMLElement} container The containing div element. | 603 * @param {HTMLElement} container The containing div element. |
| 554 * @param {number} value Initial value [0..1]. | 604 * @param {number} value Initial value [0..1]. |
| 555 * @param {number} range Number of distinct slider positions to be supported. | 605 * @param {number} range Number of distinct slider positions to be supported. |
| 556 * @param {function(number)} onChange Value change handler. | 606 * @param {function(number)} onChange Value change handler. |
| 557 * @param {function(boolean)} onDrag Drag begin/end handler. | 607 * @param {function(boolean)} onDrag Drag begin/end handler. |
| 558 * @constructor | 608 * @constructor |
| 609 * @struct | |
| 559 */ | 610 */ |
| 560 | 611 |
| 561 MediaControls.Slider = function(container, value, range, onChange, onDrag) { | 612 MediaControls.Slider = function(container, value, range, onChange, onDrag) { |
| 562 this.container_ = container; | 613 this.container_ = container; |
| 563 this.onChange_ = onChange; | 614 this.onChange_ = onChange; |
| 564 this.onDrag_ = onDrag; | 615 this.onDrag_ = onDrag; |
| 565 | 616 |
| 617 /** | |
| 618 * @type {boolean} | |
| 619 * @private | |
| 620 */ | |
| 621 this.isDragging_ = false; | |
| 622 | |
| 566 var document = this.container_.ownerDocument; | 623 var document = this.container_.ownerDocument; |
| 567 | 624 |
| 568 this.container_.classList.add('custom-slider'); | 625 this.container_.classList.add('custom-slider'); |
| 569 | 626 |
| 570 this.input_ = document.createElement('input'); | 627 this.input_ = assertInstanceof(document.createElement('input'), |
| 628 HTMLInputElement); | |
| 571 this.input_.type = 'range'; | 629 this.input_.type = 'range'; |
| 572 this.input_.min = 0; | 630 this.input_.min = (0).toString(); |
| 573 this.input_.max = range; | 631 this.input_.max = range.toString(); |
| 574 this.input_.value = value * range; | 632 this.input_.value = (value * range).toString(); |
| 575 this.container_.appendChild(this.input_); | 633 this.container_.appendChild(this.input_); |
| 576 | 634 |
| 577 this.input_.addEventListener( | 635 this.input_.addEventListener( |
| 578 'change', this.onInputChange_.bind(this)); | 636 'change', this.onInputChange_.bind(this)); |
| 579 this.input_.addEventListener( | 637 this.input_.addEventListener( |
| 580 'mousedown', this.onInputDrag_.bind(this, true)); | 638 'mousedown', this.onInputDrag_.bind(this, true)); |
| 581 this.input_.addEventListener( | 639 this.input_.addEventListener( |
| 582 'mouseup', this.onInputDrag_.bind(this, false)); | 640 'mouseup', this.onInputDrag_.bind(this, false)); |
| 583 | 641 |
| 584 this.bar_ = document.createElement('div'); | 642 this.bar_ = assertInstanceof(document.createElement('div'), HTMLDivElement); |
| 585 this.bar_.className = 'bar'; | 643 this.bar_.className = 'bar'; |
| 586 this.container_.appendChild(this.bar_); | 644 this.container_.appendChild(this.bar_); |
| 587 | 645 |
| 588 this.filled_ = document.createElement('div'); | 646 this.filled_ = document.createElement('div'); |
| 589 this.filled_.className = 'filled'; | 647 this.filled_.className = 'filled'; |
| 590 this.bar_.appendChild(this.filled_); | 648 this.bar_.appendChild(this.filled_); |
| 591 | 649 |
| 592 var leftCap = document.createElement('div'); | 650 var leftCap = document.createElement('div'); |
| 593 leftCap.className = 'cap left'; | 651 leftCap.className = 'cap left'; |
| 594 this.bar_.appendChild(leftCap); | 652 this.bar_.appendChild(leftCap); |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 658 return this.input_.value / this.input_.max; | 716 return this.input_.value / this.input_.max; |
| 659 }; | 717 }; |
| 660 | 718 |
| 661 /** | 719 /** |
| 662 * Update the UI with the current value. | 720 * Update the UI with the current value. |
| 663 * | 721 * |
| 664 * @param {number} value [0..1]. | 722 * @param {number} value [0..1]. |
| 665 * @private | 723 * @private |
| 666 */ | 724 */ |
| 667 MediaControls.Slider.prototype.setValueToUI_ = function(value) { | 725 MediaControls.Slider.prototype.setValueToUI_ = function(value) { |
| 668 this.input_.value = value * this.input_.max; | 726 this.input_.value = (value * this.input_.max).toString(); |
| 669 this.setFilled_(value); | 727 this.setFilled_(value); |
| 670 }; | 728 }; |
| 671 | 729 |
| 672 /** | 730 /** |
| 673 * Compute the proportion in which the given position divides the slider bar. | 731 * Compute the proportion in which the given position divides the slider bar. |
| 674 * | 732 * |
| 675 * @param {number} position in pixels. | 733 * @param {number} position in pixels. |
| 676 * @return {number} [0..1] proportion. | 734 * @return {number} [0..1] proportion. |
| 677 */ | 735 */ |
| 678 MediaControls.Slider.prototype.getProportion = function(position) { | 736 MediaControls.Slider.prototype.getProportion = function(position) { |
| 679 var rect = this.bar_.getBoundingClientRect(); | 737 var rect = this.bar_.getBoundingClientRect(); |
| 680 return Math.max(0, Math.min(1, (position - rect.left) / rect.width)); | 738 return Math.max(0, Math.min(1, (position - rect.left) / rect.width)); |
| 681 }; | 739 }; |
| 682 | 740 |
| 683 /** | 741 /** |
| 742 * Set value formatting function. | |
|
hirono
2014/12/24 01:43:22
nit: Sets
yawano
2014/12/24 02:39:55
Done.
| |
| 743 * @param {function(number):string} func Value formatting function. | |
| 744 */ | |
| 745 MediaControls.Slider.prototype.setValueToStringFunction = function(func) {}; | |
| 746 | |
| 747 /** | |
| 684 * 'change' event handler. | 748 * 'change' event handler. |
| 685 * @private | 749 * @private |
| 686 */ | 750 */ |
| 687 MediaControls.Slider.prototype.onInputChange_ = function() { | 751 MediaControls.Slider.prototype.onInputChange_ = function() { |
| 688 this.value_ = this.getValueFromUI_(); | 752 this.value_ = this.getValueFromUI_(); |
| 689 this.setFilled_(this.value_); | 753 this.setFilled_(this.value_); |
| 690 this.onChange_(this.value_); | 754 this.onChange_(this.value_); |
| 691 }; | 755 }; |
| 692 | 756 |
| 693 /** | 757 /** |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 716 }; | 780 }; |
| 717 | 781 |
| 718 /** | 782 /** |
| 719 * Create a customized slider with animated thumb movement. | 783 * Create a customized slider with animated thumb movement. |
| 720 * | 784 * |
| 721 * @param {HTMLElement} container The containing div element. | 785 * @param {HTMLElement} container The containing div element. |
| 722 * @param {number} value Initial value [0..1]. | 786 * @param {number} value Initial value [0..1]. |
| 723 * @param {number} range Number of distinct slider positions to be supported. | 787 * @param {number} range Number of distinct slider positions to be supported. |
| 724 * @param {function(number)} onChange Value change handler. | 788 * @param {function(number)} onChange Value change handler. |
| 725 * @param {function(boolean)} onDrag Drag begin/end handler. | 789 * @param {function(boolean)} onDrag Drag begin/end handler. |
| 726 * @param {function(number):string} formatFunction Value formatting function. | |
| 727 * @constructor | 790 * @constructor |
| 791 * @struct | |
| 792 * @extends {MediaControls.Slider} | |
| 728 */ | 793 */ |
| 729 MediaControls.AnimatedSlider = function( | 794 MediaControls.AnimatedSlider = function( |
| 730 container, value, range, onChange, onDrag, formatFunction) { | 795 container, value, range, onChange, onDrag) { |
| 731 MediaControls.Slider.apply(this, arguments); | 796 MediaControls.Slider.apply(this, arguments); |
| 797 | |
| 798 /** | |
| 799 * @type {number} | |
| 800 * @private | |
| 801 */ | |
| 802 this.animationInterval_ = 0; | |
| 732 }; | 803 }; |
| 733 | 804 |
| 734 MediaControls.AnimatedSlider.prototype = { | 805 MediaControls.AnimatedSlider.prototype = { |
| 735 __proto__: MediaControls.Slider.prototype | 806 __proto__: MediaControls.Slider.prototype |
| 736 }; | 807 }; |
| 737 | 808 |
| 738 /** | 809 /** |
| 739 * Number of animation steps. | 810 * Number of animation steps. |
| 740 */ | 811 */ |
| 741 MediaControls.AnimatedSlider.STEPS = 10; | 812 MediaControls.AnimatedSlider.STEPS = 10; |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 772 * | 843 * |
| 773 * The time value is shown above the slider bar at the mouse position. | 844 * The time value is shown above the slider bar at the mouse position. |
| 774 * | 845 * |
| 775 * @param {HTMLElement} container The containing div element. | 846 * @param {HTMLElement} container The containing div element. |
| 776 * @param {number} value Initial value [0..1]. | 847 * @param {number} value Initial value [0..1]. |
| 777 * @param {number} range Number of distinct slider positions to be supported. | 848 * @param {number} range Number of distinct slider positions to be supported. |
| 778 * @param {function(number)} onChange Value change handler. | 849 * @param {function(number)} onChange Value change handler. |
| 779 * @param {function(boolean)} onDrag Drag begin/end handler. | 850 * @param {function(boolean)} onDrag Drag begin/end handler. |
| 780 * @param {function(number):string} formatFunction Value formatting function. | 851 * @param {function(number):string} formatFunction Value formatting function. |
| 781 * @constructor | 852 * @constructor |
| 853 * @struct | |
| 854 * @extends {MediaControls.Slider} | |
| 782 */ | 855 */ |
| 783 MediaControls.PreciseSlider = function( | 856 MediaControls.PreciseSlider = function( |
| 784 container, value, range, onChange, onDrag, formatFunction) { | 857 container, value, range, onChange, onDrag, formatFunction) { |
| 785 MediaControls.Slider.apply(this, arguments); | 858 MediaControls.Slider.apply(this, arguments); |
| 786 | 859 |
| 787 var doc = this.container_.ownerDocument; | 860 var doc = this.container_.ownerDocument; |
| 788 | 861 |
| 789 /** | 862 /** |
| 790 * @type {function(number):string} | 863 * @type {number} |
| 864 * @private | |
| 865 */ | |
| 866 this.latestMouseUpTime_ = 0; | |
| 867 | |
| 868 /** | |
| 869 * @type {number} | |
| 870 * @private | |
| 871 */ | |
| 872 this.seekMarkTimer_ = 0; | |
| 873 | |
| 874 /** | |
| 875 * @type {number} | |
| 876 * @private | |
| 877 */ | |
| 878 this.latestSeekRatio_ = 0; | |
| 879 | |
| 880 /** | |
| 881 * @type {?function(number):string} | |
| 791 * @private | 882 * @private |
| 792 */ | 883 */ |
| 793 this.valueToString_ = null; | 884 this.valueToString_ = null; |
| 794 | 885 |
| 795 this.seekMark_ = doc.createElement('div'); | 886 this.seekMark_ = doc.createElement('div'); |
| 796 this.seekMark_.className = 'seek-mark'; | 887 this.seekMark_.className = 'seek-mark'; |
| 797 this.getBar().appendChild(this.seekMark_); | 888 this.getBar().appendChild(this.seekMark_); |
| 798 | 889 |
| 799 this.seekLabel_ = doc.createElement('div'); | 890 this.seekLabel_ = doc.createElement('div'); |
| 800 this.seekLabel_.className = 'seek-label'; | 891 this.seekLabel_.className = 'seek-label'; |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 824 * Hide the seek mark for this long after changing the position with a drag. | 915 * Hide the seek mark for this long after changing the position with a drag. |
| 825 */ | 916 */ |
| 826 MediaControls.PreciseSlider.HIDE_AFTER_DRAG_DELAY = 750; | 917 MediaControls.PreciseSlider.HIDE_AFTER_DRAG_DELAY = 750; |
| 827 | 918 |
| 828 /** | 919 /** |
| 829 * Default hide timeout (no hiding). | 920 * Default hide timeout (no hiding). |
| 830 */ | 921 */ |
| 831 MediaControls.PreciseSlider.NO_AUTO_HIDE = 0; | 922 MediaControls.PreciseSlider.NO_AUTO_HIDE = 0; |
| 832 | 923 |
| 833 /** | 924 /** |
| 834 * @param {function(number):string} func Value formatting function. | 925 * @override |
| 835 */ | 926 */ |
| 836 MediaControls.PreciseSlider.prototype.setValueToStringFunction = | 927 MediaControls.PreciseSlider.prototype.setValueToStringFunction = |
| 837 function(func) { | 928 function(func) { |
| 838 this.valueToString_ = func; | 929 this.valueToString_ = func; |
| 839 | 930 |
| 840 /* It is not completely accurate to assume that the max value corresponds | 931 /* It is not completely accurate to assume that the max value corresponds |
| 841 to the longest string, but generous CSS padding will compensate for that. */ | 932 to the longest string, but generous CSS padding will compensate for that. */ |
| 842 var labelWidth = this.valueToString_(1).length / 2 + 1; | 933 var labelWidth = this.valueToString_(1).length / 2 + 1; |
| 843 this.seekLabel_.style.width = labelWidth + 'em'; | 934 this.seekLabel_.style.width = labelWidth + 'em'; |
| 844 this.seekLabel_.style.marginLeft = -labelWidth / 2 + 'em'; | 935 this.seekLabel_.style.marginLeft = -labelWidth / 2 + 'em'; |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 864 this.seekMark_.classList.remove('inverted'); | 955 this.seekMark_.classList.remove('inverted'); |
| 865 } else { | 956 } else { |
| 866 this.seekMark_.classList.add('inverted'); | 957 this.seekMark_.classList.add('inverted'); |
| 867 } | 958 } |
| 868 this.seekLabel_.textContent = this.valueToString_(ratio); | 959 this.seekLabel_.textContent = this.valueToString_(ratio); |
| 869 | 960 |
| 870 this.seekMark_.classList.add('visible'); | 961 this.seekMark_.classList.add('visible'); |
| 871 | 962 |
| 872 if (this.seekMarkTimer_) { | 963 if (this.seekMarkTimer_) { |
| 873 clearTimeout(this.seekMarkTimer_); | 964 clearTimeout(this.seekMarkTimer_); |
| 874 this.seekMarkTimer_ = null; | 965 this.seekMarkTimer_ = 0; |
| 875 } | 966 } |
| 876 if (timeout != MediaControls.PreciseSlider.NO_AUTO_HIDE) { | 967 if (timeout != MediaControls.PreciseSlider.NO_AUTO_HIDE) { |
| 877 this.seekMarkTimer_ = setTimeout(this.hideSeekMark_.bind(this), timeout); | 968 this.seekMarkTimer_ = setTimeout(this.hideSeekMark_.bind(this), timeout); |
| 878 } | 969 } |
| 879 }; | 970 }; |
| 880 | 971 |
| 881 /** | 972 /** |
| 882 * @private | 973 * @private |
| 883 */ | 974 */ |
| 884 MediaControls.PreciseSlider.prototype.hideSeekMark_ = function() { | 975 MediaControls.PreciseSlider.prototype.hideSeekMark_ = function() { |
| 885 this.seekMarkTimer_ = null; | 976 this.seekMarkTimer_ = 0; |
| 886 this.seekMark_.classList.remove('visible'); | 977 this.seekMark_.classList.remove('visible'); |
| 887 }; | 978 }; |
| 888 | 979 |
| 889 /** | 980 /** |
| 890 * 'mouseout' event handler. | 981 * 'mouseout' event handler. |
| 891 * @param {Event} e Event. | 982 * @param {Event} e Event. |
| 892 * @private | 983 * @private |
| 893 */ | 984 */ |
| 894 MediaControls.PreciseSlider.prototype.onMouseMove_ = function(e) { | 985 MediaControls.PreciseSlider.prototype.onMouseMove_ = function(e) { |
| 895 this.latestSeekRatio_ = this.getProportion(e.clientX); | 986 this.latestSeekRatio_ = this.getProportion(e.clientX); |
| 896 | 987 |
| 897 var self = this; | 988 var showMark = function() { |
| 898 function showMark() { | 989 if (!this.isDragging()) { |
| 899 if (!self.isDragging()) { | 990 this.showSeekMark_(this.latestSeekRatio_, |
| 900 self.showSeekMark_(self.latestSeekRatio_, | |
| 901 MediaControls.PreciseSlider.HIDE_AFTER_MOVE_DELAY); | 991 MediaControls.PreciseSlider.HIDE_AFTER_MOVE_DELAY); |
| 902 } | 992 } |
| 903 } | 993 }.bind(this); |
| 904 | 994 |
| 905 if (this.seekMark_.classList.contains('visible')) { | 995 if (this.seekMark_.classList.contains('visible')) { |
| 906 showMark(); | 996 showMark(); |
| 907 } else if (!this.seekMarkTimer_) { | 997 } else if (!this.seekMarkTimer_) { |
| 908 this.seekMarkTimer_ = | 998 this.seekMarkTimer_ = |
| 909 setTimeout(showMark, MediaControls.PreciseSlider.SHOW_DELAY); | 999 setTimeout(showMark, MediaControls.PreciseSlider.SHOW_DELAY); |
| 910 } | 1000 } |
| 911 }; | 1001 }; |
| 912 | 1002 |
| 913 /** | 1003 /** |
| 914 * 'mouseout' event handler. | 1004 * 'mouseout' event handler. |
| 915 * @param {Event} e Event. | 1005 * @param {Event} e Event. |
| 916 * @private | 1006 * @private |
| 917 */ | 1007 */ |
| 918 MediaControls.PreciseSlider.prototype.onMouseOut_ = function(e) { | 1008 MediaControls.PreciseSlider.prototype.onMouseOut_ = function(e) { |
| 919 for (var element = e.relatedTarget; element; element = element.parentNode) { | 1009 for (var element = e.relatedTarget; element; element = element.parentNode) { |
| 920 if (element == this.getContainer()) | 1010 if (element == this.getContainer()) |
| 921 return; | 1011 return; |
| 922 } | 1012 } |
| 923 if (this.seekMarkTimer_) { | 1013 if (this.seekMarkTimer_) { |
| 924 clearTimeout(this.seekMarkTimer_); | 1014 clearTimeout(this.seekMarkTimer_); |
| 925 this.seekMarkTimer_ = null; | 1015 this.seekMarkTimer_ = 0; |
| 926 } | 1016 } |
| 927 this.hideSeekMark_(); | 1017 this.hideSeekMark_(); |
| 928 }; | 1018 }; |
| 929 | 1019 |
| 930 /** | 1020 /** |
| 931 * 'change' event handler. | 1021 * 'change' event handler. |
| 932 * @private | 1022 * @private |
| 933 */ | 1023 */ |
| 934 MediaControls.PreciseSlider.prototype.onInputChange_ = function() { | 1024 MediaControls.PreciseSlider.prototype.onInputChange_ = function() { |
| 935 MediaControls.Slider.prototype.onInputChange_.apply(this, arguments); | 1025 MediaControls.Slider.prototype.onInputChange_.apply(this, arguments); |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 957 this.showSeekMark_( | 1047 this.showSeekMark_( |
| 958 this.getValue(), MediaControls.PreciseSlider.HIDE_AFTER_DRAG_DELAY); | 1048 this.getValue(), MediaControls.PreciseSlider.HIDE_AFTER_DRAG_DELAY); |
| 959 this.latestMouseUpTime_ = Date.now(); | 1049 this.latestMouseUpTime_ = Date.now(); |
| 960 } | 1050 } |
| 961 }; | 1051 }; |
| 962 | 1052 |
| 963 /** | 1053 /** |
| 964 * Create video controls. | 1054 * Create video controls. |
| 965 * | 1055 * |
| 966 * @param {HTMLElement} containerElement The container for the controls. | 1056 * @param {HTMLElement} containerElement The container for the controls. |
| 967 * @param {function} onMediaError Function to display an error message. | 1057 * @param {function(Event)} onMediaError Function to display an error message. |
| 968 * @param {function(string):string} stringFunction Function providing localized | 1058 * @param {function(string):string} stringFunction Function providing localized |
| 969 * strings. | 1059 * strings. |
| 970 * @param {function=} opt_fullScreenToggle Function to toggle fullscreen mode. | 1060 * @param {function(Event)=} opt_fullScreenToggle Function to toggle fullscreen |
| 1061 * mode. | |
| 971 * @param {HTMLElement=} opt_stateIconParent The parent for the icon that | 1062 * @param {HTMLElement=} opt_stateIconParent The parent for the icon that |
| 972 * gives visual feedback when the playback state changes. | 1063 * gives visual feedback when the playback state changes. |
| 973 * @constructor | 1064 * @constructor |
| 1065 * @struct | |
| 1066 * @extends {MediaControls} | |
| 974 */ | 1067 */ |
| 975 function VideoControls(containerElement, onMediaError, stringFunction, | 1068 function VideoControls(containerElement, onMediaError, stringFunction, |
| 976 opt_fullScreenToggle, opt_stateIconParent) { | 1069 opt_fullScreenToggle, opt_stateIconParent) { |
| 977 MediaControls.call(this, containerElement, onMediaError); | 1070 MediaControls.call(this, containerElement, onMediaError); |
| 978 this.stringFunction_ = stringFunction; | 1071 this.stringFunction_ = stringFunction; |
| 979 | 1072 |
| 980 this.container_.classList.add('video-controls'); | 1073 this.container_.classList.add('video-controls'); |
| 981 this.initPlayButton(); | 1074 this.initPlayButton(); |
| 982 this.initTimeControls(true /* show seek mark */); | 1075 this.initTimeControls(true /* show seek mark */); |
| 983 this.initVolumeControls(); | 1076 this.initVolumeControls(); |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1065 'webkitAnimationEnd', onAnimationEnd); | 1158 'webkitAnimationEnd', onAnimationEnd); |
| 1066 this.textBanner_.removeAttribute('visible'); | 1159 this.textBanner_.removeAttribute('visible'); |
| 1067 }.bind(this); | 1160 }.bind(this); |
| 1068 this.textBanner_.addEventListener('webkitAnimationEnd', onAnimationEnd); | 1161 this.textBanner_.addEventListener('webkitAnimationEnd', onAnimationEnd); |
| 1069 | 1162 |
| 1070 this.textBanner_.setAttribute('visible', 'true'); | 1163 this.textBanner_.setAttribute('visible', 'true'); |
| 1071 }.bind(this), 0); | 1164 }.bind(this), 0); |
| 1072 }; | 1165 }; |
| 1073 | 1166 |
| 1074 /** | 1167 /** |
| 1075 * Toggle play/pause state on a mouse click on the play/pause button. Can be | 1168 * @override |
| 1076 * called externally. | |
| 1077 * | |
| 1078 * @param {Event} event Mouse click event. | |
| 1079 */ | 1169 */ |
| 1080 VideoControls.prototype.onPlayButtonClicked = function(event) { | 1170 VideoControls.prototype.onPlayButtonClicked = function(event) { |
| 1081 if (event.ctrlKey) { | 1171 if (event.ctrlKey) { |
| 1082 this.toggleLoopedModeWithFeedback(true); | 1172 this.toggleLoopedModeWithFeedback(true); |
| 1083 if (!this.isPlaying()) | 1173 if (!this.isPlaying()) |
| 1084 this.togglePlayState(); | 1174 this.togglePlayState(); |
| 1085 } else { | 1175 } else { |
| 1086 this.togglePlayState(); | 1176 this.togglePlayState(); |
| 1087 } | 1177 } |
| 1088 }; | 1178 }; |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1209 hideBelow('.volume', 275); | 1299 hideBelow('.volume', 275); |
| 1210 hideBelow('.volume-controls', 210); | 1300 hideBelow('.volume-controls', 210); |
| 1211 hideBelow('.fullscreen', 150); | 1301 hideBelow('.fullscreen', 150); |
| 1212 }; | 1302 }; |
| 1213 | 1303 |
| 1214 /** | 1304 /** |
| 1215 * Creates audio controls. | 1305 * Creates audio controls. |
| 1216 * | 1306 * |
| 1217 * @param {HTMLElement} container Parent container. | 1307 * @param {HTMLElement} container Parent container. |
| 1218 * @param {function(boolean)} advanceTrack Parameter: true=forward. | 1308 * @param {function(boolean)} advanceTrack Parameter: true=forward. |
| 1219 * @param {function} onError Error handler. | 1309 * @param {function(Event)} onError Error handler. |
| 1220 * @constructor | 1310 * @constructor |
| 1311 * @struct | |
| 1312 * @extends {MediaControls} | |
| 1221 */ | 1313 */ |
| 1222 function AudioControls(container, advanceTrack, onError) { | 1314 function AudioControls(container, advanceTrack, onError) { |
| 1223 MediaControls.call(this, container, onError); | 1315 MediaControls.call(this, container, onError); |
| 1224 | 1316 |
| 1225 this.container_.classList.add('audio-controls'); | 1317 this.container_.classList.add('audio-controls'); |
| 1226 | 1318 |
| 1227 this.advanceTrack_ = advanceTrack; | 1319 this.advanceTrack_ = advanceTrack; |
| 1228 | 1320 |
| 1229 this.initPlayButton(); | 1321 this.initPlayButton(); |
| 1230 this.initTimeControls(false /* no seek mark */); | 1322 this.initTimeControls(false /* no seek mark */); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1265 AudioControls.prototype.onAdvanceClick_ = function(forward) { | 1357 AudioControls.prototype.onAdvanceClick_ = function(forward) { |
| 1266 if (!forward && | 1358 if (!forward && |
| 1267 (this.getMedia().currentTime > AudioControls.TRACK_RESTART_THRESHOLD)) { | 1359 (this.getMedia().currentTime > AudioControls.TRACK_RESTART_THRESHOLD)) { |
| 1268 // We are far enough from the beginning of the current track. | 1360 // We are far enough from the beginning of the current track. |
| 1269 // Restart it instead of than skipping to the previous one. | 1361 // Restart it instead of than skipping to the previous one. |
| 1270 this.getMedia().currentTime = 0; | 1362 this.getMedia().currentTime = 0; |
| 1271 } else { | 1363 } else { |
| 1272 this.advanceTrack_(forward); | 1364 this.advanceTrack_(forward); |
| 1273 } | 1365 } |
| 1274 }; | 1366 }; |
| OLD | NEW |