/** * 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.config.get( '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 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 _this = $.extend( true, { }, this, mw['PlayerSkin' + skinClass ] ); } if ( _this.embedPlayer.mediaElement.getPlayableSources().length <= 1 && _this.supportedMenuItems.playerSelect ) { delete _this.supportedMenuItems.playerSelect; } // 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, profile = $.client.profile(); // Set up local controlBuilder var _this = this; // Remove any old controls & old overlays: embedPlayer.getInterface().find( '.control-bar,.overlay-win' ).remove(); // Reset flags: _this.displayOptionsMenuFlag = false; // Setup the controlBar container ( starts hidden ) var $controlBar = $('
') .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'); } // Make room for audio controls in the interface: if( embedPlayer.isAudio() && embedPlayer.getInterface().height() == 0 ){ embedPlayer.getInterface().css( { 'height' : this.height } ); } // Add the controls to the interface embedPlayer.getInterface().append( $controlBar ); if ( profile.name === 'firefox' && profile.versionNumber < 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.getInterface().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.config.get( 'EmbedPlayer.AttributionButton' ) && embedPlayer.attributionbutton ){ this.supportedComponents[ 'attributionButton' ] = true; } // Check global fullscreen enabled flag if( mw.config.get( 'EmbedPlayer.EnableFullscreen' ) === false ){ this.supportedComponents[ 'fullscreen'] = false; } // Check if the options item is available if( mw.config.get( 'EmbedPlayer.EnableOptionsMenu' ) === false ){ this.supportedComponents[ 'options'] = false; } // Check for volume control if( mw.config.get( '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; } // Give embeds option to explicitly disable components via flag var source = embedPlayer.mediaElement.getPlayableSources()[0]; if ( !embedPlayer.disablecontrols && source ) { embedPlayer.disablecontrols = source.disablecontrols; } if ( embedPlayer.disablecontrols ) { embedPlayer.disablecontrols.split(',').forEach(function( key ) { mw.log( 'PlayerControlBuilder:: disabled component via flag:' + key ); _this.supportedComponents[ key ] = false; }); } $( embedPlayer ).trigger( 'addControlBarComponent', this ); var components = []; var largestPos = 0; var addComponent = function( componentId ){ if ( _this.supportedComponents[ componentId ] ) { if ( _this.availableWidth >= _this.components[ componentId ].w ) { _this.availableWidth -= _this.components[ componentId ].w; // Check if position is defined, if not, place at end of known positions var position = _this.components[ componentId ].position ? _this.components[ componentId ].position: largestPos+1 if( position > largestPos ){ largestPos = position; } components.push({ 'id': componentId, 'position': position }); //mw.log(" availableWidth:" + _this.availableWidth + ' ' + componentId + ' took: ' + _this.components[ componentId ].w ) } else { mw.log( 'PlayerControlBuilder:: Not enough space for control component:' + componentId ); } } }; var addComponents = function() { components.sort(function(a, b) { return b.position - a.position; }); for(var i=0;i 30 ){ addComponent( 'playHead' ); } addComponents(); $(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.getInterface().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' ) }; }, /** * Check if we're in Fullscreen * @return {boolean) */ isInFullScreen: function() { return this.inFullScreen; }, /** * 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.isInFullScreen() ){ this.restoreWindowPlayer(); }else { this.doFullScreenPlayer(); } // Don't follow the # link: return false; }, /** * Do full-screen mode */ doFullScreenPlayer: function( callback ) { mw.log("PlayerControlBuilder:: doFullScreenPlayer" ); // Setup pointer to control builder : var _this = this, profile = $.client.profile(); // Store the page vertical scroll var doc = window.document; var context = window; this.verticalScrollPosition = doc.all ? doc.scrollTop : context.pageYOffset; // Setup local reference to embed player: var embedPlayer = this.embedPlayer; // Setup a local reference to the player interface: var $interface = embedPlayer.getInterface(); // Check fullscreen state ( if already true do nothing ) if( this.isInFullScreen() == true ){ return ; } this.inFullScreen = true; // Add fullscreen class to interface: $interface.addClass( 'fullscreen' ); // if overlaying controls add hide show player binding. if( _this.isOverlayControls() && !embedPlayer.isTouchDevice() ){ _this.addFullscreenMouseMoveHideShowControls(); } // Store the current scroll location on the iframe: $( embedPlayer ).trigger( 'fullScreenStoreVerticalScroll' ); if( window.fullScreenApi.supportsFullScreen ) { _this.preFullscreenPlayerSize = this.getPlayerSize(); var fullscreenHeight = null; var fsTarget = this.getFsTarget(); var escapeFullscreen = function( event ) { // grab the correct document target to check for fullscreen if ( ! window.fullScreenApi.isFullScreen( window.document ) ) { _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( profile.name === 'firefox' ){ _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.config.get('EmbedPlayer.EnableIpadNativeFullscreen') && vid && vid.webkitSupportsFullscreen ){ this.doHybridNativeFullscreen(); return ; } else { // make the player traget or iframe fullscreen this.doContextTargetFullscreen(); } } // 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' ); // re draw the controls after a timeout ( to allow the screen dom to update ) setTimeout( function(){ _this.addControls(); },100) }, /** * Make the target player interface or iframe fullscreen */ doContextTargetFullscreen: function() { var _this = this, doc = window.document, $doc = $( doc ), $target = $( this.getFsTarget() ), context = window; // update / reset local restore properties this.parentsAbsoluteList = []; this.parentsRelativeList = []; // Set the original parent page scale if possible: this.orginalParnetViewPortContent = $doc.find( 'meta[name="viewport"]' ).attr( 'content' ); this.orginalTargetElementLayout = { 'style' : $target[0].style.cssText, 'width' : $target.width(), 'height' : $target.height() }; mw.log("PlayerControls:: doParentIframeFullscreen> verticalScrollPosition:" + this.verticalScrollPosition); context.scroll(0, 0); // Make sure the parent page page has a zoom of 1: if( ! $doc.find('meta[name="viewport"]').length ){ $doc.find('head').append( $( '' ).attr('name', 'viewport') ); } $doc.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 $target's parents $target.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 $target fullscreen $target .css({ 'z-index': mw.config.get( 'EmbedPlayer.FullScreenZIndex' ), 'position': playerCssPosition, 'top' : '0px', 'left' : '0px', 'margin': 0 }) .data( 'isFullscreen', true ); var updateTargetSize = function() { context.scroll(0, 0); $target.css({ 'width' : context.innerWidth, 'height' : context.innerHeight }); // update player size if needed: _this.embedPlayer.applyIntrinsicAspect(); }; updateTargetSize(); // Bind orientation change to resize player ( if fullscreen ) $( context ).bind( 'orientationchange', function(e){ if( _this.isInFullScreen() ){ updateTargetSize(); } }); // prevent scrolling when in fullscreen: ( both iframe and dom target use document ) document.ontouchmove = function( e ){ if( _this.isInFullScreen() ){ e.preventDefault(); } }; }, /** * Restore the player interface or iframe to a window player */ restoreContextPlayer: function(){ var _this = this, doc = window.document, $doc = $( doc ), $target = $( this.getFsTarget() ), context = window; mw.log("PlayerControlsBuilder:: restoreContextPlayer> verticalScrollPosition:" + this.verticalScrollPosition ); // Restore document zoom: if( this.orginalParnetViewPortContent ){ $doc.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 $doc.find('meta[name="viewport"]').attr('content', 'initial-scale=1; maximum-scale=8; minimum-scale=1;' ); } if( this.orginalTargetElementLayout ) { $target[0].style.cssText = this.orginalTargetElementLayout.style; $target.attr({ 'width': this.orginalTargetElementLayout.width, 'height': this.orginalTargetElementLayout.height }); // update player size if needed: _this.embedPlayer.applyIntrinsicAspect(); } // Restore any parent absolute pos: $doc.find( _this.parentsAbsoluteList ).each( function() { $( this ).css( 'position', 'absolute' ); } ); $doc.find( _this.parentsRelativeList ).each( function() { $( this ).css( 'position', 'relative' ); } ); }, /** * Supports hybrid native fullscreen, player html controls, and fullscreen is native */ doHybridNativeFullscreen: function(){ var vid = this.embedPlayer.getPlayerElement(); var _this = this; vid.webkitEnterFullscreen(); // start to pull for exit fullscreen: this.fsIntervalID = setInterval( function(){ var currentFS = vid.webkitDisplayingFullscreen; // Check if we have entered fullscreen but the player // has exited fullscreen with native controls click if( _this.isInFullScreen() && !currentFS ){ // restore non-fullscreen player state _this.inFullScreen = false; // Trigger the onCloseFullscreen event: $( _this.embedPlayer ).trigger( 'onCloseFullScreen' ); // stop polling for state change. clearInterval( _this.fsIntervalID ); } }, 250 ); }, getWindowSize: function(){ return { 'width' : $(window).width(), 'height' : $(window).height() }; }, doDomFullscreen: function(){ var _this = this; var embedPlayer = this.embedPlayer; var $interface = embedPlayer.getInterface(); // Remove any old mw-fullscreen-overlay $( '.mw-fullscreen-overlay' ).remove(); _this.preFullscreenPlayerSize = this.getPlayerSize(); // Add the css fixed fullscreen black overlay as a sibling to the video element // iOS4 does not respect z-index $interface.after( $( '
' ) .addClass( 'mw-fullscreen-overlay' ) // Set some arbitrary high z-index .css('z-index', mw.config.get( 'EmbedPlayer.FullScreenZIndex' ) ) .hide() .fadeIn("slow") ); // get the original interface to absolute positioned: if( ! this.windowPositionStyle ){ this.windowPositionStyle = $interface.css( 'position' ); } if( !this.windowZindex ){ this.windowZindex = $interface.css( 'z-index' ); } // Get the base offset: this.windowOffset = this.getWindowOffset(); // Change the z-index of the interface $interface.css( { 'position' : 'fixed', 'z-index' : mw.config.get( 'EmbedPlayer.FullScreenZIndex' ) + 1, 'top' : this.windowOffset.top, 'left' : this.windowOffset.left } ); // If native persistent native player update z-index: if( embedPlayer.isPersistentNativePlayer() ){ $( embedPlayer.getPlayerElement() ).css( { 'z-index': mw.config.get( 'EmbedPlayer.FullScreenZIndex' ) + 1, 'position': 'absolute' }); } // Empty out the parent absolute index _this.parentsAbsolute = []; // Hide the body scroll bar $('body').css( 'overflow', 'hidden' ); var topOffset = '0px'; var leftOffset = '0px'; // Check if we have an offsetParent if( $interface.offsetParent()[0].tagName && $interface.offsetParent()[0].tagName.toLowerCase() != 'body' ) { topOffset = -this.windowOffset.top + 'px'; leftOffset = -this.windowOffset.left + 'px'; } // Overflow hidden in fullscreen: $interface.css( 'overlow', 'hidden' ); // Remove absolute css of the interface parents $interface.parents().each( function() { //mw.log(' parent : ' + $( this ).attr('id' ) + ' class: ' + $( this ).attr('class') + ' pos: ' + $( this ).css( 'position' ) ); if( $( this ).css( 'position' ) == 'absolute' ) { _this.parentsAbsolute.push( $( this ) ); $( this ).css( 'position', null ); mw.log( 'PlayerControlBuilder:: should update position: ' + $( this ).css( 'position' ) ); } }); // Bind escape to restore in page clip $( window ).keyup( function( event ) { // Escape check if( event.keyCode == 27 ){ _this.restoreWindowPlayer(); } } ); }, addFullscreenMouseMoveHideShowControls:function(){ var _this = this; // Bind mouse move in interface to hide control bar _this.mouseMovedFlag = false; _this.embedPlayer.getInterface().mousemove( function(e){ _this.mouseMovedFlag = true; }); // Check every 2 seconds reset flag status if controls are overlay var checkMovedMouse = function(){ if( _this.isInFullScreen() ){ if( _this.mouseMovedFlag ){ _this.mouseMovedFlag = false; _this.showControlBar(); // Once we move the mouse keep displayed for 3 seconds setTimeout(checkMovedMouse, 3000); } else { // Check for mouse movement every 250ms _this.hideControlBar(); setTimeout(checkMovedMouse, 250 ); } return; } }; // always initially show the control bar: _this.showControlBar(); // start monitoring for moving mouse checkMovedMouse(); }, getWindowOffset: function(){ var windowOffset = this.embedPlayer.getInterface().offset(); windowOffset.top = windowOffset.top - $(document).scrollTop(); windowOffset.left = windowOffset.left - $(document).scrollLeft(); this.windowOffset = windowOffset; return this.windowOffset; }, // Display a fullscreen tip if configured to do and the browser supports it. displayFullscreenTip: function(){ var _this = this; // Mobile devices don't have f11 key if( mw.isMobileDevice() ){ return ; } // Safari does not have a DOM fullscreen ( no subtitles, no controls ) if ( $.client.profile().name === 'safari' ) { return; } // OSX has a different short cut than windows and liux var toolTipMsg = ( navigator.userAgent.indexOf('Mac OS X') != -1 )? mw.msg( 'mwe-embedplayer-fullscreen-tip-osx') : mw.msg( 'mwe-embedplayer-fullscreen-tip'); var $targetTip = this.addWarningBinding( 'EmbedPlayer.FullscreenTip', $('

').html( toolTipMsg ) ); // Display the target warning: $targetTip.show(); var hideTip = function(){ mw.setConfig('EmbedPlayer.FullscreenTip', false ); $targetTip.fadeOut('fast'); }; // Hide fullscreen tip if: // We leave fullscreen, $( this.embedPlayer ).bind( 'onCloseFullScreen', hideTip ); // After 5 seconds, setTimeout( hideTip, 5000 ); // Or if we catch an f11 button press $( document ).keyup( function( event ){ if( event.keyCode == 122 ){ hideTip(); } return true; }); }, // TOOD fullscreen iframe vs inpage object abstraction //( avoid repatiave conditionals in getters ) getPlayerSize: function(){ var height = $(window).height() - this.getHeight(); if( mw.config.get('EmbedPlayer.IsIframeServer' ) ){ return { 'height' : height, 'width' : $(window).width() } } else { return { 'height' : this.embedPlayer.getInterface().height(), 'width' : this.embedPlayer.getInterface().width() } } }, getFsTarget: function(){ var $interface = this.embedPlayer.getInterface(); return $interface[0]; }, /** * Restore the window player */ restoreWindowPlayer: function() { var _this = this; mw.log("PlayerControlBuilder :: restoreWindowPlayer" ); var embedPlayer = this.embedPlayer; // Check if fullscreen mode is already restored: if( this.isInFullScreen() === false ){ return ; } // Set fullscreen mode to false this.inFullScreen = false; // remove the fullscreen interface embedPlayer.getInterface().removeClass( 'fullscreen' ); // Check for native support for fullscreen and support native fullscreen restore if ( window.fullScreenApi.supportsFullScreen ) { var fsTarget = this.getFsTarget(); window.fullScreenApi.cancelFullScreen( fsTarget ); } // Restore the iFrame context player this.restoreContextPlayer(); // Restore scrolling on iPad $( document ).unbind( 'touchend.fullscreen' ); // Trigger the onCloseFullscreen event: $( embedPlayer ).trigger( 'onCloseFullScreen' ); // Scroll back to the previews position ( do in async call to allow dom fullscreen restore ) setTimeout( function(){ window.scroll( 0, _this.verticalScrollPosition ); }, 100 ); // re draw the controls after a timeout ( to allow the screen dom to update ) setTimeout( function(){ _this.addControls(); },100) }, restoreDomPlayer: function(){ var _this = this; // local ref to embedPlayer: var embedPlayer = this.embedPlayer; var $interface = embedPlayer.$interface; var interfaceHeight = ( _this.isOverlayControls() ) ? embedPlayer.getHeight() : embedPlayer.getHeight() + _this.getHeight(); mw.log( 'restoreWindowPlayer:: h:' + interfaceHeight + ' w:' + embedPlayer.getWidth()); $('.mw-fullscreen-overlay').remove( 'slow' ); mw.log( 'restore embedPlayer:: ' + embedPlayer.getWidth() + ' h: ' + embedPlayer.getHeight() ); // Restore the player: embedPlayer.getInterface().css( { 'width' : _this.preFullscreenPlayerSize.width, 'height' : _this.preFullscreenPlayerSize.height }); var topPos = { 'position' : _this.windowPositionStyle, 'z-index' : _this.windowZindex, 'overlow' : 'visible', 'top' : '0px', 'left' : '0px' }; // Restore non-absolute layout: $( [ $interface, $interface.find('.playerPoster'), embedPlayer ] ).css( topPos ); if( embedPlayer.getPlayerElement() ){ $( embedPlayer.getPlayerElement() ) .css( topPos ) } // Restore the body scroll bar $('body').css( 'overflow', 'auto' ); // If native player restore z-index: if( embedPlayer.isPersistentNativePlayer() ){ $( embedPlayer.getPlayerElement() ).css( { 'z-index': 'auto' }); } }, /** * Get minimal width for interface overlay */ getOverlayWidth: function( ) { return ( this.embedPlayer.getPlayerWidth() < 300 )? 300 : this.embedPlayer.getPlayerWidth(); }, /** * Get minimal height for interface overlay */ getOverlayHeight: function( ) { return ( this.embedPlayer.getPlayerHeight() < 200 )? 200 : this.embedPlayer.getPlayerHeight(); }, /** * addControlBindings * Adds control hooks once controls are in the DOM */ addControlBindings: function( ) { // Set up local pointer to the embedPlayer var embedPlayer = this.embedPlayer, _this = this, $interface = embedPlayer.getInterface(), profile = $.client.profile(); _this.onControlBar = false; // Remove any old interface bindings $( embedPlayer ).unbind( this.bindPostfix ); var bindFirstPlay = false; _this.addRightClickBinding(); // add the player click bindings _this.addPlayerClickBindings(); // Bind into play.ctrl namespace ( so we can unbind without affecting other play bindings ) $( embedPlayer ).bind( 'onplay' + this.bindPostfix, function() { //Only bind once played // add right click binding again ( in case the player got swaped ) embedPlayer.controlBuilder.addRightClickBinding(); }); $( embedPlayer ).bind( 'timeupdate' + this.bindPostfix, function(){ embedPlayer.updatePlayheadStatus() }); // Update buffer information $( embedPlayer ).bind( 'progress' + this.bindPostfix, function( event, jEvent, id){ // regain scope var embedPlayer = $( '#' + id )[0]; embedPlayer.updateBufferStatus(); }); // Bind to EnableInterfaceComponents $( embedPlayer ).bind( 'onEnableInterfaceComponents' + this.bindPostfix, function() { embedPlayer.controlBuilder.controlsDisabled = false; embedPlayer.controlBuilder.addPlayerClickBindings(); }); // Bind to DisableInterfaceComponents $( embedPlayer ).bind( 'onDisableInterfaceComponents' + this.bindPostfix, function() { embedPlayer.controlBuilder.controlsDisabled = true; embedPlayer.controlBuilder.removePlayerClickBindings(); }); // TODO select a player on the page var bindSpaceUp = function(){ $(window).bind('keyup' + _this.bindPostfix, function(e) { if( e.keyCode == 32 ) { if(embedPlayer.paused) { embedPlayer.play(); } else { embedPlayer.pause(); } return false; } }); }; var bindSpaceDown = function() { $(window).unbind( 'keyup' + _this.bindPostfix ); }; // Bind to resize event /* var triggerUpdate; $( window ).resize(function() { // We use setTimeout because of iOS 4.2 issues clearTimeout(triggerUpdate); triggerUpdate = setTimeout(function() { //embedPlayer.triggerHelper('updateLayout'); }, 100); }); */ $(window).on("debouncedresize", function() { embedPlayer.triggerHelper('updateLayout'); }); // Add hide show bindings for control overlay (if overlay is enabled ) if( ! _this.isOverlayControls() ) { $interface .show() .hover( bindSpaceUp, bindSpaceDown ); // include touch start pause binding $( embedPlayer).bind( 'touchstart' + this.bindPostfix, function() { embedPlayer._playContorls = true; mw.log( "PlayerControlBuilder:: touchstart:" + ' isPause:' + embedPlayer.paused); if( embedPlayer.paused ) { embedPlayer.play(); } else { embedPlayer.pause(); } }); } else { // hide show controls: // Bind a startTouch to show controls $( embedPlayer).bind( 'touchstart' + this.bindPostfix, function() { if ( embedPlayer.getInterface().find( '.control-bar' ).is( ':visible' ) ) { if( embedPlayer.paused ) { embedPlayer.play(); } else { embedPlayer.pause(); } } else { _this.showControlBar(); } clearTimeout( _this.hideControlBarCallback ); _this.hideControlBarCallback = setTimeout( function() { _this.hideControlBar(); }, 60000 ); // ( Once the user touched the video "don't hide" ) return true; } ); var hoverIntentConfig = { 'sensitivity': 100, 'timeout' : 1000, 'over' : function(e){ // Clear timeout on IE9 if( mw.isIE9() ) { clearTimeout(_this.hideControlBarCallback); _this.hideControlBarCallback = false; } // Show controls with a set timeout ( avoid fade in fade out on short mouse over ) _this.showControlBar(); bindSpaceUp(); }, 'out' : function(e){ _this.hideControlBar(); bindSpaceDown(); } }; // Check if we should display the interface: // special check for IE9 ( does not count hover on non-visiable inerface div if( mw.isIE9() ){ $( embedPlayer.getPlayerElement() ).hoverIntent( hoverIntentConfig ); // Add hover binding to control bar embedPlayer.getInterface().find( '.control-bar' ).hover( function(e) { _this.onControlBar = true; embedPlayer.getInterface().find( '.control-bar' ).show(); }, function( e ) { if (!_this.hideControlBarCallback) { _this.hideControlBarCallback = setTimeout(function(){ _this.hideControlBar(); },1000); } _this.onControlBar = false; }); } else { if ( !mw.isIpad() ) { $interface.hoverIntent( hoverIntentConfig ); } } } // Add recommend firefox if we have non-native playback: if ( _this.checkNativeWarning( ) ) { _this.addWarningBinding( 'EmbedPlayer.ShowNativeWarning', mw.msg( 'mwe-embedplayer-for_best_experience', $('
').append( $('') .attr({ 'href': 'http://www.mediawiki.org/wiki/Extension:TimedMediaHandler/Client_download', 'target' : '_new' }) )[0].innerHTML ) ); } // Do png fix for ie6 if ( profile.name === 'msie' && profile.versionNumber <= 6 ) { $( '#' + embedPlayer.id + ' .play-btn-large' ).pngFix(); } this.doVolumeBinding(); // Check if we have any custom skin Bindings to run if ( this.addSkinControlBindings && typeof( this.addSkinControlBindings ) == 'function' ){ this.addSkinControlBindings(); } mw.log( 'trigger::addControlBindingsEvent' ); $( embedPlayer ).trigger( 'addControlBindingsEvent' ); }, removePlayerClickBindings: function(){ $( this.embedPlayer ) .unbind( "click" + this.bindPostfix ) .unbind( "dblclick" + this.bindPostfix ); }, addPlayerClickBindings: function(){ var _this = this; var embedPlayer = this.embedPlayer; // prevent scrolling when in fullscreen: document.ontouchmove = function( e ){ if( _this.isInFullScreen() ){ e.preventDefault(); } }; // Remove old click bindings before adding: this.removePlayerClickBindings(); // Setup "dobuleclick" fullscreen binding to embedPlayer ( if enabled ) if ( this.supportedComponents['fullscreen'] ){ $( embedPlayer ).bind( "dblclick" + _this.bindPostfix, function(){ embedPlayer.fullscreen(); }); } var dblClickTime = 300; var lastClickTime = 0; var didDblClick = false; var playerClickCb = function( event ) { // make sure the event matches: if( event.currentTarget.id != embedPlayer.id ){ embedPlayer = $( '#' + event.currentTarget.id )[0]; } mw.log( "PlayerControlBuilder:: click:" + embedPlayer.id + ' isPause:' + embedPlayer.paused); // Don't do anything if touch interface or native controls are shown if( embedPlayer.useNativePlayerControls() || _this.isControlsDisabled() || embedPlayer.isTouchDevice() ) { return true; } var clickTime = new Date().getTime(); if( clickTime -lastClickTime < dblClickTime ) { didDblClick = true; setTimeout( function(){ didDblClick = false; }, dblClickTime + 10 ); } lastClickTime = clickTime; setTimeout( function(){ // check if no click has since the time we called the setTimeout if( !didDblClick ){ if( embedPlayer.paused ) { embedPlayer.play(); } else { embedPlayer.pause(); } } }, dblClickTime ); return true; }; // Add click binding: ( $(embedPlayer).click ) has scope issues ) if ( embedPlayer.attachEvent ) { embedPlayer.attachEvent("onclick", playerClickCb); } else{ // Firefox 3.5 requires third argument to addEventListener embedPlayer.addEventListener('click', playerClickCb, false ); } }, addRightClickBinding: function(){ var embedPlayer = this.embedPlayer; // check config: if( mw.config.get( 'EmbedPlayer.EnableRightClick') === false ){ document.oncontextmenu= function(e){return false;}; $(embedPlayer).mousedown(function(e){ if( e.button == 2 ) { return false; } }); } }, /** * Hide the control bar. */ hideControlBar : function(){ var animateDuration = 'fast'; var _this = this; // Do not hide control bar if overlay menu item is being displayed: if( _this.displayOptionsMenuFlag || _this.keepControlBarOnScreen ) { setTimeout( function(){ _this.hideControlBar(); }, 200 ); return ; } // IE9: If the user mouse is on the control bar, don't hide it if( this.onControlBar === true ) { return ; } // Hide the control bar this.embedPlayer.getInterface().find( '.control-bar') .fadeOut( animateDuration ); //mw.log('about to trigger hide control bar') // Allow interface items to update: $( this.embedPlayer ).trigger('onHideControlBar', [ {'bottom' : 15}, this.embedPlayer.id ] ); }, restoreControlsHover:function(){ if( this.isOverlayControls() ){ this.keepControlBarOnScreen = false; } }, /** * Show the control bar */ showControlBar: function( keepOnScreen ){ var animateDuration = 'fast'; if(! this.embedPlayer ) return ; if( this.embedPlayer.getPlayerElement && ! this.embedPlayer.isPersistentNativePlayer() ){ $( this.embedPlayer.getPlayerElement() ).css( 'z-index', '1' ); } mw.log( 'PlayerControlBuilder:: ShowControlBar, keep on screen: ' + keepOnScreen ); // Show interface controls this.embedPlayer.getInterface().find( '.control-bar' ) .fadeIn( animateDuration ); if( keepOnScreen ){ this.keepControlBarOnScreen = true; } // Trigger the screen overlay with layout info: $( this.embedPlayer ).trigger( 'onShowControlBar', [{ 'bottom' : this.getHeight() + 15 }, this.embedPlayer.id ] ); }, /** * Checks if the browser supports overlays and the controlsOverlay is * set to true for the player or via config */ isOverlayControls: function(){ //if the player "supports" overlays: if( ! this.embedPlayer.supports['overlays'] ){ return false; } // If disabled via the player if( this.embedPlayer.overlaycontrols === false ){ return false; } // Don't overlay controls if in audio mode: if( this.embedPlayer.isAudio() ){ return false; } // If the config is false if( mw.config.get( 'EmbedPlayer.OverlayControls' ) === false){ return false; } if( this.embedPlayer.controls === false ){ return false; } // Past all tests OverlayControls is true: return true; }, /* Check if the controls are disabled */ isControlsDisabled: function() { return this.controlsDisabled; }, /** * Check if a warning should be issued to non-native playback systems * * dependent on mediaElement being setup */ checkNativeWarning: function( ) { if( mw.config.get( 'EmbedPlayer.ShowNativeWarning' ) === false ){ return false; } // Don't show for imageOverlay player: if( this.embedPlayer.instanceOf == 'ImageOverlay' ){ return false; } // If the resolution is too small don't display the warning if( parseInt( this.embedPlayer.getPlayerHeight() ) < 199 ){ return false; } // See if we have we have native support if( this.embedPlayer.instanceOf == 'Native' ){ return false; } // Not a lot of good options for an iPhone if( this.embedPlayer.instanceOf == 'VLCApp' ){ return false; } if( this.embedPlayer.instanceOf == 'OgvJs' ){ return false; } // Chrome's webM support is oky though: if( /chrome/.test(navigator.userAgent.toLowerCase() ) && mw.EmbedTypes.getMediaPlayers().getMIMETypePlayers( 'video/webm' ).length ){ return false; } // Check for h264 and or flash/flv source and playback support and don't show warning if( ( mw.EmbedTypes.getMediaPlayers().getMIMETypePlayers( 'video/h264' ).length && this.embedPlayer.mediaElement.getSources( 'video/h264' ).length ) || ( mw.EmbedTypes.getMediaPlayers().getMIMETypePlayers( 'video/x-flv' ).length && this.embedPlayer.mediaElement.getSources( 'video/x-flv' ).length ) || ( mw.EmbedTypes.getMediaPlayers().getMIMETypePlayers( 'application/vnd.apple.mpegurl' ).length && this.embedPlayer.mediaElement.getSources( 'application/vnd.apple.mpegurl' ).length ) || ( mw.EmbedTypes.getMediaPlayers().getMIMETypePlayers( 'audio/mpeg' ).length && this.embedPlayer.mediaElement.getSources( 'audio/mpeg' ).length ) ){ // No firefox link if a h.264 or flash/flv stream is present return false; } // Should issue the native warning return true; }, /** * Does a native warning check binding to the player on mouse over. * @param {string} preferenceId The preference Id * @param {object} warningMsg The jQuery object warning message to be displayed. * */ /** * Display a warning message on the player * checks a preference Id to enable or disable it. * @param {string} preferenceId The preference Id * @param {object} warningMsg The jQuery object warning message to be displayed. * @param {boolean} if the hide ui should be exposed * */ addWarningBinding: function( preferenceId, warningMsg, hideDisableUi ) { mw.log( 'mw.PlayerControlBuilder: addWarningBinding: ' + preferenceId + ' wm: ' + warningMsg); // Set up local pointer to the embedPlayer var embedPlayer = this.embedPlayer; var _this = this; // make sure the player is large enough if( embedPlayer.getWidth() < 200 ){ return false; } // Can be uncommented to reset hide prefrence //$.cookie( preferenceId, '' ); // Check if a cookie has been set to hide the warning: if ( mw.config.get( preferenceId ) === true && $.cookie( preferenceId ) == 'hidewarning' ){ return ; } var warnId = "warningOverlay_" + embedPlayer.id; $( '#' + warnId ).remove(); // Add the targetWarning: var $targetWarning = $('
') .attr( { 'id': warnId } ) .addClass( 'ui-corner-all' ) .css({ 'position' : 'absolute', 'background' : '#FFF', 'color' : '#111', 'top' : '10px', 'left' : '10px', 'right' : '10px', 'padding' : '4px', // z-index should be > than play button, as well as greater // than the dialog box (in pop up video), or link won't work. 'z-index' : '1502', }) .html( warningMsg ); embedPlayer.getInterface().append( $targetWarning ); $targetWarning.append( $('
') ); // check if we should show the checkbox if( !hideDisableUi ){ $targetWarning.append( $( '' ) .attr({ 'id' : 'ffwarn_' + embedPlayer.id, 'name' : 'ffwarn_' + embedPlayer.id }) .click( function() { mw.log("WarningBindinng:: set " + preferenceId + ' to hidewarning ' ); // Set up a cookie for 30 days: $.cookie( preferenceId, 'hidewarning', {expires: 30} ); // Set the current instance mw.setConfig( preferenceId, false ); $( '#warningOverlay_' + embedPlayer.id ).fadeOut( 'slow' ); // set the local preference to false _this.addWarningFlag = false; } ) ); $targetWarning.append( $('