mirror of
				https://github.com/videojs/video.js.git
				synced 2025-10-31 00:08:01 +02:00 
			
		
		
		
	Close GH-672: Control bar updates. Fixes #556, Fixes #500, Fixes #374, Fixes #403, Fixes #441, Fixes #193, Fixes #602, Fixes #561, Fixes #281
This commit is contained in:
		
				
					committed by
					
						 Steve Heffernan
						Steve Heffernan
					
				
			
			
				
	
			
			
			
						parent
						
							699c476575
						
					
				
				
					commit
					02de927043
				
			| @@ -4,20 +4,21 @@ Version GENERATED_AT_BUILD | ||||
| Create your own skin at http://designer.videojs.com | ||||
| */ | ||||
|  | ||||
| // To customize the player skin, change the values of the variables or edit the  | ||||
| // To customize the player skin, change the values of the variables or edit the | ||||
| // CSS below. | ||||
| // (This file uses LESS. Learn more at http://lesscss.org/) | ||||
|  | ||||
| // The base font size controls the size of everything, not just text. All  | ||||
| // The base font size controls the size of everything, not just text. All | ||||
| // diminensions use em-based sizes so that the scale along with the font size. | ||||
| // Try increasing it to 20px and see what happens. | ||||
| @base-font-size: 10px; | ||||
| @touch-device-font-size: 15px; | ||||
|  | ||||
| // The main font color controls the color of the text and the icons (font icons) | ||||
| @main-font-color: #CCCCCC; // e.g. rgb(255, 255, 255) or #ffffff | ||||
|  | ||||
| // The default color of control backgrounds is mostly black but with a little  | ||||
| // bit of blue so it can still be seen on all black video frames, which are  | ||||
| // The default color of control backgrounds is mostly black but with a little | ||||
| // bit of blue so it can still be seen on all black video frames, which are | ||||
| // common. | ||||
| @control-bg-color: #07141E; // e.g. rgb(255, 255, 255) or #ffffff | ||||
| @control-bg-alpha: 0.7; // 1.0 = 100% opacity, 0.0 = 0% opacity | ||||
| @@ -32,12 +33,12 @@ Create your own skin at http://designer.videojs.com | ||||
| @slider-background-color: #333333; | ||||
| @slider-background-alpha: 0.9; // 1.0 = 100% opacity, 0.0 = 0% opacity | ||||
|  | ||||
| // The "Big Play Button" is the play button that shows before the video plays.  | ||||
| // To center it set the align values to center and middle. The typical location  | ||||
| // The "Big Play Button" is the play button that shows before the video plays. | ||||
| // To center it set the align values to center and middle. The typical location | ||||
| // of the button is the center, but there is trend towards moving it to a corner | ||||
| // where it gets out of the way of valuable content in the poster image. | ||||
| @big-play-align: left; // left, center, or right | ||||
| @big-play-vertical-align: top; // top, middle, or bottom  | ||||
| @big-play-vertical-align: top; // top, middle, or bottom | ||||
| // The button colors match the control colors by default but you can customize | ||||
| // them by replace the variables (@control-bg-color) with your own color values. | ||||
| @big-play-bg-color: @control-bg-color; | ||||
| @@ -58,9 +59,9 @@ Create your own skin at http://designer.videojs.com | ||||
|  | ||||
| /* SKIN | ||||
| ================================================================================ | ||||
| The main class name for all skin-specific styles. To make your own skin,  | ||||
| replace all occurances of 'vjs-default-skin' with a new name. Then add your new  | ||||
| skin name to your video tag instead of the default skin.  | ||||
| The main class name for all skin-specific styles. To make your own skin, | ||||
| replace all occurances of 'vjs-default-skin' with a new name. Then add your new | ||||
| skin name to your video tag instead of the default skin. | ||||
| e.g. <video class="video-js my-skin-name"> | ||||
| */ | ||||
| .vjs-default-skin { | ||||
| @@ -98,7 +99,7 @@ The control icons are from a custom font. Each icon corresponds to a character | ||||
| @captions-icon: "\e008"; | ||||
|  | ||||
| /* Base UI Component Classes | ||||
| --------------------------------------------------------------------------------  | ||||
| -------------------------------------------------------------------------------- | ||||
| */ | ||||
|  | ||||
| /* Slider - used for Volume bar and Seek bar */ | ||||
| @@ -141,13 +142,13 @@ The control icons are from a custom font. Each icon corresponds to a character | ||||
|  | ||||
| /* Control Bar | ||||
| -------------------------------------------------------------------------------- | ||||
| The default control bar that is a container for most of the controls.  | ||||
| The default control bar that is a container for most of the controls. | ||||
| */ | ||||
| .vjs-default-skin .vjs-control-bar { | ||||
|   /* Start hidden */// | ||||
|   display: none; | ||||
|   position: absolute; | ||||
|   /* Place control bar at the bottom of the player box/video.  | ||||
|   /* Place control bar at the bottom of the player box/video. | ||||
|      If you want more margin below the control bar, add more height. */// | ||||
|   bottom: 0; | ||||
|   /* Use left/right to stretch to 100% width of player div */// | ||||
| @@ -159,6 +160,36 @@ The default control bar that is a container for most of the controls. | ||||
|   .background-color-with-alpha(@control-bg-color, @control-bg-alpha); | ||||
| } | ||||
|  | ||||
| /* Show the control bar only once the video has started playing */ | ||||
| .vjs-default-skin.vjs-has-started .vjs-control-bar { | ||||
|   display: block; | ||||
|   /* Visibility needed to make sure things hide in older browsers too. */ | ||||
|   visibility: visible; | ||||
|   opacity: 1; | ||||
|  | ||||
|   @trans: visibility 0.1s, opacity 0.1s; // Var needed because of comma | ||||
|   .transition(@trans); | ||||
| } | ||||
|  | ||||
| /* Hide the control bar when the video is playing and the user is inactive  */ | ||||
| .vjs-default-skin.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar { | ||||
|   display: block; | ||||
|   visibility: hidden; | ||||
|   opacity: 0; | ||||
|  | ||||
|   @trans: visibility 1.0s, opacity 1.0s; | ||||
|   .transition(@trans); | ||||
| } | ||||
|  | ||||
| .vjs-default-skin.vjs-controls-disabled .vjs-control-bar { | ||||
|   display: none; | ||||
| } | ||||
|  | ||||
| .vjs-default-skin.vjs-using-native-controls .vjs-control-bar { | ||||
|   display: none; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* General styles for individual controls. */// | ||||
| .vjs-default-skin .vjs-control { | ||||
|   outline: none; | ||||
| @@ -191,15 +222,15 @@ The default control bar that is a container for most of the controls. | ||||
|   text-shadow: 0em 0em 1em rgba(255, 255, 255, 1); | ||||
| } | ||||
|  | ||||
| .vjs-default-skin .vjs-control:focus {  | ||||
| .vjs-default-skin .vjs-control:focus { | ||||
|   /*  outline: 0; */// | ||||
|   /* keyboard-only users cannot see the focus on several of the UI elements when | ||||
|   this is set to 0 */ | ||||
| } | ||||
|  | ||||
| /* Hide control text visually, but have it available for screenreaders */ | ||||
| .vjs-default-skin .vjs-control-text {  | ||||
|   .hide-visually;  | ||||
| .vjs-default-skin .vjs-control-text { | ||||
|   .hide-visually; | ||||
| } | ||||
|  | ||||
| /* Play/Pause | ||||
| @@ -388,7 +419,7 @@ The default control bar that is a container for most of the controls. | ||||
|  | ||||
| /* Big Play Button (play button at start) | ||||
| -------------------------------------------------------------------------------- | ||||
| Positioning of the play button in the center or other corners can be done more  | ||||
| Positioning of the play button in the center or other corners can be done more | ||||
| easily in the skin designer. http://designer.videojs.com/ | ||||
| */ | ||||
| .vjs-default-skin .vjs-big-play-button { | ||||
| @@ -420,6 +451,20 @@ easily in the skin designer. http://designer.videojs.com/ | ||||
|   .transition(all 0.4s); | ||||
| } | ||||
|  | ||||
| /* Hide if controls are disabled */ | ||||
| .vjs-default-skin.vjs-controls-disabled .vjs-big-play-button { | ||||
|   display: none; | ||||
| } | ||||
| /* Hide when video starts playing */ | ||||
| .vjs-default-skin.vjs-has-started .vjs-big-play-button { | ||||
|   display: none; | ||||
| } | ||||
| /* Hide on mobile devices. Remove when we stop using native controls | ||||
|     by default on mobile  */ | ||||
| .vjs-default-skin.vjs-using-native-controls .vjs-big-play-button { | ||||
|   display: none; | ||||
| } | ||||
|  | ||||
| .vjs-default-skin:hover .vjs-big-play-button, | ||||
| .vjs-default-skin .vjs-big-play-button:focus { | ||||
|   outline: 0; | ||||
| @@ -609,18 +654,18 @@ REQUIRED STYLES (be careful overriding) | ||||
| ================================================================================ | ||||
| When loading the player, the video tag is replaced with a DIV, | ||||
| that will hold the video tag or object tag for other playback methods. | ||||
| The div contains the video playback element (Flash or HTML5) and controls,  | ||||
| The div contains the video playback element (Flash or HTML5) and controls, | ||||
| and sets the width and height of the video. | ||||
|  | ||||
| ** If you want to add some kind of border/padding (e.g. a frame), or special  | ||||
| positioning, use another containing element. Otherwise you risk messing up  | ||||
| ** If you want to add some kind of border/padding (e.g. a frame), or special | ||||
| positioning, use another containing element. Otherwise you risk messing up | ||||
| control positioning and full window mode. ** | ||||
| */ | ||||
| .video-js { | ||||
|   background-color: #000; | ||||
|   position: relative; | ||||
|   padding: 0; | ||||
|   /* Start with 10px for base font size so other dimensions can be em based and  | ||||
|   /* Start with 10px for base font size so other dimensions can be em based and | ||||
|      easily calculable. */ | ||||
|   font-size: @base-font-size; | ||||
|   /* Allow poster to be vertially aligned. */ | ||||
| @@ -649,7 +694,7 @@ control positioning and full window mode. ** | ||||
|   height: 100%; | ||||
| } | ||||
|  | ||||
| /* Fix for Firefox 9 fullscreen (only if it is enabled). Not needed when  | ||||
| /* Fix for Firefox 9 fullscreen (only if it is enabled). Not needed when | ||||
|    checking fullScreenEnabled. */ | ||||
| .video-js:-moz-full-screen { position: absolute; } | ||||
|  | ||||
| @@ -675,9 +720,12 @@ body.vjs-full-window { | ||||
|   _position: absolute; | ||||
| } | ||||
| .video-js:-webkit-full-screen { | ||||
|   width: 100% !important;  | ||||
|   width: 100% !important; | ||||
|   height: 100% !important; | ||||
| } | ||||
| .video-js.vjs-fullscreen.vjs-user-inactive { | ||||
|   cursor: none; | ||||
| } | ||||
|  | ||||
| /* Poster Styles */ | ||||
| .vjs-poster { | ||||
| @@ -699,6 +747,11 @@ body.vjs-full-window { | ||||
|   width: 100%; | ||||
| } | ||||
|  | ||||
| /* Hide the poster when native controls are used otherwise it covers them */ | ||||
| .video-js.vjs-using-native-controls .vjs-poster { | ||||
|   display: none; | ||||
| } | ||||
|  | ||||
| /* Text Track Styles */ | ||||
| /* Overall track holder for both captions and subtitles */ | ||||
| .video-js .vjs-text-track-display { | ||||
| @@ -722,26 +775,6 @@ body.vjs-full-window { | ||||
| .video-js .vjs-captions { color: #fc6 /* Captions are yellow */; } | ||||
| .vjs-tt-cue { display: block; } | ||||
|  | ||||
| /* Fading sytles, used to fade control bar. */ | ||||
| .vjs-fade-in { | ||||
|   display: block !important; | ||||
|   /* Visibility needed to make sure things hide in older browsers too. */ | ||||
|   visibility: visible; | ||||
|   opacity: 1; | ||||
|  | ||||
|   @trans: visibility 0.1s, opacity 0.1s; // Var needed because of comma | ||||
|   .transition(@trans); | ||||
| } | ||||
| .vjs-fade-out { | ||||
|   display: block !important; | ||||
|   visibility: hidden; | ||||
|   opacity: 0; | ||||
|  | ||||
|   @trans: visibility 1.5s, opacity 1.5s; | ||||
|   .transition(@trans); | ||||
|   /* Wait a moment before fading out the control bar */ | ||||
|   .transition-delay(2s); | ||||
| } | ||||
| /* Hide disabled or unsupported controls */ | ||||
| .vjs-default-skin .vjs-hidden { display: none; } | ||||
|  | ||||
| @@ -751,9 +784,9 @@ body.vjs-full-window { | ||||
|   visibility: visible; | ||||
| } | ||||
|  | ||||
| // MIXINS  | ||||
| // MIXINS | ||||
| // ============================================================================= | ||||
| // Mixins are a LESS feature and are used to add vendor prefixes to CSS rules  | ||||
| // Mixins are a LESS feature and are used to add vendor prefixes to CSS rules | ||||
| // when needed. | ||||
|  | ||||
| // https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow | ||||
| @@ -813,7 +846,7 @@ body.vjs-full-window { | ||||
| .user-select (@string: none) { | ||||
|   /* user-select */// | ||||
|   -webkit-user-select: @string; | ||||
|      -moz-user-select: @string;   | ||||
|      -moz-user-select: @string; | ||||
|       -ms-user-select: @string; | ||||
|           user-select: @string; | ||||
| } | ||||
| @@ -822,14 +855,14 @@ body.vjs-full-window { | ||||
| // http://h5bp.com/v | ||||
| .hide-visually () { | ||||
|   /* hide-visually */// | ||||
|   border: 0;  | ||||
|   clip: rect(0 0 0 0);  | ||||
|   height: 1px;  | ||||
|   margin: -1px;  | ||||
|   overflow: hidden;  | ||||
|   padding: 0;  | ||||
|   position:  | ||||
|   absolute;  | ||||
|   border: 0; | ||||
|   clip: rect(0 0 0 0); | ||||
|   height: 1px; | ||||
|   margin: -1px; | ||||
|   overflow: hidden; | ||||
|   padding: 0; | ||||
|   position: | ||||
|   absolute; | ||||
|   width: 1px; | ||||
| } | ||||
|  | ||||
| @@ -882,27 +915,27 @@ body.vjs-full-window { | ||||
| // * We want this file to continue to be accessible by people who don't know | ||||
| //   LESS but know CSS. This means finding the balance between using the most | ||||
| //   valuable LESS features (e.g. variables) and keeping it looking like CSS. | ||||
| //   So it's best to avoid advanced LESS features like conditional statements.  | ||||
| //   (we're using one for the big play button position because that's a hot  | ||||
| //   So it's best to avoid advanced LESS features like conditional statements. | ||||
| //   (we're using one for the big play button position because that's a hot | ||||
| //   topic) | ||||
| //  | ||||
| // | ||||
| // * We care about the readability of the CSS output of LESS, which means we | ||||
| //   have to be careful about what features of LESS we use. (if you're building | ||||
| //   your own skin this may not apply) | ||||
| //     1. Comments inside of rules (strangely) have an extra line added after  | ||||
| //        them in the CSS output. To avoid this we can add a LESS comment after  | ||||
| //     1. Comments inside of rules (strangely) have an extra line added after | ||||
| //        them in the CSS output. To avoid this we can add a LESS comment after | ||||
| //        the CSS comment. | ||||
| //          /* comment */// | ||||
| //  | ||||
| // | ||||
| //     2. In a rule with nested rules, any comments outside of a rule are moved | ||||
| //        to the top of the parent rule. i.e. it might look like: | ||||
| //          /* title of rule 1 */ | ||||
| //          /* title of rule 2 */ | ||||
| //          .rule1 {} | ||||
| //          .rule2 {} | ||||
| //        This is why we aren't using nested rules inside of the  | ||||
| //        This is why we aren't using nested rules inside of the | ||||
| //        vjs-default-skin class. | ||||
|  | ||||
| /* -----------------------------------------------------------------------------  | ||||
| The original source of this file lives at  | ||||
| /* ----------------------------------------------------------------------------- | ||||
| The original source of this file lives at | ||||
| https://github.com/videojs/video.js/blob/master/src/css/video-js.less */ | ||||
|   | ||||
| @@ -1,24 +1,13 @@ | ||||
| /* Big Play Button | ||||
| ================================================================================ */ | ||||
| /** | ||||
|  * Initial play button. Shows before the video has played. | ||||
|  * Initial play button. Shows before the video has played. The hiding of the | ||||
|  * big play button is done via CSS and player states. | ||||
|  * @param {vjs.Player|Object} player | ||||
|  * @param {Object=} options | ||||
|  * @constructor | ||||
|  */ | ||||
| vjs.BigPlayButton = vjs.Button.extend({ | ||||
|   /** @constructor */ | ||||
|   init: function(player, options){ | ||||
|     vjs.Button.call(this, player, options); | ||||
|  | ||||
|     if (!player.controls()) { | ||||
|       this.hide(); | ||||
|     } | ||||
|  | ||||
|     player.on('play', vjs.bind(this, this.hide)); | ||||
|     // player.on('ended', vjs.bind(this, this.show)); | ||||
|   } | ||||
| }); | ||||
| vjs.BigPlayButton = vjs.Button.extend(); | ||||
|  | ||||
| vjs.BigPlayButton.prototype.createEl = function(){ | ||||
|   return vjs.Button.prototype.createEl.call(this, 'div', { | ||||
| @@ -29,10 +18,5 @@ vjs.BigPlayButton.prototype.createEl = function(){ | ||||
| }; | ||||
|  | ||||
| vjs.BigPlayButton.prototype.onClick = function(){ | ||||
|   // Go back to the beginning if big play button is showing at the end. | ||||
|   // Have to check for current time otherwise it might throw a 'not ready' error. | ||||
|   //if(this.player_.currentTime()) { | ||||
|     //this.player_.currentTime(0); | ||||
|   //} | ||||
|   this.player_.play(); | ||||
| }; | ||||
|   | ||||
| @@ -12,7 +12,9 @@ vjs.Button = vjs.Component.extend({ | ||||
|     vjs.Component.call(this, player, options); | ||||
|  | ||||
|     var touchstart = false; | ||||
|     this.on('touchstart', function() { | ||||
|     this.on('touchstart', function(event) { | ||||
|       // Stop click and other mouse events from triggering also | ||||
|       event.preventDefault(); | ||||
|       touchstart = true; | ||||
|     }); | ||||
|     this.on('touchmove', function() { | ||||
| @@ -24,7 +26,6 @@ vjs.Button = vjs.Component.extend({ | ||||
|         self.onClick(event); | ||||
|       } | ||||
|       event.preventDefault(); | ||||
|       event.stopPropagation(); | ||||
|     }); | ||||
|  | ||||
|     this.on('click', this.onClick); | ||||
|   | ||||
| @@ -539,26 +539,6 @@ vjs.Component.prototype.hide = function(){ | ||||
|   return this; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Fade a component in using CSS | ||||
|  * @return {vjs.Component} | ||||
|  */ | ||||
| vjs.Component.prototype.fadeIn = function(){ | ||||
|   this.removeClass('vjs-fade-out'); | ||||
|   this.addClass('vjs-fade-in'); | ||||
|   return this; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Fade a component out using CSS | ||||
|  * @return {vjs.Component} | ||||
|  */ | ||||
| vjs.Component.prototype.fadeOut = function(){ | ||||
|   this.removeClass('vjs-fade-in'); | ||||
|   this.addClass('vjs-fade-out'); | ||||
|   return this; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Lock an item in its visible state. To be used with fadeIn/fadeOut. | ||||
|  * @return {vjs.Component} | ||||
| @@ -583,15 +563,8 @@ vjs.Component.prototype.unlockShowing = function(){ | ||||
| vjs.Component.prototype.disable = function(){ | ||||
|   this.hide(); | ||||
|   this.show = function(){}; | ||||
|   this.fadeIn = function(){}; | ||||
| }; | ||||
|  | ||||
| // TODO: Get enable working | ||||
| // vjs.Component.prototype.enable = function(){ | ||||
| //   this.fadeIn = vjs.Component.prototype.fadeIn; | ||||
| //   this.show = vjs.Component.prototype.show; | ||||
| // }; | ||||
|  | ||||
| /** | ||||
|  * If a value is provided it will change the width of the player to that value | ||||
|  * otherwise the width is returned | ||||
| @@ -693,3 +666,50 @@ vjs.Component.prototype.dimension = function(widthOrHeight, num, skipListeners){ | ||||
|     // } | ||||
|   } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Emit 'tap' events when touch events are supported. We're requireing them to | ||||
|  * be enabled because otherwise every component would have this extra overhead | ||||
|  * unnecessarily, on mobile devices where extra overhead is especially bad. | ||||
|  * | ||||
|  * This is being implemented so we can support taps on the video element | ||||
|  * toggling the controls. | ||||
|  */ | ||||
| vjs.Component.prototype.emitTapEvents = function(){ | ||||
|   var touchStart, touchTime, couldBeTap, noTap; | ||||
|  | ||||
|   // Track the start time so we can determine how long the touch lasted | ||||
|   touchStart = 0; | ||||
|  | ||||
|   this.on('touchstart', function(event) { | ||||
|     // Record start time so we can detect a tap vs. "touch and hold" | ||||
|     touchStart = new Date().getTime(); | ||||
|     // Reset couldBeTap tracking | ||||
|     couldBeTap = true; | ||||
|   }); | ||||
|  | ||||
|   noTap = function(){ | ||||
|     couldBeTap = false; | ||||
|   }; | ||||
|   // TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s | ||||
|   this.on('touchmove', noTap); | ||||
|   this.on('touchleave', noTap); | ||||
|   this.on('touchcancel', noTap); | ||||
|  | ||||
|   // When the touch ends, measure how long it took and trigger the appropriate | ||||
|   // event | ||||
|   this.on('touchend', function() { | ||||
|     // Proceed only if the touchmove/leave/cancel event didn't happen | ||||
|     if (couldBeTap === true) { | ||||
|       // Measure how long the touch lasted | ||||
|       touchTime = new Date().getTime() - touchStart; | ||||
|       // The touch needs to be quick in order to consider it a tap | ||||
|       if (touchTime < 250) { | ||||
|         this.trigger('tap'); | ||||
|         // It may be good to copy the touchend event object and change the | ||||
|         // type to tap, if the other event properties aren't exact after | ||||
|         // vjs.fixEvent runs (e.g. event.target) | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
|   | ||||
| @@ -4,55 +4,7 @@ | ||||
|  * @param {Object=} options | ||||
|  * @constructor | ||||
|  */ | ||||
| vjs.ControlBar = vjs.Component.extend({ | ||||
|   /** @constructor */ | ||||
|   init: function(player, options){ | ||||
|     vjs.Component.call(this, player, options); | ||||
|  | ||||
|     if (!player.controls()) { | ||||
|       this.disable(); | ||||
|     } | ||||
|  | ||||
|     player.one('play', vjs.bind(this, function(){ | ||||
|       var touchstart, | ||||
|         fadeIn = vjs.bind(this, this.fadeIn), | ||||
|         fadeOut = vjs.bind(this, this.fadeOut); | ||||
|  | ||||
|       this.fadeIn(); | ||||
|  | ||||
|       if ( !('ontouchstart' in window) ) { | ||||
|         this.player_.on('mouseover', fadeIn); | ||||
|         this.player_.on('mouseout', fadeOut); | ||||
|         this.player_.on('pause', vjs.bind(this, this.lockShowing)); | ||||
|         this.player_.on('play', vjs.bind(this, this.unlockShowing)); | ||||
|       } | ||||
|  | ||||
|       touchstart = false; | ||||
|       this.player_.on('touchstart', function() { | ||||
|         touchstart = true; | ||||
|       }); | ||||
|       this.player_.on('touchmove', function() { | ||||
|         touchstart = false; | ||||
|       }); | ||||
|       this.player_.on('touchend', vjs.bind(this, function(event) { | ||||
|         var idx; | ||||
|         if (touchstart) { | ||||
|           idx = this.el().className.search('fade-in'); | ||||
|           if (idx !== -1) { | ||||
|             this.fadeOut(); | ||||
|           } else { | ||||
|             this.fadeIn(); | ||||
|           } | ||||
|         } | ||||
|         touchstart = false; | ||||
|  | ||||
|         if (!this.player_.paused()) { | ||||
|           event.preventDefault(); | ||||
|         } | ||||
|       })); | ||||
|     })); | ||||
|   } | ||||
| }); | ||||
| vjs.ControlBar = vjs.Component.extend(); | ||||
|  | ||||
| vjs.ControlBar.prototype.options_ = { | ||||
|   loadEvent: 'play', | ||||
| @@ -75,13 +27,3 @@ vjs.ControlBar.prototype.createEl = function(){ | ||||
|     className: 'vjs-control-bar' | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| vjs.ControlBar.prototype.fadeIn = function(){ | ||||
|   vjs.Component.prototype.fadeIn.call(this); | ||||
|   this.player_.trigger('controlsvisible'); | ||||
| }; | ||||
|  | ||||
| vjs.ControlBar.prototype.fadeOut = function(){ | ||||
|   vjs.Component.prototype.fadeOut.call(this); | ||||
|   this.player_.trigger('controlshidden'); | ||||
| }; | ||||
| @@ -55,14 +55,13 @@ goog.exportProperty(vjs.Component.prototype, 'width', vjs.Component.prototype.wi | ||||
| goog.exportProperty(vjs.Component.prototype, 'height', vjs.Component.prototype.height); | ||||
| goog.exportProperty(vjs.Component.prototype, 'dimensions', vjs.Component.prototype.dimensions); | ||||
| goog.exportProperty(vjs.Component.prototype, 'ready', vjs.Component.prototype.ready); | ||||
| goog.exportProperty(vjs.Component.prototype, 'fadeIn', vjs.Component.prototype.fadeIn); | ||||
| goog.exportProperty(vjs.Component.prototype, 'fadeOut', vjs.Component.prototype.fadeOut); | ||||
|  | ||||
| goog.exportSymbol('videojs.Player', vjs.Player); | ||||
| goog.exportProperty(vjs.Player.prototype, 'dispose', vjs.Player.prototype.dispose); | ||||
| goog.exportProperty(vjs.Player.prototype, 'requestFullScreen', vjs.Player.prototype.requestFullScreen); | ||||
| goog.exportProperty(vjs.Player.prototype, 'cancelFullScreen', vjs.Player.prototype.cancelFullScreen); | ||||
| goog.exportProperty(vjs.Player.prototype, 'bufferedPercent', vjs.Player.prototype.bufferedPercent); | ||||
| goog.exportProperty(vjs.Player.prototype, 'usingNativeControls', vjs.Player.prototype.usingNativeControls); | ||||
|  | ||||
| goog.exportSymbol('videojs.MediaLoader', vjs.MediaLoader); | ||||
| goog.exportSymbol('videojs.TextTrackDisplay', vjs.TextTrackDisplay); | ||||
|   | ||||
| @@ -343,6 +343,7 @@ vjs.IS_OLD_ANDROID = vjs.IS_ANDROID && (/webkit/i).test(vjs.USER_AGENT) && vjs.A | ||||
| vjs.IS_FIREFOX = (/Firefox/i).test(vjs.USER_AGENT); | ||||
| vjs.IS_CHROME = (/Chrome/i).test(vjs.USER_AGENT); | ||||
|  | ||||
| vjs.TOUCH_ENABLED = ('ontouchstart' in window); | ||||
|  | ||||
| /** | ||||
|  * Get an element's attribute values, as defined on the HTML tag | ||||
|   | ||||
| @@ -182,9 +182,6 @@ vjs.Flash = vjs.MediaTechController.extend({ | ||||
|           // Update reference to playback technology element | ||||
|           tech.el_ = el; | ||||
|  | ||||
|           // Now that the element is ready, make a click on the swf play the video | ||||
|           vjs.on(el, 'click', tech.bind(tech.onClick)); | ||||
|  | ||||
|           // Make sure swf is actually ready. Sometimes the API isn't actually yet. | ||||
|           vjs.Flash.checkReady(tech); | ||||
|         }); | ||||
| @@ -328,9 +325,6 @@ vjs.Flash['onReady'] = function(currSwf){ | ||||
|   // Update reference to playback technology element | ||||
|   tech.el_ = el; | ||||
|  | ||||
|   // Now that the element is ready, make a click on the swf play the video | ||||
|   tech.on('click', tech.onClick); | ||||
|  | ||||
|   vjs.Flash.checkReady(tech); | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -35,6 +35,14 @@ vjs.Html5 = vjs.MediaTechController.extend({ | ||||
|       this.el_.src = source.src; | ||||
|     } | ||||
|  | ||||
|     // Determine if native controls should be used | ||||
|     // Our goal should be to get the custom controls on mobile solid everywhere | ||||
|     // so we can remove this all together. Right now this will block custom | ||||
|     // controls on touch enabled laptops like the Chrome Pixel | ||||
|     if (vjs.TOUCH_ENABLED && player.options()['nativeControlsForTouch'] !== false) { | ||||
|       this.useNativeControls(); | ||||
|     } | ||||
|  | ||||
|     // Chrome and Safari both have issues with autoplay. | ||||
|     // In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work. | ||||
|     // In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays) | ||||
| @@ -46,10 +54,7 @@ vjs.Html5 = vjs.MediaTechController.extend({ | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     this.on('click', this.onClick); | ||||
|  | ||||
|     this.setupTriggers(); | ||||
|  | ||||
|     this.triggerReady(); | ||||
|   } | ||||
| }); | ||||
| @@ -116,6 +121,37 @@ vjs.Html5.prototype.eventHandler = function(e){ | ||||
|   e.stopPropagation(); | ||||
| }; | ||||
|  | ||||
| vjs.Html5.prototype.useNativeControls = function(){ | ||||
|   var tech, player, controlsOn, controlsOff, cleanUp; | ||||
|  | ||||
|   tech = this; | ||||
|   player = this.player(); | ||||
|  | ||||
|   // If the player controls are enabled turn on the native controls | ||||
|   tech.setControls(player.controls()); | ||||
|  | ||||
|   // Update the native controls when player controls state is updated | ||||
|   controlsOn = function(){ | ||||
|     tech.setControls(true); | ||||
|   }; | ||||
|   controlsOff = function(){ | ||||
|     tech.setControls(false); | ||||
|   }; | ||||
|   player.on('controlsenabled', controlsOn); | ||||
|   player.on('controlsdisabled', controlsOff); | ||||
|  | ||||
|   // Clean up when not using native controls anymore | ||||
|   cleanUp = function(){ | ||||
|     player.off('controlsenabled', controlsOn); | ||||
|     player.off('controlsdisabled', controlsOff); | ||||
|   }; | ||||
|   tech.on('dispose', cleanUp); | ||||
|   player.on('usingcustomcontrols', cleanUp); | ||||
|  | ||||
|   // Update the state of the player to using native controls | ||||
|   player.usingNativeControls(true); | ||||
| }; | ||||
|  | ||||
|  | ||||
| vjs.Html5.prototype.play = function(){ this.el_.play(); }; | ||||
| vjs.Html5.prototype.pause = function(){ this.el_.pause(); }; | ||||
| @@ -179,29 +215,19 @@ vjs.Html5.prototype.currentSrc = function(){ return this.el_.currentSrc; }; | ||||
|  | ||||
| vjs.Html5.prototype.preload = function(){ return this.el_.preload; }; | ||||
| vjs.Html5.prototype.setPreload = function(val){ this.el_.preload = val; }; | ||||
|  | ||||
| vjs.Html5.prototype.autoplay = function(){ return this.el_.autoplay; }; | ||||
| vjs.Html5.prototype.setAutoplay = function(val){ this.el_.autoplay = val; }; | ||||
|  | ||||
| vjs.Html5.prototype.controls = function(){ return this.el_.controls; } | ||||
| vjs.Html5.prototype.setControls = function(val){ this.el_.controls = !!val; } | ||||
|  | ||||
| vjs.Html5.prototype.loop = function(){ return this.el_.loop; }; | ||||
| vjs.Html5.prototype.setLoop = function(val){ this.el_.loop = val; }; | ||||
|  | ||||
| vjs.Html5.prototype.error = function(){ return this.el_.error; }; | ||||
|   // networkState: function(){ return this.el_.networkState; }, | ||||
|   // readyState: function(){ return this.el_.readyState; }, | ||||
| vjs.Html5.prototype.seeking = function(){ return this.el_.seeking; }; | ||||
|   // initialTime: function(){ return this.el_.initialTime; }, | ||||
|   // startOffsetTime: function(){ return this.el_.startOffsetTime; }, | ||||
|   // played: function(){ return this.el_.played; }, | ||||
|   // seekable: function(){ return this.el_.seekable; }, | ||||
| vjs.Html5.prototype.ended = function(){ return this.el_.ended; }; | ||||
|   // videoTracks: function(){ return this.el_.videoTracks; }, | ||||
|   // audioTracks: function(){ return this.el_.audioTracks; }, | ||||
|   // videoWidth: function(){ return this.el_.videoWidth; }, | ||||
|   // videoHeight: function(){ return this.el_.videoHeight; }, | ||||
|   // textTracks: function(){ return this.el_.textTracks; }, | ||||
|   // defaultPlaybackRate: function(){ return this.el_.defaultPlaybackRate; }, | ||||
|   // playbackRate: function(){ return this.el_.playbackRate; }, | ||||
|   // mediaGroup: function(){ return this.el_.mediaGroup; }, | ||||
|   // controller: function(){ return this.el_.controller; }, | ||||
| vjs.Html5.prototype.defaultMuted = function(){ return this.el_.defaultMuted; }; | ||||
|  | ||||
| /* HTML5 Support Testing ---------------------------------------------------- */ | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| /** | ||||
|  * @fileoverview Media Technology Controller - Base class for media playback technology controllers like Flash and HTML5 | ||||
|  * @fileoverview Media Technology Controller - Base class for media playback | ||||
|  * technology controllers like Flash and HTML5 | ||||
|  */ | ||||
|  | ||||
| /** | ||||
| @@ -13,37 +14,144 @@ vjs.MediaTechController = vjs.Component.extend({ | ||||
|   init: function(player, options, ready){ | ||||
|     vjs.Component.call(this, player, options, ready); | ||||
|  | ||||
|     // Make playback element clickable | ||||
|     // this.addEvent('click', this.proxy(this.onClick)); | ||||
|  | ||||
|     // player.triggerEvent('techready'); | ||||
|     this.initControlsListeners(); | ||||
|   } | ||||
| }); | ||||
|  | ||||
| // destroy: function(){}, | ||||
| // createElement: function(){}, | ||||
| /** | ||||
|  * Set up click and touch listeners for the playback element | ||||
|  * On desktops, a click on the video itself will toggle playback, | ||||
|  * on a mobile device a click on the video toggles controls. | ||||
|  * (toggling controls is done by toggling the user state between active and | ||||
|  * inactive) | ||||
|  * | ||||
|  * A tap can signal that a user has become active, or has become inactive | ||||
|  * e.g. a quick tap on an iPhone movie should reveal the controls. Another | ||||
|  * quick tap should hide them again (signaling the user is in an inactive | ||||
|  * viewing state) | ||||
|  * | ||||
|  * In addition to this, we still want the user to be considered inactive after | ||||
|  * a few seconds of inactivity. | ||||
|  * | ||||
|  * Note: the only part of iOS interaction we can't mimic with this setup | ||||
|  * is a touch and hold on the video element counting as activity in order to | ||||
|  * keep the controls showing, but that shouldn't be an issue. A touch and hold on | ||||
|  * any controls will still keep the user active | ||||
|  */ | ||||
| vjs.MediaTechController.prototype.initControlsListeners = function(){ | ||||
|   var player, tech, activateControls, deactivateControls; | ||||
|  | ||||
|   tech = this; | ||||
|   player = this.player(); | ||||
|  | ||||
|   var activateControls = function(){ | ||||
|     if (player.controls() && !player.usingNativeControls()) { | ||||
|       tech.addControlsListeners(); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   deactivateControls = vjs.bind(tech, tech.removeControlsListeners); | ||||
|  | ||||
|   // Set up event listeners once the tech is ready and has an element to apply | ||||
|   // listeners to | ||||
|   this.ready(activateControls); | ||||
|   player.on('controlsenabled', activateControls); | ||||
|   player.on('controlsdisabled', deactivateControls); | ||||
| }; | ||||
|  | ||||
| vjs.MediaTechController.prototype.addControlsListeners = function(){ | ||||
|   var preventBubble, userWasActive; | ||||
|  | ||||
|   // Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do | ||||
|   // trigger mousedown/up. | ||||
|   // http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object | ||||
|   // Any touch events are set to block the mousedown event from happening | ||||
|   this.on('mousedown', this.onClick); | ||||
|  | ||||
|   // We need to block touch events on the video element from bubbling up, | ||||
|   // otherwise they'll signal activity prematurely. The specific use case is | ||||
|   // when the video is playing and the controls have faded out. In this case | ||||
|   // only a tap (fast touch) should toggle the user active state and turn the | ||||
|   // controls back on. A touch and move or touch and hold should not trigger | ||||
|   // the controls (per iOS as an example at least) | ||||
|   // | ||||
|   // We always want to stop propagation on touchstart because touchstart | ||||
|   // at the player level starts the touchInProgress interval. We can still | ||||
|   // report activity on the other events, but won't let them bubble for | ||||
|   // consistency. We don't want to bubble a touchend without a touchstart. | ||||
|   this.on('touchstart', function(event) { | ||||
|     // Stop the mouse events from also happening | ||||
|     event.preventDefault(); | ||||
|     event.stopPropagation(); | ||||
|     // Record if the user was active now so we don't have to keep polling it | ||||
|     userWasActive = this.player_.userActive(); | ||||
|   }); | ||||
|  | ||||
|   preventBubble = function(event){ | ||||
|     event.stopPropagation(); | ||||
|     if (userWasActive) { | ||||
|       this.player_.reportUserActivity(); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   // Treat all touch events the same for consistency | ||||
|   this.on('touchmove', preventBubble); | ||||
|   this.on('touchleave', preventBubble); | ||||
|   this.on('touchcancel', preventBubble); | ||||
|   this.on('touchend', preventBubble); | ||||
|  | ||||
|   // Turn on component tap events | ||||
|   this.emitTapEvents(); | ||||
|  | ||||
|   // The tap listener needs to come after the touchend listener because the tap | ||||
|   // listener cancels out any reportedUserActivity when setting userActive(false) | ||||
|   this.on('tap', this.onTap); | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Handle a click on the media element. By default will play the media. | ||||
|  * | ||||
|  * On android browsers, having this toggle play state interferes with being | ||||
|  * able to toggle the controls and toggling play state with the play button | ||||
|  * Remove the listeners used for click and tap controls. This is needed for | ||||
|  * toggling to controls disabled, where a tap/touch should do nothing. | ||||
|  */ | ||||
| vjs.MediaTechController.prototype.onClick = (function(){ | ||||
|   if (vjs.IS_ANDROID) { | ||||
|     return function () {}; | ||||
|   } else { | ||||
|     return function () { | ||||
|       if (this.player_.controls()) { | ||||
|         if (this.player_.paused()) { | ||||
|           this.player_.play(); | ||||
|         } else { | ||||
|           this.player_.pause(); | ||||
|         } | ||||
|       } | ||||
|     }; | ||||
| vjs.MediaTechController.prototype.removeControlsListeners = function(){ | ||||
|   // We don't want to just use `this.off()` because there might be other needed | ||||
|   // listeners added by techs that extend this. | ||||
|   this.off('tap'); | ||||
|   this.off('touchstart'); | ||||
|   this.off('touchmove'); | ||||
|   this.off('touchleave'); | ||||
|   this.off('touchcancel'); | ||||
|   this.off('touchend'); | ||||
|   this.off('click'); | ||||
|   this.off('mousedown'); | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Handle a click on the media element. By default will play/pause the media. | ||||
|  */ | ||||
| vjs.MediaTechController.prototype.onClick = function(event){ | ||||
|   // We're using mousedown to detect clicks thanks to Flash, but mousedown | ||||
|   // will also be triggered with right-clicks, so we need to prevent that | ||||
|   if (event.button !== 0) return; | ||||
|  | ||||
|   // When controls are disabled a click should not toggle playback because | ||||
|   // the click is considered a control | ||||
|   if (this.player().controls()) { | ||||
|     if (this.player().paused()) { | ||||
|       this.player().play(); | ||||
|     } else { | ||||
|       this.player().pause(); | ||||
|     } | ||||
|   } | ||||
| })(); | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Handle a tap on the media element. By default it will toggle the user | ||||
|  * activity state, which hides and shows the controls. | ||||
|  */ | ||||
|  | ||||
| vjs.MediaTechController.prototype.onTap = function(){ | ||||
|   this.player().userActive(!this.player().userActive()); | ||||
| }; | ||||
|  | ||||
| vjs.MediaTechController.prototype.features = { | ||||
|   volumeControl: true, | ||||
|   | ||||
							
								
								
									
										212
									
								
								src/js/player.js
									
									
									
									
									
								
							
							
						
						
									
										212
									
								
								src/js/player.js
									
									
									
									
									
								
							| @@ -24,22 +24,30 @@ vjs.Player = vjs.Component.extend({ | ||||
|     this.poster_ = options['poster']; | ||||
|     // Set controls | ||||
|     this.controls_ = options['controls']; | ||||
|     // Use native controls for iOS and Android by default | ||||
|     //  until controls are more stable on those devices. | ||||
|     if (options['customControlsOnMobile'] !== true && (vjs.IS_IOS || vjs.IS_ANDROID)) { | ||||
|       tag.controls = options['controls']; | ||||
|       this.controls_ = false; | ||||
|     } else { | ||||
|       // Original tag settings stored in options | ||||
|       // now remove immediately so native controls don't flash. | ||||
|       tag.controls = false; | ||||
|     } | ||||
|     // Original tag settings stored in options | ||||
|     // now remove immediately so native controls don't flash. | ||||
|     // May be turned back on by HTML5 tech if nativeControlsForTouch is true | ||||
|     tag.controls = false; | ||||
|  | ||||
|     // Run base component initializing with new options. | ||||
|     // Builds the element through createEl() | ||||
|     // Inits and embeds any child components in opts | ||||
|     vjs.Component.call(this, this, options, ready); | ||||
|  | ||||
|     // Update controls className. Can't do this when the controls are initially | ||||
|     // set because the element doesn't exist yet. | ||||
|     if (this.controls()) { | ||||
|       this.addClass('vjs-controls-enabled'); | ||||
|     } else { | ||||
|       this.addClass('vjs-controls-disabled'); | ||||
|     } | ||||
|  | ||||
|     // TODO: Make this smarter. Toggle user state between touching/mousing | ||||
|     // using events, since devices can have both touch and mouse events. | ||||
|     // if (vjs.TOUCH_ENABLED) { | ||||
|     //   this.addClass('vjs-touch-enabled'); | ||||
|     // } | ||||
|  | ||||
|     // Firstplay event implimentation. Not sold on the event yet. | ||||
|     // Could probably just check currentTime==0? | ||||
|     this.one('play', function(e){ | ||||
| @@ -71,6 +79,8 @@ vjs.Player = vjs.Component.extend({ | ||||
|         this[key](val); | ||||
|       }, this); | ||||
|     } | ||||
|  | ||||
|     this.listenForUserActivity(); | ||||
|   } | ||||
| }); | ||||
|  | ||||
| @@ -377,6 +387,8 @@ vjs.Player.prototype.onFirstPlay = function(){ | ||||
|     if(this.options_['starttime']){ | ||||
|       this.currentTime(this.options_['starttime']); | ||||
|     } | ||||
|  | ||||
|     this.addClass('vjs-has-started'); | ||||
| }; | ||||
|  | ||||
| vjs.Player.prototype.onPause = function(){ | ||||
| @@ -848,20 +860,188 @@ vjs.Player.prototype.controls_; | ||||
|  * @param  {Boolean} controls Set controls to showing or not | ||||
|  * @return {Boolean}    Controls are showing | ||||
|  */ | ||||
| vjs.Player.prototype.controls = function(controls){ | ||||
|   if (controls !== undefined) { | ||||
| vjs.Player.prototype.controls = function(bool){ | ||||
|   if (bool !== undefined) { | ||||
|     bool = !!bool; // force boolean | ||||
|     // Don't trigger a change event unless it actually changed | ||||
|     if (this.controls_ !== controls) { | ||||
|       this.controls_ = !!controls; // force boolean | ||||
|       this.trigger('controlschange'); | ||||
|     if (this.controls_ !== bool) { | ||||
|       this.controls_ = bool; | ||||
|       if (bool) { | ||||
|         this.removeClass('vjs-controls-disabled'); | ||||
|         this.addClass('vjs-controls-enabled'); | ||||
|         this.trigger('controlsenabled'); | ||||
|       } else { | ||||
|         this.removeClass('vjs-controls-enabled'); | ||||
|         this.addClass('vjs-controls-disabled'); | ||||
|         this.trigger('controlsdisabled'); | ||||
|       } | ||||
|     } | ||||
|     return this; | ||||
|   } | ||||
|   return this.controls_; | ||||
| }; | ||||
|  | ||||
| vjs.Player.prototype.usingNativeControls_; | ||||
|  | ||||
| /** | ||||
|  * Toggle native controls on/off. Native controls are the controls built into | ||||
|  * devices (e.g. default iPhone controls), Flash, or other techs | ||||
|  * (e.g. Vimeo Controls) | ||||
|  * | ||||
|  * **This should only be set by the current tech, because only the tech knows | ||||
|  * if it can support native controls** | ||||
|  * | ||||
|  * @param  {Boolean} bool    True signals that native controls are on | ||||
|  * @return {vjs.Player}      Returns the player | ||||
|  */ | ||||
| vjs.Player.prototype.usingNativeControls = function(bool){ | ||||
|   if (bool !== undefined) { | ||||
|     bool = !!bool; // force boolean | ||||
|     // Don't trigger a change event unless it actually changed | ||||
|     if (this.usingNativeControls_ !== bool) { | ||||
|       this.usingNativeControls_ = bool; | ||||
|       if (bool) { | ||||
|         this.addClass('vjs-using-native-controls'); | ||||
|         this.trigger('usingnativecontrols'); | ||||
|       } else { | ||||
|         this.removeClass('vjs-using-native-controls'); | ||||
|         this.trigger('usingcustomcontrols'); | ||||
|       } | ||||
|     } | ||||
|     return this; | ||||
|   } | ||||
|   return this.usingNativeControls_; | ||||
| }; | ||||
|  | ||||
| vjs.Player.prototype.error = function(){ return this.techGet('error'); }; | ||||
| vjs.Player.prototype.ended = function(){ return this.techGet('ended'); }; | ||||
|  | ||||
| // When the player is first initialized, trigger activity so components | ||||
| // like the control bar show themselves if needed | ||||
| vjs.Player.prototype.userActivity_ = true; | ||||
| vjs.Player.prototype.reportUserActivity = function(event){ | ||||
|   this.userActivity_ = true; | ||||
| }; | ||||
|  | ||||
| vjs.Player.prototype.userActive_ = true; | ||||
| vjs.Player.prototype.userActive = function(bool){ | ||||
|   if (bool !== undefined) { | ||||
|     bool = !!bool; | ||||
|     if (bool !== this.userActive_) { | ||||
|       this.userActive_ = bool; | ||||
|       if (bool) { | ||||
|         // If the user was inactive and is now active we want to reset the | ||||
|         // inactivity timer | ||||
|         this.userActivity_ = true; | ||||
|         this.removeClass('vjs-user-inactive'); | ||||
|         this.addClass('vjs-user-active'); | ||||
|         this.trigger('useractive'); | ||||
|       } else { | ||||
|         // We're switching the state to inactive manually, so erase any other | ||||
|         // activity | ||||
|         this.userActivity_ = false; | ||||
|  | ||||
|         // Chrome/Safari/IE have bugs where when you change the cursor it can | ||||
|         // trigger a mousemove event. This causes an issue when you're hiding | ||||
|         // the cursor when the user is inactive, and a mousemove signals user | ||||
|         // activity. Making it impossible to go into inactive mode. Specifically | ||||
|         // this happens in fullscreen when we really need to hide the cursor. | ||||
|         // | ||||
|         // When this gets resolved in ALL browsers it can be removed | ||||
|         // https://code.google.com/p/chromium/issues/detail?id=103041 | ||||
|         this.tech.one('mousemove', function(e){ | ||||
|           e.stopPropagation(); | ||||
|           e.preventDefault(); | ||||
|         }); | ||||
|         this.removeClass('vjs-user-active'); | ||||
|         this.addClass('vjs-user-inactive'); | ||||
|         this.trigger('userinactive'); | ||||
|       } | ||||
|     } | ||||
|     return this; | ||||
|   } | ||||
|   return this.userActive_; | ||||
| }; | ||||
|  | ||||
| vjs.Player.prototype.listenForUserActivity = function(){ | ||||
|   var onMouseActivity, onMouseDown, mouseInProgress, onMouseUp, | ||||
|       activityCheck, inactivityTimeout; | ||||
|  | ||||
|   onMouseActivity = this.reportUserActivity; | ||||
|  | ||||
|   onMouseDown = function() { | ||||
|     onMouseActivity(); | ||||
|     // For as long as the they are touching the device or have their mouse down, | ||||
|     // we consider them active even if they're not moving their finger or mouse. | ||||
|     // So we want to continue to update that they are active | ||||
|     clearInterval(mouseInProgress); | ||||
|     // Setting userActivity=true now and setting the interval to the same time | ||||
|     // as the activityCheck interval (250) should ensure we never miss the | ||||
|     // next activityCheck | ||||
|     mouseInProgress = setInterval(vjs.bind(this, onMouseActivity), 250); | ||||
|   }; | ||||
|  | ||||
|   onMouseUp = function(event) { | ||||
|     onMouseActivity(); | ||||
|     // Stop the interval that maintains activity if the mouse/touch is down | ||||
|     clearInterval(mouseInProgress); | ||||
|   }; | ||||
|  | ||||
|   // Any mouse movement will be considered user activity | ||||
|   this.on('mousedown', onMouseDown); | ||||
|   this.on('mousemove', onMouseActivity); | ||||
|   this.on('mouseup', onMouseUp); | ||||
|  | ||||
|   // Listen for keyboard navigation | ||||
|   // Shouldn't need to use inProgress interval because of key repeat | ||||
|   this.on('keydown', onMouseActivity); | ||||
|   this.on('keyup', onMouseActivity); | ||||
|  | ||||
|   // Consider any touch events that bubble up to be activity | ||||
|   // Certain touches on the tech will be blocked from bubbling because they | ||||
|   // toggle controls | ||||
|   this.on('touchstart', onMouseDown); | ||||
|   this.on('touchmove', onMouseActivity); | ||||
|   this.on('touchend', onMouseUp); | ||||
|   this.on('touchcancel', onMouseUp); | ||||
|  | ||||
|   // Run an interval every 250 milliseconds instead of stuffing everything into | ||||
|   // the mousemove/touchmove function itself, to prevent performance degradation. | ||||
|   // `this.reportUserActivity` simply sets this.userActivity_ to true, which | ||||
|   // then gets picked up by this loop | ||||
|   // http://ejohn.org/blog/learning-from-twitter/ | ||||
|   activityCheck = setInterval(vjs.bind(this, function() { | ||||
|     // Check to see if mouse/touch activity has happened | ||||
|     if (this.userActivity_) { | ||||
|       // Reset the activity tracker | ||||
|       this.userActivity_ = false; | ||||
|  | ||||
|       // If the user state was inactive, set the state to active | ||||
|       this.userActive(true); | ||||
|  | ||||
|       // Clear any existing inactivity timeout to start the timer over | ||||
|       clearTimeout(inactivityTimeout); | ||||
|  | ||||
|       // In X seconds, if no more activity has occurred the user will be | ||||
|       // considered inactive | ||||
|       inactivityTimeout = setTimeout(vjs.bind(this, function() { | ||||
|         // Protect against the case where the inactivityTimeout can trigger just | ||||
|         // before the next user activity is picked up by the activityCheck loop | ||||
|         // causing a flicker | ||||
|         if (!this.userActivity_) { | ||||
|           this.userActive(false); | ||||
|         } | ||||
|       }), 2000); | ||||
|     } | ||||
|   }), 250); | ||||
|  | ||||
|   // Clean up the intervals when we kill the player | ||||
|   this.on('dispose', function(){ | ||||
|     clearInterval(activityCheck); | ||||
|     clearTimeout(inactivityTimeout); | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| // Methods to add support for | ||||
| // networkState: function(){ return this.techCall('networkState'); }, | ||||
| // readyState: function(){ return this.techCall('readyState'); }, | ||||
| @@ -929,3 +1109,5 @@ vjs.Player.prototype.ended = function(){ return this.techGet('ended'); }; | ||||
|   } | ||||
|  | ||||
| })(); | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -40,5 +40,8 @@ vjs.PosterImage.prototype.createEl = function(){ | ||||
| }; | ||||
|  | ||||
| vjs.PosterImage.prototype.onClick = function(){ | ||||
|   this.player_.play(); | ||||
|   // Only accept clicks when controls are enabled | ||||
|   if (this.player().controls()) { | ||||
|     this.player_.play(); | ||||
|   } | ||||
| }; | ||||
|   | ||||
| @@ -25,6 +25,7 @@ test('should be able to access expected player API methods', function() { | ||||
|   ok(player.one, 'one exists'); | ||||
|   ok(player.bufferedPercent, 'bufferedPercent exists'); | ||||
|   ok(player.dimensions, 'dimensions exists'); | ||||
|   ok(player.usingNativeControls, 'usingNativeControls exists'); | ||||
|  | ||||
|   player.dispose(); | ||||
| }); | ||||
| @@ -66,7 +67,7 @@ test('videojs.players should be availble after minification', function() { | ||||
|   fixture.appendChild(videoTag); | ||||
|  | ||||
|   var player = videojs(id); | ||||
|   equal(videojs.players[id], player, 'videojs.players is available'); | ||||
|   ok(videojs.players[id] === player, 'videojs.players is available'); | ||||
|  | ||||
|   player.dispose(); | ||||
| }); | ||||
|   | ||||
| @@ -221,3 +221,29 @@ test('should use a defined content el for appending children', function(){ | ||||
|   ok(comp.el().childNodes[0]['id'] === 'contentEl', 'Content El should still exist'); | ||||
|   ok(comp.el().childNodes[0].childNodes[0] !== child.el(), 'Child el should be removed.'); | ||||
| }); | ||||
|  | ||||
| test('should emit a tap event', function(){ | ||||
|   expect(1); | ||||
|  | ||||
|   // Fake touch support. Real touch support isn't needed for this test. | ||||
|   var origTouch = vjs.TOUCH_ENABLED; | ||||
|   vjs.TOUCH_ENABLED = true; | ||||
|  | ||||
|   var comp = new vjs.Component(getFakePlayer(), {}); | ||||
|  | ||||
|   comp.emitTapEvents(); | ||||
|   comp.on('tap', function(){ | ||||
|     ok(true, 'Tap event emitted'); | ||||
|   }); | ||||
|   comp.trigger('touchstart'); | ||||
|   comp.trigger('touchend'); | ||||
|  | ||||
|   // This second test should not trigger another tap event because | ||||
|   // a touchmove is happening | ||||
|   comp.trigger('touchstart'); | ||||
|   comp.trigger('touchmove'); | ||||
|   comp.trigger('touchend'); | ||||
|  | ||||
|   // Reset to orignial value | ||||
|   vjs.TOUCH_ENABLED = origTouch; | ||||
| }); | ||||
|   | ||||
							
								
								
									
										0
									
								
								test/unit/control-bar.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								test/unit/control-bar.js
									
									
									
									
									
										Normal file
									
								
							| @@ -21,10 +21,15 @@ test('should re-link the player if the tech is moved', function(){ | ||||
|   var player, tech, el; | ||||
|   el = document.createElement('div'); | ||||
|   el.innerHTML = '<div />'; | ||||
|  | ||||
|   player = { | ||||
|     id: function(){ return 'id'; }, | ||||
|     el: function(){ return el; }, | ||||
|     options_: {}, | ||||
|     options: function(){ return {}; }, | ||||
|     controls: function(){ return false; }, | ||||
|     usingNativeControls: function(){ return false; }, | ||||
|     on: function(){ return this; }, | ||||
|     ready: function(){} | ||||
|   }; | ||||
|   tech = new vjs.Html5(player, {}); | ||||
| @@ -32,4 +37,4 @@ test('should re-link the player if the tech is moved', function(){ | ||||
|   tech.createEl(); | ||||
|  | ||||
|   strictEqual(player, tech.el()['player']); | ||||
| }); | ||||
| }); | ||||
|   | ||||
| @@ -211,20 +211,29 @@ test('should be able to initialize player twice on the same tag using string ref | ||||
|   player.dispose(); | ||||
| }); | ||||
|  | ||||
| test('should set controls and trigger event', function() { | ||||
|   expect(3); | ||||
| test('should set controls and trigger events', function() { | ||||
|   expect(6); | ||||
|  | ||||
|   var player = PlayerTest.makePlayer({ 'controls': false }); | ||||
|   ok(player.controls() === false, 'controls set through options'); | ||||
|   var hasDisabledClass = player.el().className.indexOf('vjs-controls-disabled'); | ||||
|   ok(hasDisabledClass !== -1, 'Disabled class added to player'); | ||||
|  | ||||
|   player.controls(true); | ||||
|   ok(player.controls() === true, 'controls updated'); | ||||
|   var hasEnabledClass = player.el().className.indexOf('vjs-controls-enabled'); | ||||
|   ok(hasEnabledClass !== -1, 'Disabled class added to player'); | ||||
|  | ||||
|   player.on('controlschange', function(){ | ||||
|     ok(true, 'controlschange fired once'); | ||||
|   player.on('controlsenabled', function(){ | ||||
|     ok(true, 'enabled fired once'); | ||||
|   }); | ||||
|   player.on('controlsdisabled', function(){ | ||||
|     ok(true, 'disabled fired once'); | ||||
|   }); | ||||
|   player.controls(false); | ||||
|   // Check for unnecessary controlschange events | ||||
|   player.controls(false); | ||||
|   player.controls(true); | ||||
|   // Check for unnecessary events | ||||
|   player.controls(true); | ||||
|  | ||||
|   player.dispose(); | ||||
| }); | ||||
| @@ -247,3 +256,76 @@ test('should set controls and trigger event', function() { | ||||
| //   player.requestFullScreen(); | ||||
| // }); | ||||
|  | ||||
| test('should toggle user the user state between active and inactive', function(){ | ||||
|   var player = PlayerTest.makePlayer({}); | ||||
|  | ||||
|   expect(9); | ||||
|  | ||||
|   ok(player.userActive(), 'User should be active at player init'); | ||||
|  | ||||
|   player.on('userinactive', function(){ | ||||
|     ok(true, 'userinactive event triggered'); | ||||
|   }); | ||||
|  | ||||
|   player.on('useractive', function(){ | ||||
|     ok(true, 'useractive event triggered'); | ||||
|   }); | ||||
|  | ||||
|   player.userActive(false); | ||||
|   ok(player.userActive() === false, 'Player state changed to inactive'); | ||||
|   ok(player.el().className.indexOf('vjs-user-active') === -1, 'Active class removed'); | ||||
|   ok(player.el().className.indexOf('vjs-user-inactive') !== -1, 'Inactive class added'); | ||||
|  | ||||
|   player.userActive(true); | ||||
|   ok(player.userActive() === true, 'Player state changed to active'); | ||||
|   ok(player.el().className.indexOf('vjs-user-inactive') === -1, 'Inactive class removed'); | ||||
|   ok(player.el().className.indexOf('vjs-user-active') !== -1, 'Active class added'); | ||||
|  | ||||
|   player.dispose(); | ||||
| }); | ||||
|  | ||||
| test('should add a touch-enabled classname when touch is supported', function(){ | ||||
|   var player; | ||||
|  | ||||
|   expect(1); | ||||
|  | ||||
|   // Fake touch support. Real touch support isn't needed for this test. | ||||
|   var origTouch = vjs.TOUCH_ENABLED; | ||||
|   vjs.TOUCH_ENABLED = true; | ||||
|  | ||||
|   player = PlayerTest.makePlayer({}); | ||||
|  | ||||
|   ok(player.el().className.indexOf('vjs-touch-enabled'), 'touch-enabled classname added'); | ||||
|  | ||||
|  | ||||
|   vjs.TOUCH_ENABLED = origTouch; | ||||
|   player.dispose(); | ||||
| }); | ||||
|  | ||||
| test('should allow for tracking when native controls are used', function(){ | ||||
|   var player = PlayerTest.makePlayer({}); | ||||
|  | ||||
|   expect(6); | ||||
|  | ||||
|   // Make sure native controls is false before starting test | ||||
|   player.usingNativeControls(false); | ||||
|  | ||||
|   player.on('usingnativecontrols', function(){ | ||||
|     ok(true, 'usingnativecontrols event triggered'); | ||||
|   }); | ||||
|  | ||||
|   player.on('usingcustomcontrols', function(){ | ||||
|     ok(true, 'usingcustomcontrols event triggered'); | ||||
|   }); | ||||
|  | ||||
|   player.usingNativeControls(true); | ||||
|   ok(player.usingNativeControls() === true, 'Using native controls is true'); | ||||
|   ok(player.el().className.indexOf('vjs-using-native-controls') !== -1, 'Native controls class added'); | ||||
|  | ||||
|   player.usingNativeControls(false); | ||||
|   ok(player.usingNativeControls() === false, 'Using native controls is false'); | ||||
|   ok(player.el().className.indexOf('vjs-using-native-controls') === -1, 'Native controls class removed'); | ||||
|  | ||||
|   player.dispose(); | ||||
| }); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user