diff options
Diffstat (limited to 'extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins')
10 files changed, 3966 insertions, 0 deletions
diff --git a/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/EmbedPlayer.css b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/EmbedPlayer.css new file mode 100644 index 00000000..9699c2e9 --- /dev/null +++ b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/EmbedPlayer.css @@ -0,0 +1,166 @@ +.ui-dialog-content .mediaContainer { margin: 0 auto; } +.mwPlayerContainer, video { width: 100%; height: 100%; } +.mwPlayerContainer { position: relative; height: 100%; background: #000; } +.videoHolder { + position: absolute; + top: 0px; + left:0px; + right:0px; + bottom:0px; + overflow: hidden; +} +.mwPlayerContainer.fullscreen { + position: absolute !important; + width: 100% !important; + height: 100%! important; + z-index: 9999; + min-height: 100%; + top: 0; + left: 0; + margin: 0; +} + +.mwEmbedPlayer { width: 100%; height: 100%; overflow: hidden; position: absolute; top: 0; left: 0; } + + +.player_select_list { + color:white; + font-size:10pt; +/* display:none;*/ +} +.player_select_list a:visited { + color:white; +} +.mv_playhead { + position:absolute; + top:0; + left:0; + width:17px; + height:21px; + /*http://art.gnome.org/themes/gtk2*/ +} + +.mv_status { + font-family:"Times New Roman", Times, serif; + font-size:14px; + float:left; +} +.set_ogg_player_pref{ + text-align:left; +} + +.large_play_button { + display:block; + width: 130px; + height: 96px; + margin: auto; +/* margin: -202px 0 0 154px;*/ + position: absolute; + z-index: 3; + cursor: pointer; +} + +/* jquery.ui overrides */ + +.ui-icon_link { + padding: .4em 1em .4em 20px; + text-decoration: none; + position: relative; +} +.ui-icon_link span.ui-icon { + margin: 0 5px 0 0; + position: absolute; + left: 0.2em; + right: auto; + top: 50%; + margin-top: -8px; + zoom: 1; +} +.ui-icon_link span.ui-text { + position: absolute; + left: 0.2em; + right: auto; + margin-top: -3px; + zoom: 1; +} + +.ui-progressbar-value{ + background-image: none; +} + +.kplayer .ui-widget-overlay { + background: black; opacity: .40; filter: Alpha(Opacity=40); +} + +.kplayer .ui-widget-content input { + padding: 5px; +} +.kplayer .ui-widget-content a { + color: #222; +} + +ul.ui-provider-selection { + list-style-type: none; + margin: 0 0 0.6em 0; + overflow: hidden; + padding: 0; + text-align: center; +} + +ul.ui-provider-selection li { + border-left: 1px solid black; + float: left; + line-height: 1.1em; + margin: 0 0.5em 0 -0.5em; + padding: 0 0.5em; + color: blue; + list-style-image:none; + cursor:pointer; +} + +ul.ui-provider-selection li .ui-selected { + color: black; + font-weight: bold; +} + +ul.ui-provider-selection li a.ui-active { + color: black; + font-weight: bold; +} + +ul.ui-provider-selection li a { + color: blue; + text-decoration: none; +} +.fg-menu .ui-icon{ + position:relative; + top:-1px; +} + +.ui-dialog-buttonpane a{ + float: right; + margin-right: 10px; +} + + +/* Custom */ +.mv-player .overlay-win { background: transparent; border: 0; } /* Custom */ +.mv-player .overlay-content { padding: 10px; } +.mv-player .overlay-content h3 { display: block; font-size: 16px; font-weight: bold; color: #fff; font-family: arial; } +.mv-player .overlay-win h2 { font-size: 18px; margin-top: 0; } +.mv-player .overlay-content div { font-size: 12px; color: #fff; font-weight: bold; } +.mv-player .overlay-content div a { color: #00a8ff } +.mv-player .overlay-content div a:hover { color: #3abcff } +.mv-player .overlay-content ul { list-style: none; margin: 0 0 10px 0; padding: 0; } +.mv-player .vol_container { + background: #272727; + opacity: .80; + filter:Alpha(Opacity=80); + position:absolute; + left:0px; +} +.mv-player .ui-icon ui-icon-closethick { border: 1px solid #606060; background: #222;font-weight: normal; color: #EEE; } +.mv-player .overlay-win textarea { background: #e4e4e4; height: 35px; padding: 6px; color: #666; border: 0; } +.mv-player .overlay-content .copycode { padding: 8px 12px; font-weight: bold; float: right; cursor: pointer; } +.control-bar .ui-icon_link { border: 0; } +.control-bar .ui-state-hover { border: 0; } diff --git a/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/PlayerSkinKskin.css b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/PlayerSkinKskin.css new file mode 100644 index 00000000..f6c675d7 --- /dev/null +++ b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/PlayerSkinKskin.css @@ -0,0 +1,484 @@ +/* +* K-skin player +*/ +.k-player { + color: #FFF; + background-color: #000; +} +.k-player .videoHolder a { + color: #0645AD; +} +.k-player .videoHolder a:visited { + color: #0B0080; +} +.k-player .ui-widget-content { + color: #555; + z-index: 1503; +} +.k-player .ui-widget-content a{ + color: #555; +} +/* large play button */ +.k-player .play-btn-large { + width: 70px; + height: 55px; + background: url(images/ksprite.png) no-repeat 0px -433px; + position: absolute; + cursor: pointer; + border: none; +} +@print { + .k-player .play-btn-large { + display: none; + } +} +/*.ui-state-default */ +.k-player .play-btn-large:hover { + background: url(images/ksprite.png) no-repeat 0px -377px; +} + +/* control icons: */ +.k-player .control-bar .ui-icon,.k-player .control-bar .ui-icon{ + background: transparent url(images/ksprite.png) no-repeat scroll 0 -48px; +} + +.k-player .ui-state-default .ui-icon-arrow-4-diag { + background-position: 0 -32px; +} +/* fullscreen */ +.k-player .ui-state-hover .ui-icon-arrow-4-diag { + background-position: -16px -32px; +} + +.k-player .ui-state-hover .ui-icon-volume-on{ + background-position: -16px -48px; +} + +/* cc icon */ +.k-player .ui-state-default .ui-icon-comment { + background-position: 0px -65px; +} + +.k-player .ui-state-default .ui-icon-play { + background: url(images/ksprite.png) no-repeat 0 0; +} + +.k-player .ui-state-hover .ui-icon-play { + background-position: -16px 0; +} + +.k-player .ui-state-default .ui-icon-pause { + background: url(images/ksprite.png) no-repeat 0 -17px; +} + +.k-player .ui-state-hover .ui-icon-pause { + background-position: -16px -17px; +} + +.k-player .control-bar { + border:1px solid #c8c8c8; + border-top: 0px; + border-right: 0px; + height: 21px; + padding: 2px 0 0 6px; + margin-top: 0px; + background: url(images/ksprite.png) repeat-x 0 -81px; + font: normal 11px arial, sans-serif; + color: #555; + + position:absolute; + bottom:0px; + left:0px; + right:0px; + + z-index: 2; +} + +.k-player .play_head { + background: url("images/ksprite.png") repeat-x scroll 0 -350px + transparent; + display: inline; + /* @noflip */ + float: left; + margin-left: 10px; + border: 1px solid #EEEEEE; + height: 8px; + margin: 5px 2px 0 0px; + position: relative; + /* @noflip */ + direction: ltr; +} + +.k-player .play_head .ui-slider-handle { + background: url("images/ksprite.png") no-repeat scroll -67px -341px + transparent !important; + border: 1px solid #888888; + display: block; + height: 8px; + margin: -1px 0 0 -5px; + position: absolute; + top: 0; + width: 8px; + cursor: pointer; + -moz-border-radius:5px 5px 5px 5px; + border-radius:5px 5px 5px 5px; + -webkit-border-radius:5px 5px 5px 5px; +} + +.k-player .ui-corner-all { + border-radius:5px 5px 5px 5px !important; + -webkit-border-radius:5px 5px 5px 5px !important; + -moz-border-radius:5px 5px 5px 5px !important; +} +.k-player ul.fg-menu{ + margin: 0.3em 0 0 .3em; + font-size: 1.2em; + padding: 0px; +} +.k-player .fg-menu-container{ + padding: 0px; + /* @noflip */ + right: 16px; + /* Add scroll bar to list */ + overflow: auto; +} + +.ui-dialog-content .k-player ul.fg-menu{ + font-size: 1.2em; +} + +.k-player .time-disp { + border: medium none; + display: inline; + color: #555555; + font: 11px arial, sans-serif; + line-height: 20px; + overflow: hidden; + width: 39px; + /* @noflip */ + float: right; +} + +.k-player .source-switch { + border: medium none; + display: inline; + color: #555; + font: 11px arial, sans-serif; + line-height: 20px; + overflow: hidden; + width: 70px; + cursor: pointer; + /* @noflip */ + float: right; + text-align: center; +} + +.k-player .lButton { + cursor: pointer; + /* @noflip */ + float: left; + list-style: none outside none; + margin: 2px; + padding: 0px 0; + width: 19px; + height: 16px; + position: relative; + background: none repeat scroll 0 0 transparent !important; + border: medium none; +} + +.k-player .rButton { + cursor: pointer; + /* @noflip */ + float: right; + list-style: none outside none; + margin-top: 2px; + padding: 0px 0; + width: 22px; + height: 16px; + position: relative; + background: none repeat scroll 0 0 transparent !important; + border: medium none; +} + +.k-player .k-options { + border: 1px solid #AAAAAA !important; + color: #555555 !important; + /* @noflip */ + float: right; + height: 21px; + margin-top: -2px; + margin-right: 0px; + width: 50px; + background: none repeat scroll 0 0 transparent !important; + font-family: Lucida Grande, Lucida Sans, Arial, sans-serif; + font-size: 11px; + text-transform: uppercase; + text-align: center; +} + +.k-player .k-options span { + position: relative; + top: 4px; +} + +.k-player .k-menu-screens { + /* @noflip */ + float: left; + font-size: 14px; + text-align: left; + padding: 5px 5px 10px 5px; +} + +.k-player ul.k-menu-bar { + background: url("images/ksprite.png") no-repeat scroll -99px -104px + transparent; + bottom: 5px; + height: 128px; + list-style: none outside none; + padding: 0 0 5px; + position: absolute; + /* @noflip */ + right: 0; + margin-left: 0; +} + +.k-player .k-menu { + background: none repeat scroll 0 0 #181818; + border: medium none; + display: none; + left: 0; + position: absolute; + top: 0; +} + +.k-player .k-menu-bar li a { + background: url("images/ksprite.png") no-repeat scroll -51px -110px + transparent; + display: block; + height: 32px; + margin-left: 1px; + overflow: hidden; + text-indent: 99999px; + width: 49px; +} + +.k-menu-bar li a:hover { + background-position: -1px -110px; +} + +.k-menu-bar li.k-download-btn a { + background-position: -51px -203px; +} + +.k-menu-bar li.k-download-btn a:hover { + background-position: -1px -203px; +} + +.k-menu-bar li.k-share-btn a { + background-position: -51px -172px; +} + +.k-menu-bar li.k-share-btn a:hover { + background-position: -1px -172px; +} + +.k-menu-bar li.k-credits-btn a { + background-position: -51px -141px; +} + +.k-menu-bar li.k-credits-btn a:hover { + background-position: -1px -141px; +} + + + +.k-menu-screens p { + margin: 6px 0; +} + +.k-menu-screens a img { + border: none; +} + +.k-menu-screens ul { + padding: 0; + margin: 6px 0 0; + list-style: none outside none; +} + +.k-edit-screen { + width: 370px; + height: 223px; + padding-top: 77px; + text-align: center; + background: #181818; + color: #fff; +} + +.k-edit-screen div { + +} + +.k-edit-screen a { + color: #7BB8FC; +} + +.k-edit-screen a img { + border: none; +} + + +.k-menu-screens h2, .k-menu-screens h3 { + padding: 0 0 5px 15px; + clear: both; + font-size: 12px; + color: #999; + border-bottom: 0; +} + +.k-menu-screens p { + margin: 6px 0; +} + +.k-menu-screens a img { + border: none; +} + +.k-menu-screens ul { + padding: 0; + margin: 6px 0 0; + list-style: none outside none; +} + +.k-menu-screens li { + margin-bottom: 6px; +} + +.k-menu-screens li a { + padding-left: 22px; + padding-right: 22px; + background: url(images/ksprite.png) no-repeat -85px -274px; + text-decoration: none; + color: #BBB; +} + +.k-menu-screens li a.active,.k-menu-screens li a:hover .active { + background-position: -85px -245px; +} + +.k-menu-screens li a:hover { + background-position: -85px -259px; +} + +.k-menu textarea { + background: none repeat scroll 0 0 transparent; + border-color: #000000 -moz-use-text-color -moz-use-text-color #000000; + border-style: solid none none solid; + border-width: 2px medium medium 2px; + color: #CCCCCC; + font: 11px arial, sans-serif; + overflow: hidden; + padding-left: 2px; + width: 95%; +} + +.menu-screen.menu-share button { + background: url("images/ksprite.png") no-repeat scroll 0 -81px #D4D4D4; + border: 1px solid #000000; + color: #000000; + float: right; + height: 34px; + padding: 0 5px 3px; + font-size: 1em; +} + +.k-player .menu-screen { + height: 100%; + overflow-y: auto; + overflow-x: hidden; +} + + +.k-player .menu-screen.menu-share div.ui-state-highlight { + background: none repeat scroll 0 0 transparent; + border-color: #554926; + color: #FFE96E; + float: left; + padding: 2px 5px; +} + +.k-player .menu-screen.menu-share div.ui-state-highlight a { + color: #FFE96E; + font-weight: bold; +} + +.k-player .volume_control { + /* @noflip */ + margin-right: 2px; + width: 16px; +} + +.k-player .volume_control span { + margin-right: 0px; +} + +.k-player .volume-slider { + width: 20px; + /* @noflip */ + direction: ltr; +} + +.k-player .volume-slider .ui-slider-range { + -moz-border-radius: 0 0 0 0; + background: url("images/ksprite.png") repeat-x scroll -66px -306px transparent !important; + height: 17px; + position: absolute; +} + +.k-player .volume-slider a.ui-slider-handle { + background: none repeat scroll 0 0 transparent; + border: medium none; + display: block; + height: 18px; + margin: -3px 5px 0 -1px; + position: absolute; + width: 8px; +} + +.k-player .ui-slider-horizontal .ui-slider-range-min { + /* @noflip */ + left: 0; +} + +.k-player .credits_box { + background-attachment:scroll; + background-color:white; + background-image:none; + background-position:0 0; + bottom: 20px; + left: 20px; + position:absolute; + right: 20px; + top: 30px; + overflow:hidden; +} +.k-player .credits_box a{ + color:#666; + text-decoration: underline; +} +.k-player .creditline img { + float: left; + width: 90px; + margin: 4px; +} + +.k-player .k-attribution{ + position:absolute; + bottom: 5px; + right : 20px; + background: url("images/kaltura_open_source_video_platform.png"); + width : 51px; + height : 12px; + cursor: pointer; +} diff --git a/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/images/kaltura_open_source_video_platform.gif b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/images/kaltura_open_source_video_platform.gif Binary files differnew file mode 100644 index 00000000..cf05af25 --- /dev/null +++ b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/images/kaltura_open_source_video_platform.gif diff --git a/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/images/kaltura_open_source_video_platform.png b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/images/kaltura_open_source_video_platform.png Binary files differnew file mode 100644 index 00000000..77f2a32a --- /dev/null +++ b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/images/kaltura_open_source_video_platform.png diff --git a/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/images/ksprite.png b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/images/ksprite.png Binary files differnew file mode 100644 index 00000000..53c772eb --- /dev/null +++ b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/images/ksprite.png diff --git a/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/mw.PlayerSkinKskin.js b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/mw.PlayerSkinKskin.js new file mode 100644 index 00000000..39b43834 --- /dev/null +++ b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/mw.PlayerSkinKskin.js @@ -0,0 +1,394 @@ +/** +* Skin js allows you to override contrlBuilder html/class output +*/ + +( function( mw, $ ) {"use strict"; + +mw.PlayerSkinKskin = { + + // The parent class for all kskin css: + playerClass: 'k-player', + + // Display time string length + longTimeDisp: false, + + // Default control bar height + height: 20, + + // Volume control layout is horizontal + volumeLayout: 'horizontal', + + // Skin "kskin" is specific for wikimedia we have an + // api Title key so the "credits" menu item can be showed. + supportedMenuItems: { + 'credits': true + }, + // Stores the current menu item id + currentMenuItem: null, + + // Extends base components with kskin specific options: + components: { + 'pause': { + 'w': 28 + }, + 'volumeControl': { + 'w': 40 + }, + 'playButtonLarge' : { + 'h' : 55 + }, + 'options': { + 'w': 52, + 'o': function( ctrlObj ) { + return $( '<div />' ) + .attr( 'title', mw.msg( 'mwe-embedplayer-player_options' ) ) + .addClass( "ui-state-default ui-corner-bl rButton k-options" ) + .append( + $( '<span />' ) + .text( mw.msg( 'mwe-embedplayer-menu_btn' ) ) + ); + } + }, + // No attributionButton component for kSkin ( its integrated into the credits screen ) + 'attributionButton' : false, + + // Time display: + 'timeDisplay': { + 'w': 52 + }, + 'optionsMenu': { + 'w' : 0, + 'o' : function( ctrlObj ) { + var embedPlayer = ctrlObj.embedPlayer; + var $menuOverlay = $( '<div />') + .addClass( 'overlay-win k-menu ui-widget-content' ) + .css( { + 'width' : '100%', + 'position': 'absolute', + 'top' : '0px', + 'bottom' : ( ctrlObj.getHeight() + 2 ) + 'px' + } ); + + // Note safari can't display video overlays with text: + // see bug https://bugs.webkit.org/show_bug.cgi?id=48379 + + var userAgent = navigator.userAgent.toLowerCase(); + if( userAgent.indexOf('safari') != -1 ){ + $menuOverlay.css('opacity', '0.9'); + } + // Setup menu offset ( if player height < getOverlayHeight ) + // This displays the menu outside of the player on small embeds + if ( embedPlayer.getPlayerHeight() < ctrlObj.getOverlayHeight() ) { + var topPos = ( ctrlObj.isOverlayControls() ) + ? embedPlayer.getPlayerHeight() + : embedPlayer.getPlayerHeight() + ctrlObj.getHeight(); + + if( embedPlayer.isAudio() ){ + topPos = ctrlObj.embedPlayer.getInterface().height(); + } + + $menuOverlay.css( { + 'top' : topPos + 'px', + 'bottom' : null, + 'width' : ctrlObj.getOverlayWidth(), + 'height' : ctrlObj.getOverlayHeight() + 'px' + } ); + // Special common overflow hack for thumbnail display of player + $( embedPlayer ).parents( '.thumbinner' ).css( 'overflow', 'visible' ); + } + + var $menuBar = $( '<ul />' ) + .addClass( 'k-menu-bar' ); + + // Don't include about player menu item ( FIXME should be moved to a init function ) + delete ctrlObj.supportedMenuItems['aboutPlayerLibrary']; + + // Output menu item containers: + for ( var menuItem in ctrlObj.supportedMenuItems ) { + // Give grep a chance to find the usages: + // mwe-embedplayer-playerSelect, mwe-embedplayer-download, + // mwe-embedplayer-share, mwe-embedplayer-credits + $menuBar.append( + $( '<li />') + // Add the menu item class: + .addClass( 'k-' + menuItem + '-btn' ) + .attr( 'rel', menuItem ) + .append( + $( '<a />' ) + .attr( { + 'title' : mw.msg( 'mwe-embedplayer-' + menuItem ), + 'href' : '#' + }) + ) + ); + } + + // Add the menuBar to the menuOverlay + $menuOverlay.append( $menuBar ); + + var $menuScreens = $( '<div />' ) + .addClass( 'k-menu-screens' ) + .css( { + 'position' : 'absolute', + 'top' : '0px', + 'left' : '0px', + 'bottom' : '0px', + 'right' : '45px', + 'overflow' : 'hidden' + } ); + for ( var menuItem in ctrlObj.supportedMenuItems ) { + $menuScreens.append( + $( '<div />' ) + .addClass( 'menu-screen menu-' + menuItem ) + ); + } + + // Add the menuScreens to the menuOverlay + $menuOverlay.append( $menuScreens ); + + return $menuOverlay; + + } + } + }, + + /** + * Get minimal width for interface overlay + */ + getOverlayWidth: function(){ + return ( this.embedPlayer.getPlayerWidth() < 220 )? 220 : this.embedPlayer.getPlayerWidth(); + }, + + /** + * Get minimal height for interface overlay + */ + getOverlayHeight: function(){ + return ( this.embedPlayer.getPlayerHeight() < 160 )? 160 : this.embedPlayer.getPlayerHeight(); + }, + + /** + * Adds the skin Control Bindings + */ + addSkinControlBindings: function() { + var embedPlayer = this.embedPlayer; + var _this = this; + + // Set up control bar pointer + this.$playerTarget = embedPlayer.$interface; + // Set the menu target: + + + // Options menu display: + this.$playerTarget.find( '.k-options' ) + .unbind() + .click( function() { + _this.checkMenuOverlay(); + var $kmenu = _this.$playerTarget.find( '.k-menu' ); + if ( $kmenu.is( ':visible' ) ) { + _this.closeMenuOverlay( ); + } else { + _this.showMenuOverlay(); + // no other item is selected by default show the media credits: + if ( !_this.currentMenuItem ){ + _this.showMenuItem('credits'); + // Hide the others + _this.$playerTarget.find( '.menu-screen' ).hide(); + // Show credits + _this.$playerTarget.find( '.menu-credits' ).fadeIn( "fast" ); + } + } + } ); + + }, + + /** + * checks for menu overlay and runs menu bindings if unset + */ + checkMenuOverlay: function(){ + var _this = this; + var embedPlayer = this.embedPlayer; + if ( _this.$playerTarget.find( '.k-menu' ).length == 0 ) { + // Stop the player if it does not support overlays: + if ( !embedPlayer.supports['overlays'] ) { + embedPlayer.stop(); + } + + // Add the menu binding + _this.addMenuBinding(); + } + }, + + /** + * Close the menu overlay + */ + closeMenuOverlay: function() { + mw.log("PlayerSkinKskin:: close menu overlay" ); + var embedPlayer = this.embedPlayer; + var $optionsMenu = embedPlayer.getInterface().find( '.k-options' ); + var $kmenu = embedPlayer.getInterface().find( '.k-menu' ); + $kmenu.fadeOut( "fast", function() { + $optionsMenu.find( 'span' ) + .text ( mw.msg( 'mwe-embedplayer-menu_btn' ) ); + } ); + // show the play button if not playing + if( !embedPlayer.isPlaying() ){ + embedPlayer.getInterface().find( '.play-btn-large' ).fadeIn( 'fast' ); + } + + // re-display the control bar if hidden: + this.showControlBar(); + + // Set close overlay menu flag: + this.displayOptionsMenuFlag = false; + }, + + /** + * Show the menu overlay + */ + showMenuOverlay: function( $ktxt ) { + var $optionsMenu = this.$playerTarget.find( '.k-options' ); + var $kmenu = this.$playerTarget.find( '.k-menu' ); + + $kmenu.fadeIn( "fast", function() { + $optionsMenu.find( 'span' ) + .text ( mw.msg( 'mwe-embedplayer-close_btn' ) ); + } ); + this.$playerTarget.find( '.play-btn-large' ).fadeOut( 'fast' ); + + $(this.embedPlayer).trigger( 'displayMenuOverlay' ); + + // Set the Options Menu display flag to true: + this.displayOptionsMenuFlag = true; + }, + + /** + * Adds binding for the options menu + * + * @param {Object} $tp Target video container for + */ + addMenuBinding: function() { + var _this = this; + var embedPlayer = this.embedPlayer; + // Set local player target pointer: + var $playerTarget = embedPlayer.$interface; + + // Check if k-menu already exists: + if ( $playerTarget.find( '.k-menu' ).length != 0 ) + return false; + + // Add options menu to top of player target children: + $playerTarget.append( + _this.getComponent( 'optionsMenu' ) + ); + + // By default its hidden: + $playerTarget.find( '.k-menu' ).hide(); + + // Add menu-items bindings: + for ( var menuItem in _this.supportedMenuItems ) { + $playerTarget.find( '.k-' + menuItem + '-btn' ).click( function( ) { + + // Grab the context from the "clicked" menu item + var mk = $( this ).attr( 'rel' ); + + // hide all menu items + var $targetItem = $playerTarget.find( '.menu-' + mk ); + + // call the function showMenuItem + _this.showMenuItem( mk ); + + // Hide the others + $playerTarget.find( '.menu-screen' ).hide(); + + // Show the target menu item: + $targetItem.fadeIn( "fast" ); + + // Don't follow the # link + return false; + } ); + } + }, + + /** + * Shows a selected menu_item + * + * NOTE: this should be merged with parent mw.PlayerControlBuilder optionMenuItems + * binding mode + * + * @param {String} menu_itme Menu item key to display + */ + showMenuItem:function( menuItem ) { + var embedPlayer = this.embedPlayer; + this.currentMenuItem = menuItem; + //handle special k-skin specific display; + switch( menuItem ){ + case 'credits': + this.showCredits(); + break; + case 'playerSelect': + embedPlayer.$interface.find( '.menu-playerSelect').html( + this.getPlayerSelect() + ); + break; + case 'download' : + embedPlayer.$interface.find( '.menu-download').text( + mw.msg('mwe-loading_txt' ) + ); + // Call show download with the target to be populated + this.showDownload( + embedPlayer.$interface.find( '.menu-download') + ); + break; + case 'share': + embedPlayer.$interface.find( '.menu-share' ).html( + this.getShare() + ); + break; + } + }, + + /** + * Show the credit screen ( presently specific to kaltura skin ) + */ + showCredits: function() { + // Set up the shortcuts: + var embedPlayer = this.embedPlayer; + var _this = this; + var $target = embedPlayer.$interface.find( '.menu-credits' ); + + $target.empty().append( + $('<h2 />') + .text( mw.msg( 'mwe-embedplayer-credits' ) ), + $('<div />') + .addClass( "credits_box ui-corner-all" ) + .append( + $('<div/>') + .loadingSpinner() + .css({'position':'absolute','top':'50%','left':'50%'}) + ) + ); + + if( mw.config.get( 'EmbedPlayer.KalturaAttribution' ) == true ){ + $target.append( + $( '<div />' ) + .addClass( 'k-attribution' ) + .attr({ + 'title': mw.msg('mwe-embedplayer-kaltura-platform-title') + }) + .click( function( ) { + window.location = 'http://html5video.org'; + }) + ); + } + var $creditBox = $target.find('.credits_box'); + $creditBox.data( 'playerId', embedPlayer.id ); + $( embedPlayer ).triggerQueueCallback('showCredits', $creditBox, function( addedCredits ){ + if( !addedCredits ){ + $creditBox.find('.credits_box').text( mw.msg( 'mwe-embedplayer-nocredits') ) + } + }); + } + +}; + +} )( mw, jQuery ); diff --git a/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mvpcf/PlayerSkinMvpcf.css b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mvpcf/PlayerSkinMvpcf.css new file mode 100644 index 00000000..a9664ec5 --- /dev/null +++ b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mvpcf/PlayerSkinMvpcf.css @@ -0,0 +1,194 @@ +/** + * reference player skin + */ + + +/*.ui-state-default */ +.mv-player a:link {color: #2060c1; text-decoration: underline;} +.mv-player a:visited {color: #2060c1; text-decoration: underline;} +/*a:visited {color: #75a5e4; text-decoration: underline;}*/ /*Not sure if you want this*/ +.mv-player a:hover {color: #75a5e4; text-decoration: underline;} +.mv-player img, .mv-player img a, .mv-player img a:hover {border: 0;} + + +.mv-player .video { + display: block; + position: relative; + font-size: 1px; + height: 305px; +} +.mv-player .control-bar { + overflow: hidden; + height: 29px; + margin: 0; + padding: 0; + border: 0; + z-index: 2; +} +.mv-player .controlInnerSmall { +/* width: 430px;*/ + height: 29px; + float: left; + display: inline; +} + +.mv-player .lButton { + cursor:pointer; + float:left; + list-style:none outside none; + margin:2px; + padding:4px 0; + width: 24px; + height:16px; + position:relative; +} +.mv-player .rButton { + cursor:pointer; + float:right; + list-style:none outside none; + margin:2px; + padding:4px 0; + width: 23px; + height:16px; + position:relative; +} + +.mv-player .volume_icon { + float: right; + display: inline; + width: 22px; + height: 29px; + padding: 0 0 0 0; + +} + +.mv-player .vol_container{ + z-index:99; + width:23px; + height:75px; + width:23px; + position:absolute; + left:0px; + background: #CCC; +} +.mv-player .vol_container_below{ + top:30px; +} +.mv-player .vol_container_top{ + top:-77px; +} +.mv-player .vol_container .volume-slider{ + margin-top:5px; + height:65px; + width:10px; + margin-left: auto ; + margin-right: auto ; +} +.mv-player .vol_container .ui-slider-handle{ + cursor : pointer; + width:10px; + height:10px; + position:absolute; + left:-1px; +} + +.mv-player .time-disp { + line-height: 32px; + height: 29px; + overflow: visible; + font-size: 10.2px; + float: right; + display: inline; + border:none; + padding-right:4px; +} + +.mv-player .source-switch { + border: medium none; + display: inline; + color: #eee; + font: 11px arial, sans-serif; + line-height: 20px; + overflow: hidden; + width: 70px; + cursor: pointer; + float: right; + text-align: center; + padding-top:6px; +} + + +.mv-player .play_head{ + float: left; + display: inline; + height: 10px; + margin-left:8px; + margin-top:10px; + margin-right: 8px; + position:relative; +} + +.mv-player .play_head .ui-slider-handle{ + width:10px; + height:15px; + margin-left:-5px; + margin-top: -0px; + z-index: 2; +} + +.mv-player .inOutSlider .ui-slider-handle{ + width:8px; + cusror: move; +} + +.mv-player .overlay-win textarea { + background:none repeat scroll 0 0 transparent; + border: 2px solid #333; + color: #fff; + font: 11px arial,sans-serif; + height:15px; + overflow:hidden; + padding-left:2px; + width:97%; +} + +.mv-player .overlay-win div.ui-state-highlight { + background:none repeat scroll 0 0 transparent; + border-color:#554926; + color:#FFE96E; + float:left; + padding:2px 5px; +} + +.mv-player .videoOptionsComplete div.ui-state-highlight a { + color:#eee; + font-weight:bold; +} + +.mv-player .overlay-win h2{ + font-size: 115%; +} + +.mv-player .overlay-win{ + font-family : arial,sans-serif; + font-size : 85%; +} +.mv-player .overlay-win a{ + text-decoration: none; +} + +.mv-player .overlay-win ul{ + padding-left: 15px; +} + +.mv-player a:hover {} + +.mv-player .overlay-win ul li span { font-weight:bold; color:#fff;} + +.mv-player .overlay-win h2 { font-size:16px;} +.mv-player .overlay-win h3 { font-size:14px;} + +.active { font-size: 12px; } + +.ui-slider-horizontal.volume-slider { width: 44px; height: 2px; top: 7px; } +.ui-slider-horizontal.volume-slider .ui-slider-handle { border-width: 1px; } diff --git a/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mvpcf/images/player_big_play_button.png b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mvpcf/images/player_big_play_button.png Binary files differnew file mode 100644 index 00000000..155f15e1 --- /dev/null +++ b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mvpcf/images/player_big_play_button.png diff --git a/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mvpcf/mw.PlayerSkinMvpcf.js b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mvpcf/mw.PlayerSkinMvpcf.js new file mode 100644 index 00000000..4b270418 --- /dev/null +++ b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mvpcf/mw.PlayerSkinMvpcf.js @@ -0,0 +1,7 @@ +/* +mvpcf skin config +*/ + +mw.PlayerSkinMvpcf = { + playerClass : 'mv-player' +};
\ No newline at end of file diff --git a/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mw.PlayerControlBuilder.js b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mw.PlayerControlBuilder.js new file mode 100644 index 00000000..5ef54d4f --- /dev/null +++ b/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mw.PlayerControlBuilder.js @@ -0,0 +1,2721 @@ +/** +* 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 = $('<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'); + } + + // 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<components.length;i++) { + $controlBar.append( + _this.getComponent( components[ i ]['id'] ) + ); + } + } + + // 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' ){ + continue; + } + if( componentId == 'timeDisplay' && !mw.config.get( 'EmbedPlayer.EnableTimeDisplay' ) ){ + continue; + } + + // Skip "fullscreen" button for assets or where height is 0px ( audio ) + if( componentId == 'fullscreen' && this.embedPlayer.isAudio() ){ + continue; + } + // Skip sourceSwitch if width < smalles derivative + if ( componentId == 'sourceSwitch' && this.availableWidth < 320) { + continue; + } + addComponent( componentId ); + } + if( this.availableWidth > 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( $( '<meta />' ).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( + $( '<div />' ) + .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', + $('<h3/>').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', + $('<div>').append( + $('<a />') + .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 = $('<div />') + .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( + $('<br />') + ); + // check if we should show the checkbox + if( !hideDisableUi ){ + + $targetWarning.append( + $( '<input type="checkbox" />' ) + .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( + $('<label />') + .text( mw.msg( 'mwe-embedplayer-do_not_warn_again' ) ) + .attr( 'for', 'ffwarn_' + embedPlayer.id ) + ); + } + + return $targetWarning; + }, + + /** + * Binds the volume controls + */ + doVolumeBinding: function( ) { + var embedPlayer = this.embedPlayer; + var _this = this; + embedPlayer.getInterface().find( '.volume_control' ).unbind().buttonHover().click( function() { + mw.log( 'Volume control toggle' ); + embedPlayer.toggleMute(); + } ); + + // Add vertical volume display hover + if ( this.volumeLayout == 'vertical' ) { + // Default volume binding: + var hoverOverDelay = false; + var $targetvol = embedPlayer.getInterface().find( '.vol_container' ).hide(); + embedPlayer.getInterface().find( '.volume_control' ).hover( + function() { + $targetvol.addClass( 'vol_container_top' ); + // Set to "below" if playing and embedType != native + if ( embedPlayer && embedPlayer.isPlaying && embedPlayer.isPlaying() && !embedPlayer.supports['overlays'] ) { + $targetvol.removeClass( 'vol_container_top' ).addClass( 'vol_container_below' ); + } + $targetvol.fadeIn( 'fast' ); + hoverOverDelay = true; + }, + function() { + hoverOverDelay = false; + setTimeout( function() { + if ( !hoverOverDelay ) { + $targetvol.fadeOut( 'fast' ); + } + }, 500 ); + } + ); + } + var userSlide=false; + // Setup volume slider: + var sliderConf = { + range: "min", + value: 80, + min: 0, + max: 100, + slide: function( event, ui ) { + var percent = ui.value / 100; + mw.log('PlayerControlBuilder::slide:update volume:' + percent); + embedPlayer.setVolume( percent ); + userSlide = true; + }, + change: function( event, ui ) { + var percent = ui.value / 100; + if ( percent == 0 ) { + embedPlayer.getInterface().find( '.volume_control span' ).removeClass( 'ui-icon-volume-on' ).addClass( 'ui-icon-volume-off' ); + } else { + embedPlayer.getInterface().find( '.volume_control span' ).removeClass( 'ui-icon-volume-off' ).addClass( 'ui-icon-volume-on' ); + } + mw.log('PlayerControlBuilder::change:update volume:' + percent); + embedPlayer.setVolume( percent, userSlide ); + userSlide = false; + } + }; + + if ( this.volumeLayout == 'vertical' ) { + sliderConf[ 'orientation' ] = "vertical"; + } + + embedPlayer.getInterface().find( '.volume-slider' ).slider( sliderConf ); + }, + + /** + * Get the options menu ul with li menu items + */ + getOptionsMenu: function( ) { + var $optionsMenu = $( '<ul />' ); + for( var menuItemKey in this.optionMenuItems ){ + + // Make sure its supported in the current controlBuilder config: + if( $.inArray( menuItemKey, mw.config.get( 'EmbedPlayer.EnabledOptionsMenuItems' ) ) === -1 ) { + continue; + } + + $optionsMenu.append( + this.optionMenuItems[ menuItemKey ]( this ) + ); + } + return $optionsMenu; + }, + + /** + * Allow the controlBuilder to do interface actions onDone + */ + onClipDone: function(){ + // Related videos could be shown here + }, + + /** + * The ctrl builder updates the interface on seeking + */ + onSeek: function(){ + //mw.log( "controlBuilder:: onSeek" ); + // Update the interface: + this.setStatus( mw.msg( 'mwe-embedplayer-seeking' ) ); + // add a loading spinner: + this.embedPlayer.addPlayerSpinner(); + // hide once playing again: + this.embedPlayer.hideSpinnerOncePlaying(); + }, + + /** + * Updates the player status that displays short text msgs and the play clock + * @param {String} value Status string value to update + */ + setStatus: function( value ) { + // update status: + if( this.embedPlayer.getInterface() ){ + this.embedPlayer.getInterface().find( '.time-disp' ).text( value ); + } + }, + + /** + * Option menu items + * + * @return + * 'li' a li line item with click action for that menu item + */ + optionMenuItems: { + // Share the video menu + 'share': function( ctrlObj ) { + return $.getLineItem( + mw.msg( 'mwe-embedplayer-share' ), + 'mail-closed', + function( ) { + ctrlObj.displayMenuOverlay( + ctrlObj.getShare() + ); + $( ctrlObj.embedPlayer ).trigger( 'showShareEvent' ); + } + ); + }, + + 'aboutPlayerLibrary' : function( ctrlObj ){ + return $.getLineItem( + mw.msg( 'mwe-embedplayer-about-library' ), + 'info', + function( ) { + ctrlObj.displayMenuOverlay( + ctrlObj.aboutPlayerLibrary() + ); + $( ctrlObj.embedPlayer ).trigger( 'aboutPlayerLibrary' ); + } + ); + } + }, + + /** + * Close a menu overlay + */ + closeMenuOverlay: function(){ + var _this = this; + var embedPlayer = this.embedPlayer; + var $overlay = embedPlayer.getInterface().find( '.overlay-win,.ui-widget-overlay,.ui-widget-shadow' ); + + this.displayOptionsMenuFlag = false; + //mw.log(' closeMenuOverlay: ' + this.displayOptionsMenuFlag); + + $overlay.fadeOut( "slow", function() { + $overlay.remove(); + } ); + + // Show the big play button: ( if not in an ad .. TODO clean up ) + if( embedPlayer.isStopped() && + ( + embedPlayer.sequenceProxy && + embedPlayer.sequenceProxy.isInSequence == false + ) + ){ + embedPlayer.getInterface().find( '.play-btn-large' ).fadeIn( 'slow' ); + } + + $(embedPlayer).trigger( 'closeMenuOverlay' ); + + return false; // onclick action return false + }, + + /** + * Generic function to display custom HTML overlay on video. + * + * @param {String} overlayContent content to be displayed + */ + displayMenuOverlay: function( overlayContent, closeCallback, hideCloseButton ) { + var _this = this; + var embedPlayer = this.embedPlayer; + mw.log( 'PlayerControlBuilder:: displayMenuOverlay' ); + // set the overlay display flag to true: + this.displayOptionsMenuFlag = true; + + if ( !this.supportedComponents[ 'overlays' ] ) { + embedPlayer.stop(); + } + + + // Hide the big play button: + embedPlayer.hideLargePlayBtn(); + + // Check if overlay window is already present: + if ( embedPlayer.getInterface().find( '.overlay-win' ).length != 0 ) { + //Update the content + embedPlayer.getInterface().find( '.overlay-content' ).html( + overlayContent + ); + return ; + } + + // Add an overlay + embedPlayer.getInterface().append( + $('<div />') + .addClass( 'ui-widget-overlay' ) + .css( { + 'height' : '100%', + 'width' : '100%', + 'z-index' : 2 + } ) + ); + + var $closeButton = []; + + if ( !hideCloseButton ) { + // Setup the close button + $closeButton = $('<div />') + .addClass( 'ui-state-default ui-corner-all ui-icon_link rButton') + .css({ + 'position': 'absolute', + 'cursor' : 'pointer', + 'top' : '2px', + 'right' : '2px' + }) + .click( function() { + _this.closeMenuOverlay(); + if( closeCallback ){ + closeCallback(); + } + } ) + .append( + $('<span />') + .addClass( 'ui-icon ui-icon-closethick' ) + ); + } + + var controlBarHeight = embedPlayer.getInterface().find( '.control-bar' ).height(); + var overlayWidth = (embedPlayer.getWidth() - 30); + var overlayHeight = (embedPlayer.getHeight() - (controlBarHeight + 30)); + var overlayTop = (( (embedPlayer.getInterface().height() - controlBarHeight) - overlayHeight) / 2); + var overlayLeft = ((embedPlayer.getInterface().width() - overlayWidth) / 2); + + var overlayMenuCss = { + 'height' : overlayHeight + 'px', + 'width' : overlayWidth + 'px', + 'position' : 'absolute', + 'top' : overlayTop + 'px', + 'left': overlayLeft + 'px', + 'margin': '0 10px 10px 0', + 'overflow' : 'auto', + 'padding' : '4px', + 'z-index' : 3 + }; + var $overlayMenu = $('<div />') + .addClass( 'overlay-win ui-state-default ui-widget-header ui-corner-all' ) + .css( overlayMenuCss ) + .append( + $closeButton, + $('<div />') + .addClass( 'overlay-content' ) + .append( overlayContent ) + ); + + + // Append the overlay menu to the player interface + embedPlayer.getInterface().prepend( + $overlayMenu + ) + .find( '.overlay-win' ) + .fadeIn( "slow" ); + + // Trigger menu overlay display + $( embedPlayer ).trigger( 'displayMenuOverlay' ); + + return false; // onclick action return false + }, + + /** + * Close an alert + */ + closeAlert: function( keepOverlay ) { + var embedPlayer = this.embedPlayer; + var $alert = $( '#alertContainer' ); + + mw.log( 'mw.PlayerControlBuilder::closeAlert' ); + if ( !keepOverlay || ( mw.isIpad() && this.inFullScreen ) ) { + embedPlayer.controlBuilder.closeMenuOverlay(); + if ( mw.isIpad() ) { + embedPlayer.disablePlayControls(); + } + } + + $alert.remove(); + + return false; // onclick action return false; + }, + + /** + * Generic function to display custom alert overlay on video. + * + * @param (Object) Object which includes: + * title Alert Title + * body Alert body + * buttonSet[label,callback] Array of buttons + * style CSS object + */ + displayAlert: function( alertObj ) { + var embedPlayer = this.embedPlayer; + var callback; + mw.log( 'PlayerControlBuilder::displayAlert:: ' + alertObj.title ); + // Check if callback is external or internal (Internal by default) + + // Check if overlay window is already present: + if ( embedPlayer.getInterface().find( '.overlay-win' ).length != 0 ) { + return; + } + if( typeof alertObj.callbackFunction == 'string' ) { + if ( alertObj.isExternal ) { + // TODO better support of running external JS functions, instead of window.parent + try{ + callback = window.parent[ alertObj.callbackFunction ]; + } catch ( e ){ + // could not call parent method + } + } else { + callback = window[ alertObj.callbackFunction ]; + } + } else if( typeof alertObj.callbackFunction == 'function' ) { + // Make life easier for internal usage of the listener mapping by supporting + // passing a callback by function ref + callback = alertObj.callbackFunction; + } else { + mw.log( "PlayerControlBuilder :: displayAlert :: Error: bad callback type" ); + callback = function() {}; + } + + var $container = $( '<div />' ).attr( 'id', 'alertContainer' ).addClass( 'alert-container' ); + var $title = $( '<div />' ).text( alertObj.title ).addClass( 'alert-title alert-text' ); + if ( alertObj.props && alertObj.props.titleTextColor ) { + $title.removeClass( 'alert-text' ); + $title.css( 'color', mw.getHexColor( alertObj.props.titleTextColor ) ); + } + var $message = $( '<div />' ).text( alertObj.message ).addClass( 'alert-message alert-text' ); + if ( alertObj.isError ) { + $message.addClass( 'error' ); + } + if ( alertObj.props && alertObj.props.textColor ) { + $message.removeClass( 'alert-text' ); + $message.css( 'color', mw.getHexColor( alertObj.props.textColor ) ); + } + var $buttonsContainer = $( '<div />' ).addClass( 'alert-buttons-container' ); + if ( alertObj.props && alertObj.props.buttonRowSpacing ) { + $buttonsContainer.css( 'margin-top', alertObj.props.buttonRowSpacing ); + } + var $buttonSet = alertObj.buttons || []; + + // If no button was passed display just OK button + var buttonsNum = $buttonSet.length; + if ( buttonsNum == 0 && !alertObj.noButtons ) { + $buttonSet = ["OK"]; + buttonsNum++; + } + + $.each( $buttonSet, function(i) { + var label = this.toString(); + var $currentButton = $( '<button />' ) + .addClass( 'alert-button' ) + .text( label ) + .click( function( eventObject ) { + callback( eventObject ); + embedPlayer.controlBuilder.closeAlert( alertObj.keepOverlay ); + } ); + if ( alertObj.props && alertObj.props.buttonHeight ) { + $currentButton.css( 'height', alertObj.props.buttonHeight ); + } + // Apply buttons spacing only when more than one is present + if (buttonsNum > 1) { + if (i < buttonsNum-1) { + if ( alertObj.props && alertObj.props.buttonSpacing ) { + $currentButton.css( 'margin-right', alertObj.props.buttonSpacing ); + } + } + } + $buttonsContainer.append( $currentButton ); + } ) + $container.append( $title, $message, $buttonsContainer ); + return embedPlayer.controlBuilder.displayMenuOverlay( $container, false, true ); + }, + + aboutPlayerLibrary: function(){ + return $( '<div />' ) + .append( + $( '<h2 />' ) + .text( + mw.msg('mwe-embedplayer-about-library') + ) + , + $( '<span />') + .append( + mw.msg('mwe-embedplayer-about-library-desc', + $('<div>').append( + $('<a />').attr({ + 'href' : mw.config.get( 'EmbedPlayer.LibraryPage' ), + 'target' : '_new' + }) + )[0].innerHTML + ) + ) + ); + }, + /** + * Get the "share" interface + * + * TODO share should be enabled via <embed> tag usage to be compatible + * with sites social networking sites that allow <embed> tags but not js + * + * @param {Object} $target Target jQuery object to set share html + */ + getShare: function( ) { + var embedPlayer = this.embedPlayer; + var embed_code = embedPlayer.getSharingEmbedCode(); + var embed_wiki_code = embedPlayer.getWikiEmbedCode(); + var _this = this; + + var $shareInterface = $('<div />'); + + var $shareList = $( '<ul />' ); + + $shareList + .append( + $('<li />').text( + mw.msg( 'mwe-embedplayer-embed_site_or_blog' ) + ) + /* + .append( + $('<a />') + .attr('href', '#') + .addClass( 'active' ) + .text( + mw.msg( 'mwe-embedplayer-embed_site_or_blog' ) + ) + ) + */ + ); + + $shareInterface.append( + $( '<h2 />' ) + .text( embedPlayer.isAudio() ? + mw.msg( 'mwe-embedplayer-share_this_audio' ) : + mw.msg( 'mwe-embedplayer-share_this_video' ) ) + ); + + if ( embed_wiki_code ) { + $shareInterface.append( + $('<ul />').append( + $('<li />').text( + mw.msg( 'mwe-embedplayer-embed_wiki' ) + ) + ), + $( '<textarea />' ) + .attr( 'rows', 1 ) + .html( embed_wiki_code ) + .click( function() { + $( this ).select(); + }), + $('<br />') + ); + } + + $shareInterface.append( + $shareList + ); + + $shareInterface.append( + + $( '<textarea />' ) + .attr( 'rows', 4 ) + .html( embed_code ) + .click( function() { + $( this ).select(); + }), + + $('<br />'), + $('<br />') + ); + return $shareInterface; + }, + + /** + * Shows the Player Select interface + * + * @param {Object} $target jQuery target for output + */ + getPlayerSelect: function( ) { + mw.log('PlayerControlBuilder::getPlayerSelect: source:' + + this.embedPlayer.mediaElement.selectedSource.getSrc() + + ' player: ' + this.embedPlayer.selectedPlayer.id ); + + var embedPlayer = this.embedPlayer; + + var _this = this; + + var $playerSelect = $('<div />') + .append( + $( '<h2 />' ) + .text( mw.msg( 'mwe-embedplayer-choose_player' ) ) + ); + + $.each( embedPlayer.mediaElement.getPlayableSources(), function( sourceId, source ) { + + var isPlayable = (typeof mw.EmbedTypes.getMediaPlayers().defaultPlayer( source.getMIMEType() ) == 'object' ); + var isSelected = ( source.getSrc() == embedPlayer.mediaElement.selectedSource.getSrc() ); + + $playerSelect.append( + $( '<h3 />' ) + .text( source.getTitle() ) + ); + + if ( isPlayable ) { + var $playerList = $('<ul />'); + // output the player select code: + + var supportingPlayers = mw.EmbedTypes.getMediaPlayers().getMIMETypePlayers( source.getMIMEType() ); + + for ( var i = 0; i < supportingPlayers.length ; i++ ) { + // Add link to select the player if not already selected ) + if( embedPlayer.selectedPlayer.id == supportingPlayers[i].id && isSelected ) { + // Active player ( no link ) + var $playerLine = $( '<span />' ) + .append( + $('<a />') + .attr({ + 'href' : '#' + }) + .addClass( 'active') + .text( + supportingPlayers[i].getName() + ).click( function(){ + embedPlayer.controlBuilder.closeMenuOverlay(); + // Don't follow the # link: + return false; + }) + ); + //.addClass( 'ui-state-highlight ui-corner-all' ); removed by ran + } else { + // Non active player add link to select: + $playerLine = $( '<a />') + .attr({ + 'href' : '#', + 'id' : 'sc_' + sourceId + '_' + supportingPlayers[i].id + }) + .addClass( 'ui-corner-all') + .text( supportingPlayers[i].getName() ) + .click( function() { + var iparts = $( this ).attr( 'id' ).replace(/sc_/ , '' ).split( '_' ); + var sourceId = iparts[0]; + var player_id = iparts[1]; + mw.log( 'PlayerControlBuilder:: source id: ' + sourceId + ' player id: ' + player_id ); + + embedPlayer.controlBuilder.closeMenuOverlay(); + + // Close fullscreen if we are in fullscreen mode + if( _this.isInFullScreen() ){ + _this.restoreWindowPlayer(); + } + + embedPlayer.mediaElement.setSourceByIndex( sourceId ); + var playableSources = embedPlayer.mediaElement.getPlayableSources(); + + mw.EmbedTypes.getMediaPlayers().setPlayerPreference( + player_id, + playableSources[ sourceId ].getMIMEType() + ); + + // Issue a stop + embedPlayer.stop(); + + // Don't follow the # link: + return false; + } ) + .hover( + function(){ + $( this ).addClass('active'); + }, + function(){ + $( this ).removeClass('active'); + } + ); + } + + // Add the player line to the player list: + $playerList.append( + $( '<li />' ).append( + $playerLine + ) + ); + } + + // Append the player list: + $playerSelect.append( $playerList ); + + } else { + // No player available: + $playerSelect.append( mw.msg( 'mwe-embedplayer-no-player', source.getTitle() ) ); + } + } ); + + // Return the player select elements + return $playerSelect; + }, + + /** + * Loads sources and calls showDownloadWithSources + * @param {Object} $target jQuery target to output to + */ + showDownload: function( $target ) { + var _this = this; + var embedPlayer = this.embedPlayer; + _this.showDownloadWithSources( $target ); + }, + + /** + * Shows the download interface with sources loaded + * @param {Object} $target jQuery target to output to + */ + showDownloadWithSources : function( $target ) { + var _this = this; + mw.log( 'PlayerControlBuilder:: showDownloadWithSources::' + $target.length ); + var embedPlayer = this.embedPlayer; + // Empty the target: + $target.empty(); + $target.append( $('<div />') ); + $target = $target.find('div'); + + var $mediaList = $( '<ul />' ); + var $textList = $( '<ul />' ); + $.each( embedPlayer.mediaElement.getSources(), function( index, source ) { + if( source.getSrc() ) { + mw.log("showDownloadWithSources:: Add src: " + source.getTitle() ); + var fileName = source.mwtitle; + if ( !fileName ) { + var path = new mw.Uri( source.getSrc() ).path; + var pathParts = path.split( '/' ); + fileName = pathParts[ pathParts.length -1 ]; + } + var $dlLine = $( '<li />').append( + $('<a />') + .attr( { + 'href': source.getSrc(), + 'download': fileName + }) + .text( source.getTitle() ) + ); + // Add link to correct "bucket" + + //Add link to time segment: + if ( source.getSrc().indexOf( '?t=' ) !== -1 ) { + $target.append( $dlLine ); + } else if ( this.getMIMEType().indexOf('text') === 0 ) { + // Add link to text list + $textList.append( $dlLine ); + } else { + // Add link to media list + $mediaList.append( $dlLine ); + } + + } + } ); + if( $mediaList.find('li').length != 0 ) { + $target.append( + $('<h2 />') + .text( embedPlayer.isAudio() ? + mw.msg( 'mwe-embedplayer-download_full_audio' ) : + mw.msg( 'mwe-embedplayer-download_full_video' ) ), + $mediaList + ); + } + + if( $textList.find('li').length != 0 ) { + $target.append( + $('<h2 />') + .html( mw.msg( 'mwe-embedplayer-download_text' ) ), + $textList + ); + } + }, + getSwitchSourceMenu: function(){ + var _this = this; + var embedPlayer = this.embedPlayer; + // for each source with "native playback" + var $sourceMenu = $('<ul />'); + + // Local function to closure the "source" variable scope: + function addToSourceMenu( source ){ + // Check if source is selected: + var icon = ( source.getSrc() == embedPlayer.mediaElement.selectedSource.getSrc() ) ? 'bullet' : 'radio-on'; + $sourceMenu.append( + $.getLineItem( source.getShortTitle() , icon, function(){ + mw.log( 'PlayerControlBuilder::SwitchSourceMenu: ' + source.getSrc() ); + // update menu selecting parent li siblings + $( this ).parent().siblings().find('span.ui-icon').removeClass( 'ui-icon-bullet').addClass( 'ui-icon-radio-on' ); + $( this ).find('span.ui-icon').removeClass( 'ui-icon-radio-on').addClass( 'ui-icon-bullet' ); + // update control bar text + embedPlayer.getInterface().find( '.source-switch' ).text( source.getShortTitle() ); + + + // TODO this logic should be in mw.EmbedPlayer + embedPlayer.mediaElement.setSource( source ); + if( ! _this.embedPlayer.isStopped() ){ + // Get the exact play time from the video element ( instead of parent embed Player ) + var oldMediaTime = _this.embedPlayer.getPlayerElement().currentTime; + var oldPaused = _this.embedPlayer.paused; + // Do a live switch + embedPlayer.playerSwitchSource( source, function( vid ){ + // issue a seek + embedPlayer.setCurrentTime( oldMediaTime, function(){ + // reflect pause state + if( oldPaused ){ + embedPlayer.pause(); + } + } ); + }); + } + }) + ); + } + var addedSources = {}; + $.each( this.embedPlayer.mediaElement.getPlayableSources(), function( sourceIndex, source ) { + // Output the player select code: + var supportingPlayers = mw.EmbedTypes.getMediaPlayers().getMIMETypePlayers( source.getMIMEType() ); + for ( var i = 0; i < supportingPlayers.length ; i++ ) { + var lib = supportingPlayers[i].library; + if( lib === 'Native' || lib === 'OgvJs' ){ // @fixme use supports.sourceSwitch ... if preloaded? + if ( !( source.getSrc() in addedSources ) ) { + addedSources[source.getSrc()] = true; + addToSourceMenu( source ); + } + } + } + }); + return $sourceMenu; + }, + + /** + * Get component + * + * @param {String} componentId Component key to grab html output + */ + getComponent: function( componentId ) { + if ( this.components[ componentId ] ) { + return this.components[ componentId ].o( this ); + } else { + return false; + } + }, + + /** + * Get a component height + * + * @param {String} componentId Component key to grab height + * @return height or false if not set + */ + getComponentHeight: function( componentId ) { + if ( this.components[ componentId ] + && this.components[ componentId ].h ) + { + return this.components[ componentId ].h; + } + return 0; + }, + + /** + * Get a component width + * @param {String} componentId Component key to grab width + * @return width or false if not set + */ + getComponentWidth: function( componentId ){ + if ( this.components[ componentId ] + && this.components[ componentId ].w ) + { + return this.components[ componentId ].w; + } + return 0; + }, + + // Set up the disable playhead function: + // TODO this will move into the disableSeekBar binding in the new theme framework + disableSeekBar : function(){ + var $playHead = this.embedPlayer.getInterface().find( ".play_head" ); + if( $playHead.length ){ + $playHead.slider( "option", "disabled", true ); + } + }, + enableSeekBar : function(){ + var $playHead = this.embedPlayer.getInterface().find( ".play_head" ); + if( $playHead.length ){ + $playHead.slider( "option", "disabled", false); + } + }, + + /** + * Components Object + * Take in the embedPlayer and return some html for the given component. + * + * components can be overwritten by skin javascript + * + * Component JSON structure is as follows: + * 'o' Function to return a binded jQuery object ( accepts the ctrlObject as a parameter ) + * 'w' The width of the component + * 'h' The height of the component ( if height is undefined the height of the control bar is used ) + * 'position' elements are inserted into the dom based on component order and available space. + * if the element is inserted, position is then used to set relative dom insert order. + */ + components: { + /** + * The pause / play button + */ + 'pause': { + 'w': 28, + 'position': 1, + 'o': function( ctrlObj ) { + return $( '<div />' ) + .attr( 'title', mw.msg( 'mwe-embedplayer-play_clip' ) ) + .addClass ( "ui-state-default ui-corner-all ui-icon_link lButton play-btn" ) + .append( + $( '<span />' ) + .addClass( "ui-icon ui-icon-play" ) + ) + // Play / pause binding + .buttonHover() + .click( function() { + ctrlObj.embedPlayer.play(); + // Don't follow the # link: + return false; + }); + } + }, + + /** + * The volume control interface html + */ + 'volumeControl': { + 'w' : 28, + 'position': 7, + 'o' : function( ctrlObj ) { + mw.log( 'PlayerControlBuilder::Set up volume control for: ' + ctrlObj.embedPlayer.id ); + var $volumeOut = $( '<span />' ); + if ( ctrlObj.volumeLayout == 'horizontal' ) { + $volumeOut.append( + $( '<div />' ) + .addClass( "ui-slider ui-slider-horizontal rButton volume-slider" ) + ); + } + + // Add the volume control icon + $volumeOut.append( + $('<div />') + .attr( 'title', mw.msg( 'mwe-embedplayer-volume_control' ) ) + .addClass( "ui-state-default ui-corner-all ui-icon_link rButton volume_control" ) + .append( + $( '<span />' ) + .addClass( "ui-icon ui-icon-volume-on" ) + ) + ); + if ( ctrlObj.volumeLayout == 'vertical' ) { + $volumeOut.find('.volume_control').append( + $( '<div />' ) + .hide() + .addClass( "vol_container ui-corner-all" ) + .append( + $( '<div />' ) + .addClass ( "volume-slider" ) + ) + ); + } + //Return the inner html + return $volumeOut.html(); + } + }, + + /** + * The large play button in center of the player + */ + 'playButtonLarge': { + 'w' : 70, + 'h' : 53, + 'position': 2, + 'o' : function( ctrlObj ) { + return $( '<div />' ) + .attr( { + 'title' : mw.msg( 'mwe-embedplayer-play_clip' ), + 'class' : "play-btn-large" + } ) + // Get dynamic position for big play button + .css( ctrlObj.getPlayButtonPosition() ) + // Add play hook: + .click( function() { + ctrlObj.embedPlayer.play(); + return false; // Event Stop Propagation + } ); + } + }, + + /** + * The Attribution button ( by default this is kaltura-icon + */ + 'attributionButton' : { + 'w' : 28, + 'position': 3, + 'o' : function( ctrlObj ){ + var buttonConfig = mw.config.get( 'EmbedPlayer.AttributionButton'); + // Check for source ( by configuration convention this is a 16x16 image + if( buttonConfig.iconurl ){ + var $icon = $('<img />') + .attr('src', buttonConfig.iconurl ); + } else { + var $icon = $('<span />') + .addClass( 'ui-icon' ); + if( buttonConfig['class'] ){ + $icon.addClass( buttonConfig['class'] ); + } + } + if( typeof buttonConfig.style != 'object'){ + buttonConfig.style = {}; + } + // update the configured size of the attribution button if we have a specific width configured + if( buttonConfig.style.width ){ + this.w = parseInt( buttonConfig.style.width ); + } else { + buttonConfig.style.width = parseInt( this.w ) + 'px'; + } + + return $( '<div />' ) + .addClass( 'rButton' ) + .css({ + 'top' : '1px', + 'left' : '2px' + }) + // Allow button config style to override + .css( buttonConfig.style ) + .append( + $('<a />') + .attr({ + 'href': buttonConfig.href, + 'title' : buttonConfig.title, + 'target' : '_new' + }) + .append( $icon ) + ); + } + }, + + /* + * The time display area + */ + 'timeDisplay': { + 'w' : mw.config.get( 'EmbedPlayer.TimeDisplayWidth' ), + 'position': 6, + 'o' : function( ctrlObj ) { + return $( '<div />' ) + .addClass( "ui-widget time-disp" ) + .append( + ctrlObj.embedPlayer.getTimeRange() + ); + } + }, + + /** + * The options button, invokes display of the options menu + */ + 'options': { + 'w': 28, + 'position': 10, + 'o': function( ctrlObj ) { + return $( '<div />' ) + .attr( 'title', mw.msg( 'mwe-embedplayer-player_options' ) ) + .addClass( 'ui-state-default ui-corner-all ui-icon_link rButton options-btn' ) + .append( + $('<span />') + .addClass( 'ui-icon ui-icon-wrench' ) + ) + .buttonHover() + // Options binding: + .embedMenu( { + 'content' : ctrlObj.getOptionsMenu(), + 'zindex' : mw.config.get( 'EmbedPlayer.FullScreenZIndex' ) + 2, + 'positionOpts': { + 'directionV' : 'up', + 'offsetY' : 30, + 'directionH' : 'left', + 'offsetX' : -28 + } + } ); + } + }, + + /** + * The fullscreen button for displaying the video fullscreen + */ + 'fullscreen': { + 'w': 24, + 'position': 8, + 'o': function( ctrlObj ) { + var $btn = $( '<div />' ) + .attr( 'title', mw.msg( 'mwe-embedplayer-player_fullscreen' ) ) + .addClass( "ui-state-default ui-corner-all ui-icon_link rButton fullscreen-btn" ) + .append( + $( '<span />' ) + .addClass( "ui-icon ui-icon-arrow-4-diag" ) + ) + // Fullscreen binding: + .buttonHover(); + // Link out to another window if iPad 3x ( broken iframe resize ) + if( ( + mw.config.get('EmbedPlayer.IsIframeServer') + && + mw.isIpad3() + ) + || + mw.config.get( "EmbedPlayer.NewWindowFullscreen" ) + || + ( mw.config.get('EmbedPlayer.IsIframeServer') && mw.config.get('EmbedPlayer.EnableIframeApi') === false ) + ){ + // Get the iframe url: + var url = ctrlObj.embedPlayer.getIframeSourceUrl(); + // Change button into new window ( of the same url as the iframe ) : + return $('<a />').attr({ + 'href': url, + 'target' : '_new' + }) + .click(function(){ + // Update the url: + var url = $(this).attr('href'); + var iframeMwConfig = {}; + + iframeMwConfig['EmbedPlayer.IsFullscreenIframe'] = true; + // add a seek offset: + iframeMwConfig['EmbedPlayer.IframeCurrentTime'] = ctrlObj.embedPlayer.currentTime; + // add play state: + iframeMwConfig['EmbedPlayer.IframeIsPlaying'] = ctrlObj.embedPlayer.isPlaying(); + + // Append the configuration and request domain to the iframe hash: + + // Add the parentUrl to the iframe config: + iframeMwConfig['EmbedPlayer.IframeParentUrl'] = document.URL; + + url += '#' + encodeURIComponent( + JSON.stringify({ + 'mwConfig' :iframeMwConfig, + 'playerId' : playerId + }) + ); + ctrlObj.embedPlayer.pause(); + // try and do a browser popup: + var newwin = window.open( + url, + ctrlObj.embedPlayer.id, + // Fullscreen window params: + 'width=' + screen.width + + ', height=' + ( screen.height - 90 ) + + ', top=0, left=0' + + ', fullscreen=yes' + ); + // if for some reason we could not open the window run the href link: + if( newwin === null){ + return true; + } + if ( window.focus ) { + newwin.focus(); + } + // Else do not follow the href link + return false; + }) + .append($btn); + } else { + return $btn.click( function() { + ctrlObj.embedPlayer.fullscreen(); + } ); + } + } + }, + + 'sourceSwitch' : { + 'w' : 70, + 'position': 9, + 'o' : function( ctrlObj ){ + var $menuContainer = $('<div />').addClass( 'swMenuContainer' ).hide(); + ctrlObj.embedPlayer.getInterface().append( + $menuContainer + ) + // Stream switching widget ( display the current selected stream text ) + return $( '<div />' ) + .addClass('ui-widget source-switch') + .append( + ctrlObj.embedPlayer.mediaElement.selectedSource.getShortTitle() + ).embedMenu( { + 'content' : ctrlObj.getSwitchSourceMenu(), + 'zindex' : mw.config.get( 'EmbedPlayer.FullScreenZIndex' ) + 2, + 'keepPosition' : true, + 'targetMenuContainer' : $menuContainer, + 'width' : 130, + 'showSpeed': 0, + 'createMenuCallback' : function(){ + var $interface = ctrlObj.embedPlayer.getInterface(); + var $sw = $interface.find( '.source-switch' ); + var $swMenuContainer = $interface.find('.swMenuContainer'); + var height = $swMenuContainer.find( 'li' ).length * 30; + // position from top ( unkown why we can't use bottom here ) + var top = $interface.height() - height - ctrlObj.getHeight() - 6; + $menuContainer.css({ + 'position' : 'absolute', + 'left': $sw[0].offsetLeft, + 'top' : top, + 'bottom': ctrlObj.getHeight(), + 'height' : height + }) + ctrlObj.showControlBar( true ); + }, + 'closeMenuCallback' : function(){ + ctrlObj.restoreControlsHover() + } + } ); + } + }, + + /** + * The playhead component + */ + 'playHead': { + 'w':0, // special case (takes up remaining space) + 'position': 5, + 'o':function( ctrlObj ) { + + var sliderConfig = { + range: "min", + value: 0, + min: 0, + max: 1000, + start: function( event, ui ) { + var id = ( embedPlayer.pc != null ) ? embedPlayer.pc.pp.id:embedPlayer.id; + embedPlayer.userSlide = true; + $( id + ' .play-btn-large' ).fadeOut( 'fast' ); + // If playlist always start at 0 + embedPlayer.startTimeSec = ( embedPlayer.instanceOf == 'mvPlayList' ) ? 0: + mw.npt2seconds( embedPlayer.getTimeRange().split( '/' )[0] ); + }, + slide: function( event, ui ) { + var perc = ui.value / 1000; + embedPlayer.jumpTime = mw.seconds2npt( parseFloat( parseFloat( embedPlayer.getDuration() ) * perc ) + embedPlayer.startTimeSec ); + // mw.log('perc:' + perc + ' * ' + embedPlayer.getDuration() + ' jt:'+ this.jumpTime); + if ( _this.longTimeDisp ) { + ctrlObj.setStatus( mw.msg( 'mwe-embedplayer-seek_to', embedPlayer.jumpTime ) ); + } else { + ctrlObj.setStatus( embedPlayer.jumpTime ); + } + // Update the thumbnail / frame + if ( embedPlayer.isPlaying == false ) { + embedPlayer.updateThumbPerc( perc ); + } + }, + change: function( event, ui ) { + // Only run the onChange event if done by a user slide + // (otherwise it runs times it should not) + if ( embedPlayer.userSlide ) { + embedPlayer.userSlide = false; + embedPlayer.seeking = true; + + var perc = ui.value / 1000; + // set seek time (in case we have to do a url seek) + embedPlayer.seekTimeSec = mw.npt2seconds( embedPlayer.jumpTime, true ); + mw.log( 'PlayerControlBuilder:: seek to: ' + embedPlayer.jumpTime + ' perc:' + perc + ' sts:' + embedPlayer.seekTimeSec ); + ctrlObj.setStatus( mw.msg( 'mwe-embedplayer-seeking' ) ); + if( embedPlayer.isStopped() ){ + embedPlayer.play(); + } + embedPlayer.seek( perc ); + } + } + }; + + var embedPlayer = ctrlObj.embedPlayer; + var _this = this; + var $playHead = $( '<div />' ) + .addClass ( "play_head" ) + .css({ + "position" : 'absolute', + "left" : '33px', + "right" : ( ( embedPlayer.getPlayerWidth() - ctrlObj.availableWidth - 33 ) ) + 'px' + }) + // Playhead binding + .slider( sliderConfig ); + + // Up the z-index of the default status indicator: + $playHead.find( '.ui-slider-handle' ).css( 'z-index', 4 ); + $playHead.find( '.ui-slider-range' ).addClass( 'ui-corner-all' ).css( 'z-index', 2 ); + + // Add buffer html: + $playHead.append( + $('<div />') + .addClass( "ui-slider-range ui-slider-range-min ui-widget-header") + .addClass( "ui-state-highlight ui-corner-all mw_buffer") + ); + + // Show video timeline position on hover and when dragging playhead + function showPosition(event) { + var pos = ( event.clientX - $playHead.offset().left ) / $playHead.width(); + var time = mw.seconds2npt( parseFloat( embedPlayer.getDuration() ) * pos + (embedPlayer.startTimeSec || 0) ); + $playHead.attr('title', time); + } + $playHead.on({ + mouseenter: showPosition, + mouseleave: function(event) { + $playHead.attr({title: ''}); + }, + mousemove: showPosition + }); + + return $playHead; + } + } + } +}; + +} )( mediaWiki, jQuery ); |