<commit_before>/**
* Msg text is inherited from embedPlayer
*/

( function( mw, $ ) {"use strict";
/**
* mw.PlayerControlBuilder object
*	@param the embedPlayer element we are targeting
*/
mw.PlayerControlBuilder = function( embedPlayer, options ) {
	return this.init( embedPlayer, options );
};

/**
 * ControlsBuilder prototype:
 */
mw.PlayerControlBuilder.prototype = {
	//Default Local values:

	// Parent css Class name
	playerClass : 'mv-player',

	// Long string display of time value
	longTimeDisp: true,

	// Default volume layout is "vertical"
	volumeLayout : 'vertical',

	// Default control bar height
	height: mw.getConfig( 'EmbedPlayer.ControlsHeight' ),

	// Default supported components is merged with embedPlayer set of supported types
	supportedComponents: {
		// All playback types support options
		'options': true
	},

	// Default supported menu items is merged with skin menu items
	supportedMenuItems: {
		// Player Select
		'playerSelect' : true,

		// Download the file menu
		'download' : true,

		// Share the video menu
		'share' : true,

		// Player library link
		'aboutPlayerLibrary': true
	},

	// Flag to store the current fullscreen mode
	inFullScreen: false,

	// Flag for full window size players
	isWindowSizePlayer: false,

	// Flag to store if a warning binding has been added
	addWarningFlag: false,

	// Flag to store state of overlay on player
	displayOptionsMenuFlag: false,

	// Local storage of ControlBar Callback
	hideControlBarCallback: false,

	// Flag to store controls status (disabled/enabled)
	controlsDisabled: false,

	// binding postfix
	bindPostfix: '.controlBuilder',

	/**
	* Initialization Object for the control builder
	*
	* @param {Object} embedPlayer EmbedPlayer interface
	*/
	init: function( embedPlayer ) {
		var _this = this;
		this.embedPlayer = embedPlayer;

		// Check for skin overrides for controlBuilder
		var skinClass = embedPlayer.skinName.substr(0,1).toUpperCase() + embedPlayer.skinName.substr( 1 );
		if ( mw['PlayerSkin' + skinClass ] ) {

			// Clone as to not override prototype with the skin config
			var _this = $.extend( true, { }, this, mw['PlayerSkin' + skinClass ] );
			return _this;
		}
		// Return the controlBuilder Object:
		return this;
	},

	/**
	* Get the control bar height
	* @return {Number} control bar height
	*/
	getHeight: function(){
		return this.height;
	},


	/**
	* Add the controls html to player interface
	*/
	addControls: function() {
		// Set up local pointer to the embedPlayer
		var embedPlayer = this.embedPlayer;

		// Set up local controlBuilder
		var _this = this;

		// Remove any old controls & old overlays:
		embedPlayer.$interface.find( '.control-bar,.overlay-win' ).remove();

		// Reset flags:
		_this.displayOptionsMenuFlag = false;

		// Setup the controlBar container ( starts hidden )
		var $controlBar = $('<div />')
			.addClass( 'ui-state-default ui-widget-header ui-helper-clearfix control-bar' )
			.css( 'height', this.height );

		// Controls are hidden by default if overlaying controls:
		if( _this.isOverlayControls() ){
			$controlBar.hide();
		} else {
			// Include the control bar height when calculating the layout
			$controlBar.addClass('block');
		}

		$controlBar.css( {
			'position': 'absolute',
			'bottom' : '0px',
			'left' : '0px',
			'right' : '0px'
		} );

		// Make room for audio controls in the interface:
		if( embedPlayer.isAudio() && embedPlayer.$interface.height() == 0 ){
			embedPlayer.$interface.css( {
				'height' : this.height
			} );
		}

		// Add the controls to the interface
		embedPlayer.$interface.append( $controlBar );

        if ( $.browser.mozilla && parseFloat( $.browser.version ) < 2 ) {
			embedPlayer.triggerHelper( 'resizeIframeContainer', [ {'height' : embedPlayer.height + $controlBar.height() - 1} ] );
        }

		// Add the Controls Component
		this.addControlComponents();

		// Add top level Controls bindings
		this.addControlBindings();
	},

	/**
	* Add control components as defined per this.components
	*/
	addControlComponents: function( ) {
		var _this = this;

		// Set up local pointer to the embedPlayer
		var embedPlayer = this.embedPlayer;

		//Set up local var to control container:
		var $controlBar = embedPlayer.$interface.find( '.control-bar' );

		this.availableWidth = embedPlayer.getPlayerWidth();

		mw.log( 'PlayerControlsBuilder:: addControlComponents into:' + this.availableWidth );
		// Build the supportedComponents list
		this.supportedComponents = $.extend( this.supportedComponents, embedPlayer.supports );

		// Check for Attribution button
		if( mw.getConfig( 'EmbedPlayer.AttributionButton' ) && embedPlayer.attributionbutton ){
			this.supportedComponents[ 'attributionButton' ] = true;
		}
		// Check global fullscreen enabled flag
		if( mw.getConfig( 'EmbedPlayer.EnableFullscreen' ) === false ){
			this.supportedComponents[ 'fullscreen'] = false;
		}
		// Check if the options item is available
		if( mw.getConfig( 'EmbedPlayer.EnableOptionsMenu' ) === false ){
			this.supportedComponents[ 'options'] = false;
		}
		// Check for volume control
		if( mw.getConfig( 'EmbedPlayer.EnableVolumeControl') === false ){
			this.supportedComponents[ 'volumeControl'] = false;
		}

		// Check if we have multiple playable sources ( if only one source don't display source switch )
		if( embedPlayer.mediaElement.getPlayableSources().length == 1 ){
			this.supportedComponents[ 'sourceSwitch'] = false;
		}

		$( embedPlayer ).trigger( 'addControlBarComponent', this );

		var addComponent = function( componentId ){
			if ( _this.supportedComponents[ componentId ] ) {
				if ( _this.availableWidth > _this.components[ componentId ].w ) {
					// Append the component
					$controlBar.append(
						_this.getComponent( componentId )
					);
					_this.availableWidth -= _this.components[ componentId ].w;
					mw.log(" availableWidth:" + _this.availableWidth + ' ' + componentId + ' took: ' +  _this.components[ componentId ].w )
				} else {
					mw.log( 'PlayerControlBuilder:: Not enough space for control component:' + componentId );
				}
			}
		};

		// Output components
		for ( var componentId in this.components ) {
			// Check for (component === false ) and skip
			if( this.components[ componentId ] === false ){
				continue;
			}

			// Special case with playhead and time ( to make sure they are to the left of everything else )
			if ( componentId == 'playHead' || componentId == 'timeDisplay'){
				continue;
			}

			// Skip "fullscreen" button for assets or where height is 0px ( audio )
			if( componentId == 'fullscreen' && this.embedPlayer.isAudio() ){
				continue;
			}
			addComponent( componentId );
		}
		// Add special case remaining components:
		if( mw.getConfig( 'EmbedPlayer.EnableTimeDisplay' ) ){
			addComponent( 'timeDisplay' );
		}
		if( this.availableWidth > 30 ){
			addComponent( 'playHead' );
		}
		$(embedPlayer).trigger( 'controlBarBuildDone' );
	},

	/**
	* Get a window size for the player while preserving aspect ratio:
	*
	* @@TODO This has similar logic to mw.embedPlayerNative applyIntrinsicAspect we should look
	* at merging their functionality.
	*
	* @param {object} windowSize
	* 		object that set { 'width': {width}, 'height':{height} } of target window
	* @return {object}
	* 	 css settings for fullscreen player
	*/
	getAspectPlayerWindowCss: function( windowSize ) {
		var embedPlayer = this.embedPlayer;
		var _this = this;
		// Setup target height width based on max window size
		if( !windowSize ){
			var windowSize = {
				'width' : $( window ).width(),
				'height' : $( window ).height()
			};
		}
		windowSize.width = parseInt( windowSize.width );
		windowSize.height = parseInt( windowSize.height );
		// See if we need to leave space for control bar
		if( !_this.isOverlayControls() ){
			//targetHeight =  targetHeight - this.height;
			windowSize.height = windowSize.height - this.height;
		}

		// Set target width
		var targetWidth = windowSize.width;
		var targetHeight = targetWidth * ( 1 / _this.getIntrinsicAspect() );
		// Check if it exceeds the height constraint:
		if( targetHeight > windowSize.height ){
			targetHeight = windowSize.height;
			targetWidth = parseInt( targetHeight * _this.getIntrinsicAspect() );
		}
		var offsetTop = 0;
		//  Move the video down 1/2 of the difference of window height
		offsetTop+= ( targetHeight < windowSize.height )? ( windowSize.height- targetHeight ) / 2 : 0;
		// if the video is very tall in a short window adjust the size:
		var offsetLeft = ( targetWidth < windowSize.width )? parseInt( windowSize.width- targetWidth ) / 2 : 0;

		var position = (mw.isIOS4() && mw.isIphone()) ? 'static' : 'absolute';
		mw.log( 'PlayerControlBuilder::getAspectPlayerWindowCss: ' + ' h:' + targetHeight + ' w:' + targetWidth + ' t:' + offsetTop + ' l:' + offsetLeft );
		return {
			'position' : position,
			'height': parseInt( targetHeight ),
			'width' : parseInt( targetWidth ),
			'top' : parseInt( offsetTop ),
			'left': parseInt( offsetLeft)
		};
	},

	/**
	 * Get the intrinsic aspect ratio of media  ( width / height )
	 * @return {float}
	 * 			size object with width and height
	 */
	getIntrinsicAspect: function(){
		var vid = this.embedPlayer.getPlayerElement();
		// Check for raw intrinsic media size:
		if( vid && vid.videoWidth && vid.videoHeight ){
			return vid.videoWidth / vid.videoHeight;
		}

		// See if we have source data attributes available:
		if( this.embedPlayer.mediaElement &&
			this.embedPlayer.mediaElement.selectedSource )
		{
			var ss = this.embedPlayer.mediaElement.selectedSource;
			// See if we have a hardcoded aspect to the source ( Adaptive streams don't have width / height )
			if( ss.aspect ){
				return ss.aspect;
			}

			if( ss.width && ss.height ){
				return ss.width / ss.height
			}
		}

		// check for posterImage size: ( should have Intrinsic aspect size as well )
		var img = this.embedPlayer.$interface.find('.playerPoster')[0];
		if( img && img.naturalWidth && img.naturalHeight){
			return img.naturalWidth /  img.naturalHeight
		}

		// if all else fails use embedPlayer.getWidth()
		return this.embedPlayer.getWidth() / this.embedPlayer.getHeight()
	},

	/**
	* Get the play button css
	*/
	getPlayButtonPosition: function() {
		var _this = this;
		return {
			'position' : 'absolute',
			'left' : '50%',
			'top' : '50%',
			'margin-left' : - .5 * this.getComponentWidth( 'playButtonLarge' ),
			'margin-top' : - .5 * this.getComponentHeight( 'playButtonLarge' )
		};
	},

	/**
	 * Toggles full screen by calling
	 *  doFullScreenPlayer to enable fullscreen mode
	 *  restoreWindowPlayer to restore window mode
	 */
	toggleFullscreen: function( forceClose ) {
		var _this = this;
		// Do normal in-page fullscreen handling:
		if( this.inFullScreen ){
			this.restoreWindowPlayer();
		}else {
			this.doFullScreenPlayer();
		}
	},

	/**
	* Do full-screen mode
	*/
	doFullScreenPlayer: function( callback ) {
		mw.log("PlayerControlBuilder:: doFullScreenPlayer" );
		// Setup pointer to control builder :
		var _this = this;

		// Setup local reference to embed player:
		var embedPlayer = this.embedPlayer;

		// Setup a local reference to the player interface:
		var $interface = embedPlayer.$interface;
		// Check fullscreen state ( if already true do nothing )
		if( this.inFullScreen == true ){
			return ;
		}
		this.inFullScreen = true;

		// if overlaying controls add hide show player binding.
		if( _this.isOverlayControls() ){
			_this.addFullscreenMouseMoveHideShowControls();
		}

		// Store the current scroll location on the iframe:
		$( embedPlayer ).trigger( 'fullScreenStoreVerticalScroll' );

		// Check for native support for fullscreen and we are in an iframe server
		if( window.fullScreenApi.supportsFullScreen ) {
			_this.preFullscreenPlayerSize = this.getPlayerSize();
			var fullscreenHeight = null;
			var fsTarget = this.getFsTarget();

			var escapeFullscreen = function( event ) {
				if ( ! window.fullScreenApi.isFullScreen() ) {
					_this.restoreWindowPlayer();
				}
			}
			// remove any old binding: 
			fsTarget.removeEventListener(  fullScreenApi.fullScreenEventName, escapeFullscreen );
			// Add a binding to catch "escape" fullscreen
			fsTarget.addEventListener( fullScreenApi.fullScreenEventName, escapeFullscreen );
			// Make the iframe fullscreen:
			window.fullScreenApi.requestFullScreen( fsTarget );

			// There is a bug with mozfullscreenchange event in all versions of firefox with supportsFullScreen
			// https://bugzilla.mozilla.org/show_bug.cgi?id=724816
			// so we have to have an extra binding to check for size change and then restore.
			if( $.browser.mozilla ){
				_this.fullscreenRestoreCheck = setInterval( function(){
					if( fullscreenHeight && $(window).height() < fullscreenHeight ){
						// Mozilla triggered size change:
						clearInterval ( _this.fullscreenRestoreCheck );
						_this.restoreWindowPlayer();
					}
					// set fullscreen height:
					if( ! fullscreenHeight && _this.preFullscreenPlayerSize.height != $(window).height() ){
						fullscreenHeight = $(window).height();
					}
				}, 250 );
			}
		} else {
			// Check for hybrid html controls / native fullscreen support:
			var vid = this.embedPlayer.getPlayerElement();
			if( mw.getConfig('EmbedPlayer.EnableIpadNativeFullscreen')
					&&
				vid && vid.webkitSupportsFullscreen
			){
				this.doHybridNativeFullscreen();
				return ;
			} else {
				if( mw.getConfig('EmbedPlayer.IsIframeServer' ) ){
					this.doParentIframeFullscreen();
				} else {
					// do pseudo fullscren
					this.doFullScreenPlayerDom();
				}
			}
		}

		// Bind escape to restore in page clip ( IE9 needs a secondary escape binding )
		$( window ).keyup( function( event ) {
			// Escape check
			if( event.keyCode == 27 ){
				_this.restoreWindowPlayer();
			}
		} );

		// trigger the open fullscreen event:
		$( embedPlayer ).trigger( 'onOpenFullScreen' );
	},
	
	doParentIframeFullscreen: function(){
		var 
			_this = this,
			parentDoc = window['parent'].document,
			$parent = $( window['parent'].document ),
			$iframe = $( this.getFsTarget() ),
			parentContext = window['parent'];

		// update / reset local restore properties 
		this.verticalScrollPosition = (parentDoc.all ? parentDoc.scrollTop : parentContext.pageYOffset);
		this.parentsAbsoluteList = [];
		this.parentsRelativeList = [];
		
		// Set the original parent page scale if possible: 
		this.orginalParnetViewPortContent = $parent.find( 'meta[name="viewport"]' ).attr( 'content' );
		this.orginalParentIframeLayout = {
				'style' : $iframe[0].style.cssText,
				'width' : $iframe.attr('width'),
				'height' : $iframe.attr('height')
		}
		
		mw.log("PlayerControls:: doParentIframeFullscreen> verticalScrollPosition:" + this.verticalScrollPosition);
		parentContext.scroll(0, 0);

		// Make sure the parent page page has a zoom of 1:
		if( ! $parent.find('meta[name="viewport"]').length ){
			$parent.find('head').append( $( '<meta />' ).attr('name', 'viewport') );
		}
		$parent.find('meta[name="viewport"]').attr('content', 'initial-scale=1; maximum-scale=1; minimum-scale=1;' );

		// iPad 5 supports fixed position in a bad way, use absolute pos for iOS
		var playerCssPosition = ( mw.isIOS() ) ? 'absolute': 'fixed';
		
		// Remove absolute css of the iframe parents
		$iframe.parents().each( function() {
			var $parent = $( this );
			if( $parent.css( 'position' ) == 'absolute' ) {
				_this.parentsAbsoluteList.push( $parent );
				$parent.css( 'position', 'static' );
			}
			if( $parent.css( 'position' ) == 'relative' ) {
				_this.parentsRelativeList.push( $parent );
				$parent.css( 'position', 'static' );
			}
		});

		// Make the iframe fullscreen
		$iframe
			.css({
				'z-index': mw.getConfig( 'EmbedPlayer.FullScreenZIndex' ),
				'position': playerCssPosition,
				'top' : '0px',
				'left' : '0px',
				'width' : parentContext.innerWidth,
				'height' : parentContext.innerHeight,
				'margin': 0
			})
			.data(
				'isFullscreen', true
			);
		
		
		// Bind orientation change to resize player ( if fullscreen )
		$( parentContext ).bind( 'orientationchange', function(e){
			if( localIframeInFullscreen ){
				doFullscreen();
			}
		});

		// prevent scrolling when in fullscreen:
		document.ontouchmove = function( e ){
			if( localIframeInFullscreen ){
				e.preventDefault();
			}
		};
	},
	restoreParentIframeFullscreen: function(){
		var 
		_this = this,
		parentDoc = window['parent'].document,
		$parent = $( window['parent'].document ),
		$iframe = $( this.getFsTarget() ),
		parentContext = window['parent'];
		
		mw.log("PlayerControlBuilder:: restoreParentIframeFullscreen> verticalScrollPosition:" + this.verticalScrollPosition );

		// Restore document zoom:
		if( this.orginalParnetViewPortContent ){
			$parent.find('meta[name="viewport"]').attr('content', this.orginalParnetViewPortContent );
		} else{
			// Restore user zoom: ( NOTE, there does not appear to be a way to know the
			// initial scale, so we just restore to 1 in the absence of explicit viewport tag )
			// In order to restore zoom, we must set maximum-scale to a valid value
			$parent.find('meta[name="viewport"]').attr('content', 'initial-scale=1; maximum-scale=8; minimum-scale=1;' );
		}
		$iframe[0].style.cssText = this.orginalParentIframeLayout.style;
		$iframe.attr({
			'width': this.orginalParentIframeLayout.width,
			'height': this.orginalParentIframeLayout.height
		})

		// Restore any parent absolute pos:
		$parent.find( _this.parentsAbsoluteList ).each( function() {
			$( this ).css( 'position', 'absolute' );
		} );<commit_msg>added check for orginalParentIframeLayout exists<commit_after>/**
* Msg text is inherited from embedPlayer
*/

( function( mw, $ ) {"use strict";
/**
* mw.PlayerControlBuilder object
*	@param the embedPlayer element we are targeting
*/
mw.PlayerControlBuilder = function( embedPlayer, options ) {
	return this.init( embedPlayer, options );
};

/**
 * ControlsBuilder prototype:
 */
mw.PlayerControlBuilder.prototype = {
	//Default Local values:

	// Parent css Class name
	playerClass : 'mv-player',

	// Long string display of time value
	longTimeDisp: true,

	// Default volume layout is "vertical"
	volumeLayout : 'vertical',

	// Default control bar height
	height: mw.getConfig( 'EmbedPlayer.ControlsHeight' ),

	// Default supported components is merged with embedPlayer set of supported types
	supportedComponents: {
		// All playback types support options
		'options': true
	},

	// Default supported menu items is merged with skin menu items
	supportedMenuItems: {
		// Player Select
		'playerSelect' : true,

		// Download the file menu
		'download' : true,

		// Share the video menu
		'share' : true,

		// Player library link
		'aboutPlayerLibrary': true
	},

	// Flag to store the current fullscreen mode
	inFullScreen: false,

	// Flag for full window size players
	isWindowSizePlayer: false,

	// Flag to store if a warning binding has been added
	addWarningFlag: false,

	// Flag to store state of overlay on player
	displayOptionsMenuFlag: false,

	// Local storage of ControlBar Callback
	hideControlBarCallback: false,

	// Flag to store controls status (disabled/enabled)
	controlsDisabled: false,

	// binding postfix
	bindPostfix: '.controlBuilder',

	/**
	* Initialization Object for the control builder
	*
	* @param {Object} embedPlayer EmbedPlayer interface
	*/
	init: function( embedPlayer ) {
		var _this = this;
		this.embedPlayer = embedPlayer;

		// Check for skin overrides for controlBuilder
		var skinClass = embedPlayer.skinName.substr(0,1).toUpperCase() + embedPlayer.skinName.substr( 1 );
		if ( mw['PlayerSkin' + skinClass ] ) {

			// Clone as to not override prototype with the skin config
			var _this = $.extend( true, { }, this, mw['PlayerSkin' + skinClass ] );
			return _this;
		}
		// Return the controlBuilder Object:
		return this;
	},

	/**
	* Get the control bar height
	* @return {Number} control bar height
	*/
	getHeight: function(){
		return this.height;
	},


	/**
	* Add the controls html to player interface
	*/
	addControls: function() {
		// Set up local pointer to the embedPlayer
		var embedPlayer = this.embedPlayer;

		// Set up local controlBuilder
		var _this = this;

		// Remove any old controls & old overlays:
		embedPlayer.$interface.find( '.control-bar,.overlay-win' ).remove();

		// Reset flags:
		_this.displayOptionsMenuFlag = false;

		// Setup the controlBar container ( starts hidden )
		var $controlBar = $('<div />')
			.addClass( 'ui-state-default ui-widget-header ui-helper-clearfix control-bar' )
			.css( 'height', this.height );

		// Controls are hidden by default if overlaying controls:
		if( _this.isOverlayControls() ){
			$controlBar.hide();
		} else {
			// Include the control bar height when calculating the layout
			$controlBar.addClass('block');
		}

		$controlBar.css( {
			'position': 'absolute',
			'bottom' : '0px',
			'left' : '0px',
			'right' : '0px'
		} );

		// Make room for audio controls in the interface:
		if( embedPlayer.isAudio() && embedPlayer.$interface.height() == 0 ){
			embedPlayer.$interface.css( {
				'height' : this.height
			} );
		}

		// Add the controls to the interface
		embedPlayer.$interface.append( $controlBar );

        if ( $.browser.mozilla && parseFloat( $.browser.version ) < 2 ) {
			embedPlayer.triggerHelper( 'resizeIframeContainer', [ {'height' : embedPlayer.height + $controlBar.height() - 1} ] );
        }

		// Add the Controls Component
		this.addControlComponents();

		// Add top level Controls bindings
		this.addControlBindings();
	},

	/**
	* Add control components as defined per this.components
	*/
	addControlComponents: function( ) {
		var _this = this;

		// Set up local pointer to the embedPlayer
		var embedPlayer = this.embedPlayer;

		//Set up local var to control container:
		var $controlBar = embedPlayer.$interface.find( '.control-bar' );

		this.availableWidth = embedPlayer.getPlayerWidth();

		mw.log( 'PlayerControlsBuilder:: addControlComponents into:' + this.availableWidth );
		// Build the supportedComponents list
		this.supportedComponents = $.extend( this.supportedComponents, embedPlayer.supports );

		// Check for Attribution button
		if( mw.getConfig( 'EmbedPlayer.AttributionButton' ) && embedPlayer.attributionbutton ){
			this.supportedComponents[ 'attributionButton' ] = true;
		}
		// Check global fullscreen enabled flag
		if( mw.getConfig( 'EmbedPlayer.EnableFullscreen' ) === false ){
			this.supportedComponents[ 'fullscreen'] = false;
		}
		// Check if the options item is available
		if( mw.getConfig( 'EmbedPlayer.EnableOptionsMenu' ) === false ){
			this.supportedComponents[ 'options'] = false;
		}
		// Check for volume control
		if( mw.getConfig( 'EmbedPlayer.EnableVolumeControl') === false ){
			this.supportedComponents[ 'volumeControl'] = false;
		}

		// Check if we have multiple playable sources ( if only one source don't display source switch )
		if( embedPlayer.mediaElement.getPlayableSources().length == 1 ){
			this.supportedComponents[ 'sourceSwitch'] = false;
		}

		$( embedPlayer ).trigger( 'addControlBarComponent', this );

		var addComponent = function( componentId ){
			if ( _this.supportedComponents[ componentId ] ) {
				if ( _this.availableWidth > _this.components[ componentId ].w ) {
					// Append the component
					$controlBar.append(
						_this.getComponent( componentId )
					);
					_this.availableWidth -= _this.components[ componentId ].w;
					mw.log(" availableWidth:" + _this.availableWidth + ' ' + componentId + ' took: ' +  _this.components[ componentId ].w )
				} else {
					mw.log( 'PlayerControlBuilder:: Not enough space for control component:' + componentId );
				}
			}
		};

		// Output components
		for ( var componentId in this.components ) {
			// Check for (component === false ) and skip
			if( this.components[ componentId ] === false ){
				continue;
			}

			// Special case with playhead and time ( to make sure they are to the left of everything else )
			if ( componentId == 'playHead' || componentId == 'timeDisplay'){
				continue;
			}

			// Skip "fullscreen" button for assets or where height is 0px ( audio )
			if( componentId == 'fullscreen' && this.embedPlayer.isAudio() ){
				continue;
			}
			addComponent( componentId );
		}
		// Add special case remaining components:
		if( mw.getConfig( 'EmbedPlayer.EnableTimeDisplay' ) ){
			addComponent( 'timeDisplay' );
		}
		if( this.availableWidth > 30 ){
			addComponent( 'playHead' );
		}
		$(embedPlayer).trigger( 'controlBarBuildDone' );
	},

	/**
	* Get a window size for the player while preserving aspect ratio:
	*
	* @@TODO This has similar logic to mw.embedPlayerNative applyIntrinsicAspect we should look
	* at merging their functionality.
	*
	* @param {object} windowSize
	* 		object that set { 'width': {width}, 'height':{height} } of target window
	* @return {object}
	* 	 css settings for fullscreen player
	*/
	getAspectPlayerWindowCss: function( windowSize ) {
		var embedPlayer = this.embedPlayer;
		var _this = this;
		// Setup target height width based on max window size
		if( !windowSize ){
			var windowSize = {
				'width' : $( window ).width(),
				'height' : $( window ).height()
			};
		}
		windowSize.width = parseInt( windowSize.width );
		windowSize.height = parseInt( windowSize.height );
		// See if we need to leave space for control bar
		if( !_this.isOverlayControls() ){
			//targetHeight =  targetHeight - this.height;
			windowSize.height = windowSize.height - this.height;
		}

		// Set target width
		var targetWidth = windowSize.width;
		var targetHeight = targetWidth * ( 1 / _this.getIntrinsicAspect() );
		// Check if it exceeds the height constraint:
		if( targetHeight > windowSize.height ){
			targetHeight = windowSize.height;
			targetWidth = parseInt( targetHeight * _this.getIntrinsicAspect() );
		}
		var offsetTop = 0;
		//  Move the video down 1/2 of the difference of window height
		offsetTop+= ( targetHeight < windowSize.height )? ( windowSize.height- targetHeight ) / 2 : 0;
		// if the video is very tall in a short window adjust the size:
		var offsetLeft = ( targetWidth < windowSize.width )? parseInt( windowSize.width- targetWidth ) / 2 : 0;

		var position = (mw.isIOS4() && mw.isIphone()) ? 'static' : 'absolute';
		mw.log( 'PlayerControlBuilder::getAspectPlayerWindowCss: ' + ' h:' + targetHeight + ' w:' + targetWidth + ' t:' + offsetTop + ' l:' + offsetLeft );
		return {
			'position' : position,
			'height': parseInt( targetHeight ),
			'width' : parseInt( targetWidth ),
			'top' : parseInt( offsetTop ),
			'left': parseInt( offsetLeft)
		};
	},

	/**
	 * Get the intrinsic aspect ratio of media  ( width / height )
	 * @return {float}
	 * 			size object with width and height
	 */
	getIntrinsicAspect: function(){
		var vid = this.embedPlayer.getPlayerElement();
		// Check for raw intrinsic media size:
		if( vid && vid.videoWidth && vid.videoHeight ){
			return vid.videoWidth / vid.videoHeight;
		}

		// See if we have source data attributes available:
		if( this.embedPlayer.mediaElement &&
			this.embedPlayer.mediaElement.selectedSource )
		{
			var ss = this.embedPlayer.mediaElement.selectedSource;
			// See if we have a hardcoded aspect to the source ( Adaptive streams don't have width / height )
			if( ss.aspect ){
				return ss.aspect;
			}

			if( ss.width && ss.height ){
				return ss.width / ss.height
			}
		}

		// check for posterImage size: ( should have Intrinsic aspect size as well )
		var img = this.embedPlayer.$interface.find('.playerPoster')[0];
		if( img && img.naturalWidth && img.naturalHeight){
			return img.naturalWidth /  img.naturalHeight
		}

		// if all else fails use embedPlayer.getWidth()
		return this.embedPlayer.getWidth() / this.embedPlayer.getHeight()
	},

	/**
	* Get the play button css
	*/
	getPlayButtonPosition: function() {
		var _this = this;
		return {
			'position' : 'absolute',
			'left' : '50%',
			'top' : '50%',
			'margin-left' : - .5 * this.getComponentWidth( 'playButtonLarge' ),
			'margin-top' : - .5 * this.getComponentHeight( 'playButtonLarge' )
		};
	},

	/**
	 * Toggles full screen by calling
	 *  doFullScreenPlayer to enable fullscreen mode
	 *  restoreWindowPlayer to restore window mode
	 */
	toggleFullscreen: function( forceClose ) {
		var _this = this;
		// Do normal in-page fullscreen handling:
		if( this.inFullScreen ){
			this.restoreWindowPlayer();
		}else {
			this.doFullScreenPlayer();
		}
	},

	/**
	* Do full-screen mode
	*/
	doFullScreenPlayer: function( callback ) {
		mw.log("PlayerControlBuilder:: doFullScreenPlayer" );
		// Setup pointer to control builder :
		var _this = this;

		// Setup local reference to embed player:
		var embedPlayer = this.embedPlayer;

		// Setup a local reference to the player interface:
		var $interface = embedPlayer.$interface;
		// Check fullscreen state ( if already true do nothing )
		if( this.inFullScreen == true ){
			return ;
		}
		this.inFullScreen = true;

		// if overlaying controls add hide show player binding.
		if( _this.isOverlayControls() ){
			_this.addFullscreenMouseMoveHideShowControls();
		}

		// Store the current scroll location on the iframe:
		$( embedPlayer ).trigger( 'fullScreenStoreVerticalScroll' );

		// Check for native support for fullscreen and we are in an iframe server
		if( window.fullScreenApi.supportsFullScreen ) {
			_this.preFullscreenPlayerSize = this.getPlayerSize();
			var fullscreenHeight = null;
			var fsTarget = this.getFsTarget();

			var escapeFullscreen = function( event ) {
				if ( ! window.fullScreenApi.isFullScreen() ) {
					_this.restoreWindowPlayer();
				}
			}
			// remove any old binding: 
			fsTarget.removeEventListener(  fullScreenApi.fullScreenEventName, escapeFullscreen );
			// Add a binding to catch "escape" fullscreen
			fsTarget.addEventListener( fullScreenApi.fullScreenEventName, escapeFullscreen );
			// Make the iframe fullscreen:
			window.fullScreenApi.requestFullScreen( fsTarget );

			// There is a bug with mozfullscreenchange event in all versions of firefox with supportsFullScreen
			// https://bugzilla.mozilla.org/show_bug.cgi?id=724816
			// so we have to have an extra binding to check for size change and then restore.
			if( $.browser.mozilla ){
				_this.fullscreenRestoreCheck = setInterval( function(){
					if( fullscreenHeight && $(window).height() < fullscreenHeight ){
						// Mozilla triggered size change:
						clearInterval ( _this.fullscreenRestoreCheck );
						_this.restoreWindowPlayer();
					}
					// set fullscreen height:
					if( ! fullscreenHeight && _this.preFullscreenPlayerSize.height != $(window).height() ){
						fullscreenHeight = $(window).height();
					}
				}, 250 );
			}
		} else {
			// Check for hybrid html controls / native fullscreen support:
			var vid = this.embedPlayer.getPlayerElement();
			if( mw.getConfig('EmbedPlayer.EnableIpadNativeFullscreen')
					&&
				vid && vid.webkitSupportsFullscreen
			){
				this.doHybridNativeFullscreen();
				return ;
			} else {
				if( mw.getConfig('EmbedPlayer.IsIframeServer' ) ){
					this.doParentIframeFullscreen();
				} else {
					// do pseudo fullscren
					this.doFullScreenPlayerDom();
				}
			}
		}

		// Bind escape to restore in page clip ( IE9 needs a secondary escape binding )
		$( window ).keyup( function( event ) {
			// Escape check
			if( event.keyCode == 27 ){
				_this.restoreWindowPlayer();
			}
		} );

		// trigger the open fullscreen event:
		$( embedPlayer ).trigger( 'onOpenFullScreen' );
	},
	
	doParentIframeFullscreen: function(){
		var 
			_this = this,
			parentDoc = window['parent'].document,
			$parent = $( window['parent'].document ),
			$iframe = $( this.getFsTarget() ),
			parentContext = window['parent'];

		// update / reset local restore properties 
		this.verticalScrollPosition = (parentDoc.all ? parentDoc.scrollTop : parentContext.pageYOffset);
		this.parentsAbsoluteList = [];
		this.parentsRelativeList = [];
		
		// Set the original parent page scale if possible: 
		this.orginalParnetViewPortContent = $parent.find( 'meta[name="viewport"]' ).attr( 'content' );
		this.orginalParentIframeLayout = {
				'style' : $iframe[0].style.cssText,
				'width' : $iframe.attr('width'),
				'height' : $iframe.attr('height')
		}
		
		mw.log("PlayerControls:: doParentIframeFullscreen> verticalScrollPosition:" + this.verticalScrollPosition);
		parentContext.scroll(0, 0);

		// Make sure the parent page page has a zoom of 1:
		if( ! $parent.find('meta[name="viewport"]').length ){
			$parent.find('head').append( $( '<meta />' ).attr('name', 'viewport') );
		}
		$parent.find('meta[name="viewport"]').attr('content', 'initial-scale=1; maximum-scale=1; minimum-scale=1;' );

		// iPad 5 supports fixed position in a bad way, use absolute pos for iOS
		var playerCssPosition = ( mw.isIOS() ) ? 'absolute': 'fixed';
		
		// Remove absolute css of the iframe parents
		$iframe.parents().each( function() {
			var $parent = $( this );
			if( $parent.css( 'position' ) == 'absolute' ) {
				_this.parentsAbsoluteList.push( $parent );
				$parent.css( 'position', 'static' );
			}
			if( $parent.css( 'position' ) == 'relative' ) {
				_this.parentsRelativeList.push( $parent );
				$parent.css( 'position', 'static' );
			}
		});

		// Make the iframe fullscreen
		$iframe
			.css({
				'z-index': mw.getConfig( 'EmbedPlayer.FullScreenZIndex' ),
				'position': playerCssPosition,
				'top' : '0px',
				'left' : '0px',
				'width' : parentContext.innerWidth,
				'height' : parentContext.innerHeight,
				'margin': 0
			})
			.data(
				'isFullscreen', true
			);
		
		
		// Bind orientation change to resize player ( if fullscreen )
		$( parentContext ).bind( 'orientationchange', function(e){
			if( localIframeInFullscreen ){
				doFullscreen();
			}
		});

		// prevent scrolling when in fullscreen:
		document.ontouchmove = function( e ){
			if( localIframeInFullscreen ){
				e.preventDefault();
			}
		};
	},
	restoreParentIframeFullscreen: function(){
		var 
		_this = this,
		parentDoc = window['parent'].document,
		$parent = $( window['parent'].document ),
		$iframe = $( this.getFsTarget() ),
		parentContext = window['parent'];
		
		mw.log("PlayerControlBuilder:: restoreParentIframeFullscreen> verticalScrollPosition:" + this.verticalScrollPosition );

		// Restore document zoom:
		if( this.orginalParnetViewPortContent ){
			$parent.find('meta[name="viewport"]').attr('content', this.orginalParnetViewPortContent );
		} else{
			// Restore user zoom: ( NOTE, there does not appear to be a way to know the
			// initial scale, so we just restore to 1 in the absence of explicit viewport tag )
			// In order to restore zoom, we must set maximum-scale to a valid value
			$parent.find('meta[name="viewport"]').attr('content', 'initial-scale=1; maximum-scale=8; minimum-scale=1;' );
		}
		if( this.orginalParentIframeLayout ) {
			$iframe[0].style.cssText = this.orginalParentIframeLayout.style;
			$iframe.attr({
				'width': this.orginalParentIframeLayout.width,
				'height': this.orginalParentIframeLayout.height
			});
		}
		// Restore any parent absolute pos:
		$parent.find( _this.parentsAbsoluteList ).each( function() {
			$( this ).css( 'position', 'absolute' );
		} );
{'fraction_non_alphanumeric': 0.12696428063993634, 'fraction_numerical': 0.003239464635844392, 'mean_word_length': 3.4973801916932907, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 17, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '677094', 'n_tokens_mistral': 12073, 'n_tokens_neox': 10404, 'n_words': 2952}