Index: branches/MwEmbedStandAlone/DefaultSettings.js |
— | — | @@ -0,0 +1,4 @@ |
| 2 | +/** |
| 3 | +* Custom Javascript Configuration included on every request. |
| 4 | +* In a default svn checkout this file should always be empty. |
| 5 | +*/ |
\ No newline at end of file |
Index: branches/MwEmbedStandAlone/mwEmbedFrame.php |
— | — | @@ -12,8 +12,8 @@ |
13 | 13 | // Setup the mwEmbedFrame |
14 | 14 | $myMwEmbedFrame = new mwEmbedFrame(); |
15 | 15 | |
16 | | -// Do mwEmbedFrame output: |
17 | | -$myMwEmbedFrame->outputFrame(); |
| 16 | +// Do mwEmbedFrame video output: |
| 17 | +$myMwEmbedFrame->outputIFrame(); |
18 | 18 | |
19 | 19 | /** |
20 | 20 | * mwEmbed iFrame class |
— | — | @@ -29,11 +29,13 @@ |
30 | 30 | 'poster', |
31 | 31 | 'kentryid', |
32 | 32 | 'kwidgetid', |
| 33 | + 'kuiconfid', |
| 34 | + 'kplaylistid', |
33 | 35 | 'skin' |
34 | 36 | ); |
35 | 37 | var $playerIframeId = 'iframeVid'; |
36 | 38 | var $debug = false; |
37 | | - |
| 39 | + |
38 | 40 | // When used in direct source mode the source asset. |
39 | 41 | // NOTE: can be an array of sources in cases of "many" sources set |
40 | 42 | var $sources = array(); |
— | — | @@ -42,39 +44,55 @@ |
43 | 45 | //parse input: |
44 | 46 | $this->parseRequest(); |
45 | 47 | } |
46 | | - function outputFrame(){ |
47 | | - // Presently only video frame supported: |
48 | | - $this->outputEmbedFrame(); |
49 | | - } |
50 | | - |
| 48 | + |
51 | 49 | // Parse the embedFrame request and sanitize input |
52 | 50 | private function parseRequest(){ |
| 51 | + // Check for / attribute type request and update "REQUEST" global |
| 52 | + // ( uses kaltura standard entry_id/{entryId} request ) |
| 53 | + // normalize to the REQUEST object |
| 54 | + // @@FIXME: this should be moved over to a kaltura specific iframe implementation |
| 55 | + if( $_SERVER['REQUEST_URI'] ){ |
| 56 | + $kalturaUrlMap = Array( |
| 57 | + 'entry_id' => 'kentryid', |
| 58 | + 'uiconf_id' => 'kuiconfid', |
| 59 | + 'wid' => 'kwidgetid', |
| 60 | + 'playlist_id' => 'kplaylistid' |
| 61 | + ); |
| 62 | + $urlParts = explode( '/', $_SERVER['REQUEST_URI'] ); |
| 63 | + foreach( $urlParts as $inx => $urlPart ){ |
| 64 | + foreach( $kalturaUrlMap as $urlKey => $reqeustAttribute ){ |
| 65 | + if( $urlPart == $reqeustAttribute && isset( $urlParts[$inx+1] ) ){ |
| 66 | + $_REQUEST[ $reqeustAttribute ] = $urlParts[$inx+1]; |
| 67 | + } |
| 68 | + } |
| 69 | + } |
| 70 | + } |
53 | 71 | // Check for attributes |
54 | 72 | foreach( $this->playerAttributes as $attributeKey){ |
55 | | - if( isset( $_GET[ $attributeKey ] ) ){ |
56 | | - $this->$attributeKey = htmlspecialchars( $_GET[$attributeKey] ); |
| 73 | + if( isset( $_REQUEST[ $attributeKey ] ) ){ |
| 74 | + $this->$attributeKey = htmlspecialchars( $_REQUEST[$attributeKey] ); |
57 | 75 | } |
58 | 76 | } |
59 | 77 | |
60 | 78 | // Check for debug flag |
61 | | - if( isset( $_GET['debug'] ) ){ |
| 79 | + if( isset( $_REQUEST['debug'] ) ){ |
62 | 80 | $this->debug = true; |
63 | 81 | } |
64 | | - |
| 82 | + |
65 | 83 | // Process the special "src" attribute |
66 | | - if( isset( $_GET['src'] ) ){ |
67 | | - if( is_array( $_GET['src'] ) ){ |
68 | | - foreach($_GET['src'] as $src ){ |
| 84 | + if( isset( $_REQUEST['src'] ) ){ |
| 85 | + if( is_array( $_REQUEST['src'] ) ){ |
| 86 | + foreach($_REQUEST['src'] as $src ){ |
69 | 87 | $this->sources[] = htmlspecialchars( $src ); |
70 | 88 | } |
71 | 89 | } else { |
72 | | - $this->sources = array( htmlspecialchars( $_GET['src'] ) ); |
| 90 | + $this->sources = array( htmlspecialchars( $_REQUEST['src'] ) ); |
73 | 91 | } |
74 | 92 | } |
75 | | - |
| 93 | + |
76 | 94 | } |
77 | 95 | private function getVideoTag( ){ |
78 | | - // Add default video tag with 100% width / height |
| 96 | + // Add default video tag with 100% width / height |
79 | 97 | // ( parent embed is responsible for setting the iframe size ) |
80 | 98 | $o = '<video id="' . htmlspecialchars( $this->playerIframeId ) . '" style="width:100%;height:100%"'; |
81 | 99 | foreach( $this->playerAttributes as $attributeKey){ |
— | — | @@ -92,11 +110,12 @@ |
93 | 111 | } |
94 | 112 | $o.= '</video>'; |
95 | 113 | return $o; |
96 | | - } |
97 | | - private function outputEmbedFrame( ){ |
| 114 | + } |
| 115 | + |
| 116 | + function outputIFrame( ){ |
98 | 117 | // Setup the embed string based on attribute set: |
99 | 118 | $embedResourceList = 'window.jQuery,mwEmbed,mw.style.mwCommon,$j.fn.menu,mw.style.jquerymenu,mw.EmbedPlayer,mw.EmbedPlayerNative,mw.EmbedPlayerJava,mw.PlayerControlBuilder,$j.fn.hoverIntent,mw.style.EmbedPlayer,$j.cookie,$j.ui,mw.style.ui_redmond,$j.widget,$j.ui.mouse,mw.PlayerSkinKskin,mw.style.PlayerSkinKskin,mw.TimedText,mw.style.TimedText,$j.ui.slider'; |
100 | | - |
| 119 | + |
101 | 120 | if( isset( $this->kentryid ) ){ |
102 | 121 | $embedResourceList.= ',' . implode(',', array( |
103 | 122 | 'KalturaClientBase', |
— | — | @@ -106,7 +125,7 @@ |
107 | 126 | 'KalturaAccessControl', |
108 | 127 | 'MD5', |
109 | 128 | 'mw.KWidgetSupport', |
110 | | - 'mw.KAnalytics', |
| 129 | + 'mw.KAnalytics', |
111 | 130 | 'mw.KDPMapping', |
112 | 131 | 'mw.MobileAdTimeline', |
113 | 132 | 'mw.KAds' |
— | — | @@ -126,43 +145,52 @@ |
127 | 146 | left:0px; |
128 | 147 | bottom:0px; |
129 | 148 | right:0px; |
130 | | - |
| 149 | + |
131 | 150 | } |
132 | 151 | </style> |
133 | | - <script type="text/javascript" src="ResourceLoader.php?class=<?php |
| 152 | + <script type="text/javascript" src="<?php echo str_replace( 'mwEmbedFrame.php', '', $_SERVER['SCRIPT_NAME'] ); ?>ResourceLoader.php?class=<?php |
| 153 | + // @@TODO we should move this over to using the mwEmbedLoader.js so we don't have to mannage the resource list in two places. |
| 154 | + // ( this will matter less once we migrate to the new mediaWiki resource loader framework) |
134 | 155 | echo $embedResourceList; |
135 | 156 | if( $this->debug ){ |
136 | 157 | echo '&debug=true'; |
137 | | - } |
| 158 | + } |
138 | 159 | ?>"></script> |
139 | | - |
| 160 | + |
140 | 161 | <script type="text/javascript"> |
141 | 162 | //Set some iframe embed config: |
142 | | - // We can't support full screen in object context since it requires outter page DOM control |
| 163 | + // We can't support full screen in object context since it requires outer page DOM control |
143 | 164 | mw.setConfig( 'EmbedPlayer.EnableFullscreen', false ); |
144 | 165 | |
145 | 166 | // Enable the iframe player server: |
146 | | - mw.setConfig( 'EmbedPlayer.EnalbeIFramePlayerServer', true ); |
147 | | - |
| 167 | + mw.setConfig( 'EmbedPlayer.EnableIFramePlayerServer', true ); |
| 168 | + |
148 | 169 | mw.ready(function(){ |
149 | | - // Trigger fullscreen so that iframe resize keeps player size |
150 | | - $j( '#<?php echo htmlspecialchars( $this->playerIframeId )?>' ) |
151 | | - .get(0).fullscreen(); |
| 170 | + // Bind window resize to reize the player: |
| 171 | + $j(window).resize(function(){ |
| 172 | + $j( '#<?php echo htmlspecialchars( $this->playerIframeId )?>' ) |
| 173 | + .get(0).resizePlayer({ |
| 174 | + 'width' : $j(window).width(), |
| 175 | + 'height' : $j(window).height() |
| 176 | + }); |
| 177 | + }); |
152 | 178 | }); |
153 | 179 | </script> |
154 | | - </head> |
155 | | - <body> |
156 | | - <?php |
157 | | - // Check if we have a way to get sources: |
158 | | - if( isset( $this->apiTitleKey ) || isset( $this->kentryid ) || count( $this->sources ) != 0 ) { |
| 180 | + |
| 181 | + </head> |
| 182 | + <body> |
| 183 | + <? |
| 184 | + // Check if we have a way to get sources: |
| 185 | + if( isset( $this->apiTitleKey ) || isset( $this->kentryid ) || count( $this->sources ) != 0 ) { |
159 | 186 | echo $this->getVideoTag(); |
160 | | - } else { |
161 | | - echo "Error: mwEmbedFrame missing required parameter for video sources"; |
162 | | - } |
163 | | - ?> |
164 | | - </body> |
| 187 | + } else { |
| 188 | + echo "Error: mwEmbedFrame missing required parameter for video sources</body></html>"; |
| 189 | + exit(1); |
| 190 | + } |
| 191 | + ?> |
| 192 | + </body> |
165 | 193 | </html> |
166 | 194 | <?php |
167 | 195 | } |
168 | 196 | } |
169 | | - ?> |
| 197 | +?> |
Index: branches/MwEmbedStandAlone/mwEmbed.js |
— | — | @@ -81,21 +81,34 @@ |
82 | 82 | } |
83 | 83 | return ; |
84 | 84 | } |
| 85 | + mwConfig[ name ] = value; |
| 86 | + }; |
| 87 | + /** |
| 88 | + * Merge in a configuration value: |
| 89 | + */ |
| 90 | + mw.mergeConfig = function( name, value ){ |
| 91 | + if( typeof name == 'object' ) { |
| 92 | + $j.each( name, function( inx, val) { |
| 93 | + mw.setConfig( inx, val ); |
| 94 | + }); |
| 95 | + return ; |
| 96 | + } |
85 | 97 | // Check if we should "merge" the config |
86 | 98 | if( typeof value == 'object' && typeof mwConfig[ name ] == 'object' ) { |
87 | 99 | if ( value.constructor.toString().indexOf("Array") == -1 ){ |
| 100 | + // merge in the array |
| 101 | + mwConfig[ name ] = mwConfig[ name ].concat( value ); |
| 102 | + } else { |
88 | 103 | for( var i in value ){ |
89 | 104 | mwConfig[ name ][ i ] = value[ i ]; |
90 | 105 | } |
91 | | - } else { |
92 | | - // merge in the array |
93 | | - mwConfig[ name ] = mwConfig[ name ].concat( value ); |
94 | 106 | } |
95 | | - } else { |
96 | | - mwConfig[ name ] = value; |
| 107 | + return ; |
97 | 108 | } |
| 109 | + // else do a normal setConfig |
| 110 | + mwConfig[ name ] = value; |
98 | 111 | }; |
99 | | - |
| 112 | + |
100 | 113 | /** |
101 | 114 | * Set a default config value Will only update configuration if no value is |
102 | 115 | * present |
— | — | @@ -120,16 +133,7 @@ |
121 | 134 | } |
122 | 135 | // Check if we should "merge" the config |
123 | 136 | if( typeof value == 'object' && typeof mwConfig[ name ] == 'object' ) { |
124 | | - if ( value.constructor.toString().indexOf("Array") == -1 ){ |
125 | | - for( var i in value ){ |
126 | | - if( typeof mwConfig[ name ][ i ] == 'undefined' ){ |
127 | | - mwConfig[ name ][ i ] = value[ i ]; |
128 | | - } |
129 | | - } |
130 | | - } else { |
131 | | - // merge in the array |
132 | | - mwConfig[ name ] = mwConfig[ name ].concat( value); |
133 | | - } |
| 137 | + mw.mergeConfig( name, value); |
134 | 138 | } |
135 | 139 | }; |
136 | 140 | |
— | — | @@ -1150,39 +1154,64 @@ |
1151 | 1155 | uiRequest |
1152 | 1156 | ], function() { |
1153 | 1157 | var $dialog = $j( '#mwTempLoaderDialog' ).show().dialog( options ); |
1154 | | - // center the dialog |
1155 | | - // xxx figure out why jquery ui is messing up here |
1156 | | - /*$j( '#mwTempLoaderDialog' ).parent('.ui-dialog').css({ |
1157 | | - 'position' : 'absolute', |
1158 | | - 'left' : '50%', |
1159 | | - 'margin-left': -1 * $dialog.width()/2, |
1160 | | - 'top' : '50%', |
1161 | | - 'margin-top': -1 * $dialog.height()/2 |
1162 | | - }); */ |
1163 | 1158 | } ); |
1164 | 1159 | return $j( '#mwTempLoaderDialog' ); |
1165 | 1160 | }; |
1166 | 1161 | |
1167 | 1162 | /** |
1168 | | - * Mobile HTML5 has special properties for html5 video:: |
1169 | | - * |
| 1163 | + * Fallforward system by default prefers flash. |
| 1164 | + * |
| 1165 | + * This is separate from the EmbedPlayer library detection to provide package loading control |
1170 | 1166 | * NOTE: should be phased out in favor of browser feature detection where possible |
| 1167 | + * |
1171 | 1168 | */ |
1172 | | - mw.isMobileHTML5 = function() { |
1173 | | - // check mobile safari foce ( for debug ) |
1174 | | - if( mw.getConfig( 'forceMobileHTML5' ) || document.URL.indexOf('forceMobileHTML5') != -1 ){ |
| 1169 | + mw.isHTML5FallForwardNative = function(){ |
| 1170 | + // Check for a mobile html5 user agent: |
| 1171 | + if ( (navigator.userAgent.indexOf('iPhone') != -1) || |
| 1172 | + (navigator.userAgent.indexOf('iPod') != -1) || |
| 1173 | + (navigator.userAgent.indexOf('iPad') != -1) || |
| 1174 | + (navigator.userAgent.indexOf('Android 2.') != -1) || |
| 1175 | + // to debug in chrome / desktop safari |
| 1176 | + (document.URL.indexOf('forceMobileHTML5') != -1 ) |
| 1177 | + ) { |
1175 | 1178 | return true; |
1176 | 1179 | } |
1177 | | - if (( navigator.userAgent.indexOf('iPhone') != -1) || |
1178 | | - ( navigator.userAgent.indexOf('iPod') != -1) || |
1179 | | - ( navigator.userAgent.indexOf('iPad') != -1) || |
1180 | | - ( mw.isAndroid2() ) |
1181 | | - ) { |
| 1180 | + |
| 1181 | + // Check if the client does not have flash and has the video tag |
| 1182 | + if ( navigator.mimeTypes && navigator.mimeTypes.length > 0 ) { |
| 1183 | + for ( var i = 0; i < navigator.mimeTypes.length; i++ ) { |
| 1184 | + var type = navigator.mimeTypes[i].type; |
| 1185 | + var semicolonPos = type.indexOf( ';' ); |
| 1186 | + if ( semicolonPos > -1 ) { |
| 1187 | + type = type.substr( 0, semicolonPos ); |
| 1188 | + } |
| 1189 | + if (type == 'application/x-shockwave-flash' ) { |
| 1190 | + // flash is installed don't use html5 |
| 1191 | + return false; |
| 1192 | + } |
| 1193 | + } |
| 1194 | + } |
| 1195 | + |
| 1196 | + // For IE: |
| 1197 | + var hasObj = true; |
| 1198 | + try { |
| 1199 | + var obj = new ActiveXObject( 'ShockwaveFlash.ShockwaveFlash' ); |
| 1200 | + } catch ( e ) { |
| 1201 | + hasObj = false; |
| 1202 | + } |
| 1203 | + if( hasObj ){ |
| 1204 | + return false; |
| 1205 | + } |
| 1206 | + // No flash return true if the browser supports html5 video tag with basic support for canPlayType: |
| 1207 | + var dummyvid = document.createElement( "video" ); |
| 1208 | + // temporary hack firefox does not work well with native player: |
| 1209 | + if( dummyvid.canPlayType && !$j.browser.mozilla) { |
1182 | 1210 | return true; |
1183 | 1211 | } |
| 1212 | + // No video tag or flash, return false ( normal "install flash" user flow ) |
1184 | 1213 | return false; |
1185 | | - }; |
1186 | | - // Android 2 has some restrictions vs other mobile platforms |
| 1214 | + } |
| 1215 | + // Android 2 has some restrictions vs other mobile platforms |
1187 | 1216 | mw.isAndroid2 = function(){ |
1188 | 1217 | if ( navigator.userAgent.indexOf('Android 2.') != -1) { |
1189 | 1218 | return true; |
— | — | @@ -1602,7 +1631,6 @@ |
1603 | 1632 | } |
1604 | 1633 | return false; |
1605 | 1634 | }; |
1606 | | - |
1607 | 1635 | /** |
1608 | 1636 | * Given a float number of seconds, returns npt format response. ( ignore |
1609 | 1637 | * days for now ) |
— | — | @@ -1646,7 +1674,6 @@ |
1647 | 1675 | } |
1648 | 1676 | return hoursStr + tm.minutes + ":" + tm.seconds; |
1649 | 1677 | }; |
1650 | | - |
1651 | 1678 | /** |
1652 | 1679 | * Given seconds return array with 'days', 'hours', 'min', 'seconds' |
1653 | 1680 | * |
— | — | @@ -1663,11 +1690,14 @@ |
1664 | 1691 | }; |
1665 | 1692 | |
1666 | 1693 | /** |
1667 | | - * Take hh:mm:ss,ms or hh:mm:ss.ms input, return the number of seconds |
| 1694 | + * Given a float number of seconds, returns npt format response. ( ignore |
| 1695 | + * days for now ) |
1668 | 1696 | * |
1669 | | - * @param {String} |
1670 | | - * npt_str NPT time string |
1671 | | - * @return {Float} Number of seconds |
| 1697 | + * @param {Float} |
| 1698 | + * sec Seconds |
| 1699 | + * @param {Boolean} |
| 1700 | + * verbose If hours and milliseconds should padded be displayed. |
| 1701 | + * @return {Float} String npt format |
1672 | 1702 | */ |
1673 | 1703 | mw.npt2seconds = function ( npt_str ) { |
1674 | 1704 | if ( !npt_str ) { |
Index: branches/MwEmbedStandAlone/modules/Playlist/mw.Playlist.js |
— | — | @@ -257,7 +257,7 @@ |
258 | 258 | $videoList.show() |
259 | 259 | // show the video list and apply the swipe binding |
260 | 260 | $j( _this.target ).find('.media-rss-video-list-wrapper').fadeIn(); |
261 | | - if( mw.isMobileHTML5() ){ |
| 261 | + if( mw.isHTML5FallForwardNative() ){ |
262 | 262 | // iScroll is buggy with current version of iPad / iPhone use scroll buttons instead |
263 | 263 | /* |
264 | 264 | document.addEventListener('touchmove', function(e){ e.preventDefault(); }); |
— | — | @@ -444,7 +444,7 @@ |
445 | 445 | // see: http://developer.apple.com/iphone/search/search.php?simp=1&num=10&Search=html5+autoplay |
446 | 446 | var updateVideoPlayerToDom = true; |
447 | 447 | |
448 | | - if( mw.isMobileHTML5() ){ |
| 448 | + if( mw.isHTML5FallForwardNative() ){ |
449 | 449 | // Check for a current video: |
450 | 450 | var $inDomVideo = $j( _this.target + ' .media-rss-video-player video' ); |
451 | 451 | if( $inDomVideo.length == 0 ){ |
— | — | @@ -588,7 +588,7 @@ |
589 | 589 | .click( function(){ |
590 | 590 | mw.log( 'clicked on: ' + $j( this ).data( 'clipIndex') ); |
591 | 591 | // Make sure the existing player is "playing " (safari can't play async with javascript ) |
592 | | - /*if( mw.isMobileHTML5() ){ |
| 592 | + /*if( mw.isHTML5FallForwardNative() ){ |
593 | 593 | var embedPlayer = $j('#' + _this.getVideoPlayerId() ).get(0); |
594 | 594 | //embedPlayer.playerElement.play(); |
595 | 595 | }*/ |
— | — | @@ -612,7 +612,7 @@ |
613 | 613 | |
614 | 614 | play: function(){ |
615 | 615 | var embedPlayer = $j('#' + this.getVideoPlayerId() ).get(0); |
616 | | - if( mw.isMobileHTML5() ){ |
| 616 | + if( mw.isHTML5FallForwardNative() ){ |
617 | 617 | embedPlayer.playerElement.play(); |
618 | 618 | } else{ |
619 | 619 | embedPlayer.play(); |
Index: branches/MwEmbedStandAlone/modules/EmbedPlayer/loader.js |
— | — | @@ -82,6 +82,133 @@ |
83 | 83 | 'EmbedPLayer.IFramePlayer.DomainWhiteList' : '*' |
84 | 84 | } ); |
85 | 85 | |
| 86 | + /* |
| 87 | + * The default video attributes supported by embedPlayer |
| 88 | + */ |
| 89 | + mw.setDefaultConfig('EmbedPlayer.Attributes', { |
| 90 | + /* |
| 91 | + * Base html element attributes: |
| 92 | + */ |
| 93 | + |
| 94 | + // id: Auto-populated if unset |
| 95 | + "id" : null, |
| 96 | + |
| 97 | + // Width: alternate to "style" to set player width |
| 98 | + "width" : null, |
| 99 | + |
| 100 | + // Height: alternative to "style" to set player height |
| 101 | + "height" : null, |
| 102 | + |
| 103 | + /* |
| 104 | + * Base html5 video element attributes / states also see: |
| 105 | + * http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html |
| 106 | + */ |
| 107 | + |
| 108 | + // Media src URI, can be relative or absolute URI |
| 109 | + "src" : null, |
| 110 | + |
| 111 | + // Poster attribute for displaying a place holder image before loading |
| 112 | + // or playing the video |
| 113 | + "poster" : null, |
| 114 | + |
| 115 | + // Autoplay if the media should start playing |
| 116 | + "autoplay" : false, |
| 117 | + |
| 118 | + // Loop attribute if the media should repeat on complete |
| 119 | + "loop" : false, |
| 120 | + |
| 121 | + // If the player controls should be displayed |
| 122 | + "controls" : true, |
| 123 | + |
| 124 | + // Video starts "paused" |
| 125 | + "paused" : true, |
| 126 | + |
| 127 | + // ReadyState an attribute informs clients of video loading state: |
| 128 | + // see: http://www.whatwg.org/specs/web-apps/current-work/#readystate |
| 129 | + "readyState" : 0, |
| 130 | + |
| 131 | + // Loading state of the video element |
| 132 | + "networkState" : 0, |
| 133 | + |
| 134 | + // Current playback position |
| 135 | + "currentTime" : 0, |
| 136 | + |
| 137 | + // Previous player set time |
| 138 | + // Lets javascript use $j('#videoId').get(0).currentTime = newTime; |
| 139 | + "previousTime" : 0, |
| 140 | + |
| 141 | + // Previous player set volume |
| 142 | + // Lets javascript use $j('#videoId').get(0).volume = newVolume; |
| 143 | + "previousVolume" : 1, |
| 144 | + |
| 145 | + // Initial player volume: |
| 146 | + "volume" : 0.75, |
| 147 | + |
| 148 | + // Caches the volume before a mute toggle |
| 149 | + "preMuteVolume" : 0.75, |
| 150 | + |
| 151 | + // Media duration: Value is populated via |
| 152 | + // custom durationHint attribute or via the media file once its played |
| 153 | + "duration" : null, |
| 154 | + |
| 155 | + // Mute state |
| 156 | + "muted" : false, |
| 157 | + |
| 158 | + /** |
| 159 | + * Custom attributes for embedPlayer player: (not part of the html5 |
| 160 | + * video spec) |
| 161 | + */ |
| 162 | + |
| 163 | + // Default video aspect ratio |
| 164 | + 'videoAspect' : '4:3', |
| 165 | + |
| 166 | + // Start time of the clip |
| 167 | + "start" : 0, |
| 168 | + |
| 169 | + // End time of the clip |
| 170 | + "end" : null, |
| 171 | + |
| 172 | + // A apiTitleKey for looking up subtitles, credits and related videos |
| 173 | + "apiTitleKey" : null, |
| 174 | + |
| 175 | + // The apiProvider where to lookup the title key |
| 176 | + "apiProvider" : null, |
| 177 | + |
| 178 | + // If the player controls should be overlaid |
| 179 | + // ( Global default via config EmbedPlayer.OverlayControls in module |
| 180 | + // loader.js) |
| 181 | + "overlaycontrols" : true, |
| 182 | + |
| 183 | + // Attribute to use 'native' controls |
| 184 | + "usenativecontrols" : false, |
| 185 | + |
| 186 | + // If the player should include an attribution button: |
| 187 | + 'attributionbutton' : true, |
| 188 | + |
| 189 | + // ROE url ( for xml based metadata ) |
| 190 | + // also see: http://wiki.xiph.org/ROE |
| 191 | + "roe" : null, |
| 192 | + |
| 193 | + // If serving an ogg_chop segment use this to offset the presentation |
| 194 | + // time |
| 195 | + // ( for some plugins that use ogg page time rather than presentation |
| 196 | + // time ) |
| 197 | + "startOffset" : 0, |
| 198 | + |
| 199 | + // Thumbnail (same as poster) |
| 200 | + "thumbnail" : null, |
| 201 | + |
| 202 | + // Source page for media asset ( used for linkbacks in remote embedding |
| 203 | + // ) |
| 204 | + "linkback" : null, |
| 205 | + |
| 206 | + // If the download link should be shown |
| 207 | + "download_link" : true, |
| 208 | + |
| 209 | + // Content type of the media |
| 210 | + "type" : null |
| 211 | + } ); |
| 212 | + |
86 | 213 | // Add class file paths |
87 | 214 | mw.addResourcePaths( { |
88 | 215 | "mw.EmbedPlayer" : "mw.EmbedPlayer.js", |
— | — | @@ -217,6 +344,13 @@ |
218 | 345 | if( !!document.createElement('video').canPlayType && !$j.browser.safari ) { |
219 | 346 | dependencyRequest[0].push( 'mw.EmbedPlayerNative' ) |
220 | 347 | } |
| 348 | + |
| 349 | + // Check if the iFrame player server is enabled: |
| 350 | + if (mw.getConfig('EmbedPlayer.EnableIFramePlayerServer')) { |
| 351 | + dependencyRequest.push('mw.EmbedPlayerNative'); |
| 352 | + dependencyRequest.push('$j.postMessage'); |
| 353 | + dependencyRequest.push('mw.IFramePlayerApiServer'); |
| 354 | + } |
221 | 355 | |
222 | 356 | // Return the set of libs to be loaded |
223 | 357 | return dependencyRequest; |
Index: branches/MwEmbedStandAlone/modules/EmbedPlayer/mw.EmbedPlayer.js |
— | — | @@ -1,306 +1,126 @@ |
2 | | -/** |
| 2 | +/** |
3 | 3 | * embedPlayer is the base class for html5 video tag javascript abstraction library |
4 | 4 | * embedPlayer include a few subclasses: |
5 | | -* |
6 | | -* mediaPlayer Media player embed system ie: java, vlc or native. |
7 | | -* mediaElement Represents source media elements |
8 | | -* mw.PlayerControlBuilder Handles skinning of the player controls |
| 5 | +* |
| 6 | +* mediaPlayer Media player embed system ie: java, vlc or native. |
| 7 | +* mediaElement Represents source media elements |
| 8 | +* mw.PlayerControlBuilder Handles skinning of the player controls |
9 | 9 | */ |
10 | 10 | |
| 11 | +/** |
| 12 | + * Add the messages text: |
| 13 | + */ |
| 14 | + |
11 | 15 | mw.includeAllModuleMessages(); |
12 | 16 | |
13 | | -/* |
14 | | -* The default video attributes supported by embedPlayer |
15 | | -*/ |
16 | | -mw.setDefaultConfig( 'EmbedPlayer.Attributes', { |
17 | | - /* |
18 | | - * Base html element attributes: |
19 | | - */ |
20 | 17 | |
21 | | - // id: Auto-populated if unset |
22 | | - "id" : null, |
23 | | - |
24 | | - // Width: alternate to "style" to set player width |
25 | | - "width" : null, |
26 | | - |
27 | | - // Height: alternative to "style" to set player height |
28 | | - "height" : null, |
29 | | - |
30 | | - /* |
31 | | - * Base html5 video element attributes / states |
32 | | - * also see: http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html |
33 | | - */ |
34 | | - |
35 | | - // Media src URI, can be relative or absolute URI |
36 | | - "src" : null, |
37 | | - |
38 | | - // Poster attribute for displaying a place holder image before loading or playing the video |
39 | | - "poster": null, |
40 | | - |
41 | | - // Autoplay if the media should start playing |
42 | | - "autoplay" : false, |
43 | | - |
44 | | - // Loop attribute if the media should repeat on complete |
45 | | - "loop" : false, |
46 | | - |
47 | | - // If the player controls should be displayed |
48 | | - "controls" : true, |
49 | | - |
50 | | - // Video starts "paused" |
51 | | - "paused" : true, |
52 | | - |
53 | | - // ReadyState an attribute informs clients of video loading state: |
54 | | - // see: http://www.whatwg.org/specs/web-apps/current-work/#readystate |
55 | | - "readyState" : 0, |
56 | | - |
57 | | - // Loading state of the video element |
58 | | - "networkState" : 0, |
59 | | - |
60 | | - // Current playback position |
61 | | - "currentTime" :0, |
62 | | - |
63 | | - // Previous player set time |
64 | | - // Lets javascript use $j('#videoId').get(0).currentTime = newTime; |
65 | | - "previousTime" :0, |
66 | | - |
67 | | - // Previous player set volume |
68 | | - // Lets javascript use $j('#videoId').get(0).volume = newVolume; |
69 | | - "previousVolume" : 1, |
70 | | - |
71 | | - // Initial player volume: |
72 | | - "volume" : 0.75, |
73 | | - |
74 | | - // Caches the volume before a mute toggle |
75 | | - "preMuteVolume" : 0.75, |
76 | | - |
77 | | - // Media duration: Value is populated via |
78 | | - // custom durationHint attribute or via the media file once its played |
79 | | - "duration" :null, |
80 | | - |
81 | | - // Mute state |
82 | | - "muted" : false, |
83 | | - |
84 | | - /** |
85 | | - * Custom attributes for embedPlayer player: |
86 | | - * (not part of the html5 video spec) |
87 | | - */ |
88 | | - |
89 | | - // Default video aspect ratio |
90 | | - 'videoAspect': '4:3', |
91 | | - |
92 | | - // Start time of the clip |
93 | | - "start" : 0, |
94 | | - |
95 | | - // End time of the clip |
96 | | - "end" : null, |
97 | | - |
98 | | - // A apiTitleKey for looking up subtitles, credits and related videos |
99 | | - "apiTitleKey" : null, |
100 | | - |
101 | | - // The apiProvider where to lookup the title key |
102 | | - "apiProvider" : null, |
103 | | - |
104 | | - // If the player controls should be overlaid |
105 | | - //( Global default via config EmbedPlayer.OverlayControls in module loader.js) |
106 | | - "overlaycontrols" : true, |
107 | | - |
108 | | - // Attribute to use 'native' controls |
109 | | - "usenativecontrols" : false, |
110 | | - |
111 | | - // If the player should include an attribution button: |
112 | | - 'attributionbutton' : true, |
113 | | - |
114 | | - // ROE url ( for xml based metadata ) |
115 | | - // also see: http://wiki.xiph.org/ROE |
116 | | - "roe" : null, |
117 | | - |
118 | | - // If serving an ogg_chop segment use this to offset the presentation time |
119 | | - // ( for some plugins that use ogg page time rather than presentation time ) |
120 | | - "startOffset" : 0, |
121 | | - |
122 | | - // Thumbnail (same as poster) |
123 | | - "thumbnail" : null, |
124 | | - |
125 | | - // Source page for media asset ( used for linkbacks in remote embedding ) |
126 | | - "linkback" : null, |
127 | | - |
128 | | - // If the download link should be shown |
129 | | - "download_link" : true, |
130 | | - |
131 | | - // Content type of the media |
132 | | - "type" : null |
133 | | -}); |
134 | | - |
135 | | -mw.setDefaultConfig( { |
136 | | - // If the player controls should be overlaid on top of the video ( if supported by playback method) |
137 | | - // can be set to false per embed player via overlayControls attribute |
138 | | - 'EmbedPlayer.OverlayControls' : true, |
139 | | - |
140 | | - 'EmbedPlayer.LibraryPage': 'http://www.kaltura.org/project/HTML5_Video_Media_JavaScript_Library', |
141 | | - |
142 | | - // A default apiProvider ( ie where to lookup subtitles, video properties etc ) |
143 | | - // NOTE: Each player instance can also specify a specific provider |
144 | | - "EmbedPlayer.ApiProvider" : "local", |
145 | | - |
146 | | - // Default video size ( if no size provided ) |
147 | | - "EmbedPlayer.DefaultSize" : "400x300", |
148 | | - |
149 | | - // If the video player should attribute kaltura |
150 | | - "EmbedPlayer.KalturaAttribution" : true, |
151 | | - |
152 | | - // The attribution button |
153 | | - 'EmbedPlayer.AttributionButton' :{ |
154 | | - 'title' : 'Kaltura html5 video library', |
155 | | - 'href' : 'http://www.kaltura.org/project/HTML5_Video_Media_JavaScript_Library', |
156 | | - // Style icon to be applied |
157 | | - 'class' : 'kaltura-icon', |
158 | | - // An icon image url ( should be a 12x12 image or data url ) |
159 | | - 'iconurl' : false |
160 | | - }, |
161 | | - |
162 | | - |
163 | | - // Set the browser player warning flag displays warning for non optimal playback |
164 | | - "EmbedPlayer.ShowNativeWarning" : true, |
165 | | - |
166 | | - // If fullscreen is global enabled. |
167 | | - "EmbedPlayer.EnableFullscreen" : true, |
168 | | - |
169 | | - // If mwEmbed should use the Native player controls |
170 | | - // this will prevent video tag rewriting and skinning |
171 | | - // useful for devices such as iPad / iPod that |
172 | | - // don't fully support DOM overlays or don't expose full-screen |
173 | | - // functionality to javascript |
174 | | - "EmbedPlayer.NativeControls" : false, |
175 | | - |
176 | | - // If mwEmbed should use native controls on mobile safari |
177 | | - "EmbedPlayer.NativeControlsMobileSafari" : true, |
178 | | - |
179 | | - |
180 | | - // The z-index given to the player interface during full screen ( high z-index ) |
181 | | - "EmbedPlayer.fullScreenZIndex" : 999998, |
182 | | - |
183 | | - // The default share embed mode ( can be "object" or "videojs" ) |
184 | | - // |
185 | | - // "object" will provide a <object tag pointing to mwEmbedFrame.php |
186 | | - // Object embedding should be much more compatible with sites that |
187 | | - // let users embed flash applets |
188 | | - // "videojs" will include the source javascript and video tag to |
189 | | - // rewrite the player on the remote page DOM |
190 | | - // Video tag embedding is much more mash-up friendly but exposes |
191 | | - // the remote site to the mwEmbed javascript and can be a xss issue. |
192 | | - "EmbedPlayer.ShareEmbedMode" : 'object', |
193 | | - |
194 | | - // Default player skin name |
195 | | - "EmbedPlayer.SkinName" : "mvpcf", |
196 | | - |
197 | | - // Number of milliseconds between interface updates |
198 | | - 'EmbedPlayer.MonitorRate' : 250 |
199 | | -} ); |
200 | | - |
201 | 18 | /** |
202 | | - * The base source attribute checks |
203 | | - * also see: http://dev.w3.org/html5/spec/Overview.html#the-source-element |
| 19 | + * The base source attribute checks also see: |
| 20 | + * http://dev.w3.org/html5/spec/Overview.html#the-source-element |
204 | 21 | */ |
205 | | -mw.setDefaultConfig( 'embedPlayerSourceAttributes', [ |
| 22 | +mw.setDefaultConfig( 'EmbedPlayer.SourceAttributes', [ |
206 | 23 | // source id |
207 | 24 | 'id', |
208 | | - |
| 25 | + |
209 | 26 | // media url |
210 | 27 | 'src', |
211 | | - |
| 28 | + |
212 | 29 | // media codecs attribute ( if provided ) |
213 | 30 | 'codecs', |
214 | | - |
| 31 | + |
215 | 32 | // Title string for the source asset |
216 | 33 | 'title', |
217 | | - |
| 34 | + |
218 | 35 | // boolean if we support temporal url requests on the source media |
219 | 36 | 'URLTimeEncoding', |
220 | | - |
| 37 | + |
221 | 38 | // Media has a startOffset ( used for plugins that |
222 | 39 | // display ogg page time rather than presentation time |
223 | 40 | 'startOffset', |
224 | | - |
| 41 | + |
225 | 42 | // A hint to the duration of the media file so that duration |
226 | 43 | // can be displayed in the player without loading the media file |
227 | 44 | 'durationHint', |
228 | | - |
| 45 | + |
229 | 46 | // Media start time |
230 | 47 | 'start', |
231 | | - |
| 48 | + |
232 | 49 | // Media end time |
233 | 50 | 'end', |
234 | | - |
| 51 | + |
235 | 52 | // If the source is the default source |
236 | 53 | 'default', |
237 | | - |
| 54 | + |
238 | 55 | // Language key used for subtitle tracks |
239 | 56 | 'srclang', |
240 | | - |
| 57 | + |
241 | 58 | // titleKey ( used for api lookups ) |
242 | 59 | 'titleKey', |
243 | | - |
| 60 | + |
244 | 61 | // The provider type ( for what type of api query to make ) |
245 | 62 | 'provider_type', |
246 | | - |
| 63 | + |
247 | 64 | // The api url for the provider |
248 | | - 'provider_url' |
| 65 | + 'provider_url' |
249 | 66 | ] ); |
250 | 67 | |
251 | 68 | /** |
252 | | -* Adds jQuery binding for embedPlayer |
253 | | -*/ |
| 69 | + * Adds jQuery binding for embedPlayer |
| 70 | + */ |
254 | 71 | ( function( $ ) { |
255 | | - |
| 72 | + |
256 | 73 | /* |
257 | | - * embeds all players that match the rewrite player tags config |
258 | | - * Passes off request to the embedPlayer selector: |
259 | | - * |
260 | | - * @param {Object} attributes Attributes to apply to embed players |
261 | | - * @param {Function} callback Function to call once embedding is done |
262 | | - */ |
| 74 | + * embeds all players that match the rewrite player tags config Passes off |
| 75 | + * request to the embedPlayer selector: |
| 76 | + * |
| 77 | + * @param {Object} attributes Attributes to apply to embed players @param |
| 78 | + * {Function} callback Function to call once embedding is done |
| 79 | + */ |
263 | 80 | $.embedPlayers = function( attributes, callback) { |
264 | 81 | $j( mw.getConfig( 'EmbedPlayer.RewriteTags' ) ).embedPlayer( attributes, callback ); |
265 | 82 | }; |
266 | | - |
| 83 | + |
267 | 84 | /** |
268 | | - * Selector based embedPlayer jQuery binding |
269 | | - * |
270 | | - * Rewrites all tags via a given selector |
271 | | - * |
272 | | - * @param {object=} attributes Optional embedPlayer attributes for the given video interface. |
273 | | - * Attributes Object can include any key value pair that would otherwise be |
274 | | - * an attribute in the html element. |
275 | | - * |
276 | | - * also see: mw.getConfig( 'EmbedPlayer.Attributes' ) |
277 | | - * |
278 | | - * @param {Function=} callback Optional Function to be called once video interfaces are ready |
279 | | - * |
280 | | - */ |
| 85 | + * Selector based embedPlayer jQuery binding |
| 86 | + * |
| 87 | + * Rewrites all tags via a given selector |
| 88 | + * |
| 89 | + * @param {object=} |
| 90 | + * attributes Optional embedPlayer attributes for the given video |
| 91 | + * interface. Attributes Object can include any key value pair |
| 92 | + * that would otherwise be an attribute in the html element. |
| 93 | + * |
| 94 | + * also see: mw.getConfig( 'EmbedPlayer.Attributes' ) |
| 95 | + * |
| 96 | + * @param {Function=} |
| 97 | + * callback Optional Function to be called once video interfaces |
| 98 | + * are ready |
| 99 | + * |
| 100 | + */ |
281 | 101 | $.fn.embedPlayer = function( attributes, callback ) { |
282 | 102 | mw.log( 'EmbedPlayer:: fn.embedPlayer' ); |
283 | 103 | var playerSelect = this.selector; |
284 | | - |
| 104 | + |
285 | 105 | // Define attributes if unset |
286 | 106 | if( !attributes ) { |
287 | 107 | attributes = {}; |
288 | 108 | } |
289 | | - |
| 109 | + |
290 | 110 | // Handle optional include of attributes argument: |
291 | 111 | if( typeof attributes == 'function' ){ |
292 | 112 | callback = attributes; |
293 | 113 | attributes = {}; |
294 | 114 | } |
295 | | - |
| 115 | + |
296 | 116 | $j( playerSelect ).each( function( index, playerElement) { |
297 | 117 | // make sure the playerElement has an id: |
298 | 118 | if( $j( playerElement ).attr('id') =='' ){ |
299 | 119 | $j( playerElement ).attr( "id", 'mwe_v' + ( index ) ); |
300 | 120 | } |
301 | | - |
| 121 | + |
302 | 122 | // If we are dynamically embedding on a "div" check if we can |
303 | 123 | // add a poster image behind the loader: |
304 | | - if( playerElement.nodeName.toLowerCase() == 'div' |
| 124 | + if( playerElement.nodeName.toLowerCase() == 'div' |
305 | 125 | && ( attributes.poster || $j(playerElement).attr( 'poster' ) ) ){ |
306 | 126 | var posterSrc = ( attributes.poster ) ? attributes.poster : $j(playerElement).attr( 'poster' ); |
307 | 127 | |
— | — | @@ -313,7 +133,7 @@ |
314 | 134 | if( !height ){ |
315 | 135 | var height = ( attributes.height )? attributes.height : '100%'; |
316 | 136 | } |
317 | | - |
| 137 | + |
318 | 138 | mw.log('EmbedPlayer:: set loading background: ' + posterSrc); |
319 | 139 | $j( playerElement ).append( |
320 | 140 | $j( '<img />' ) |
— | — | @@ -326,12 +146,12 @@ |
327 | 147 | ); |
328 | 148 | } |
329 | 149 | }); |
330 | | - |
| 150 | + |
331 | 151 | // If we have not detected browser plugin embed types do that now |
332 | 152 | if( ! mw.EmbedTypes.players ){ |
333 | 153 | mw.EmbedTypes.init(); |
334 | 154 | } |
335 | | - |
| 155 | + |
336 | 156 | // Create the Global Embed Player Manager ( if not already created ) |
337 | 157 | if( ! mw.playerManager ) { |
338 | 158 | mw.log( "EmbedPlayer::Create the player manager:" ); |
— | — | @@ -341,7 +161,8 @@ |
342 | 162 | $j( mw ).trigger( 'EmbedPlayerManagerReady' ); |
343 | 163 | } |
344 | 164 | var addedToPlayerManager = false; |
345 | | - // Make sure we have user preference setup ( for setting preferences on video selection ) |
| 165 | + // Make sure we have user preference setup ( for setting preferences on |
| 166 | + // video selection ) |
346 | 167 | mw.setupUserConfig( function() { |
347 | 168 | mw.log("EmbedPlayer:: found: " + $j( playerSelect ).length + ' players '); |
348 | 169 | // Add each selected element to the player manager: |
— | — | @@ -357,132 +178,136 @@ |
358 | 179 | // Add the player |
359 | 180 | mw.playerManager.addElement( playerElement, attributes); |
360 | 181 | } |
361 | | - |
| 182 | + |
362 | 183 | } ); |
363 | | - // run the callback directly if no players were added to the playerManager |
| 184 | + // run the callback directly if no players were added to the |
| 185 | + // playerManager |
364 | 186 | if( !addedToPlayerManager && callback ){ |
365 | 187 | callback(); |
366 | 188 | } |
367 | | - |
| 189 | + |
368 | 190 | }); |
369 | 191 | }; |
370 | 192 | |
371 | 193 | } )( jQuery ); |
372 | 194 | |
373 | 195 | /** |
374 | | -* EmbedPlayerManager |
375 | | -* |
376 | | -* Manages calls to embed video interfaces |
377 | | -*/ |
| 196 | + * EmbedPlayerManager |
| 197 | + * |
| 198 | + * Manages calls to embed video interfaces |
| 199 | + */ |
378 | 200 | var EmbedPlayerManager = function( ) { |
379 | 201 | // Create a Player Manage |
380 | 202 | return this.init( ); |
381 | 203 | }; |
382 | 204 | EmbedPlayerManager.prototype = { |
383 | | - |
| 205 | + |
384 | 206 | // Functions to run after the video interface is ready |
385 | 207 | callbackFunctions : null, |
386 | | - |
| 208 | + |
387 | 209 | playerElementQueue: [], |
388 | | - |
| 210 | + |
389 | 211 | /** |
390 | | - * Constructor initializes callbackFunctions and playerList |
391 | | - */ |
| 212 | + * Constructor initializes callbackFunctions and playerList |
| 213 | + */ |
392 | 214 | init: function( ) { |
393 | 215 | this.callbackFunctions = []; |
394 | 216 | this.playerList = []; |
395 | 217 | }, |
396 | | - |
| 218 | + |
397 | 219 | /** |
398 | | - * Adds a callback to the callbackFunctions list |
399 | | - * the callback functions are called once the players are ready. |
400 | | - * |
401 | | - * @param {Function} callback Function to be called once players are ready |
402 | | - */ |
| 220 | + * Adds a callback to the callbackFunctions list the callback functions are |
| 221 | + * called once the players are ready. |
| 222 | + * |
| 223 | + * @param {Function} |
| 224 | + * callback Function to be called once players are ready |
| 225 | + */ |
403 | 226 | addCallback: function( callback ) { |
404 | 227 | if( typeof callback == 'function' ){ |
405 | 228 | this.callbackFunctions.push( callback ); |
406 | 229 | } |
407 | 230 | }, |
408 | | - |
| 231 | + |
409 | 232 | /** |
410 | | - * Get the list of players |
411 | | - */ |
| 233 | + * Get the list of players |
| 234 | + */ |
412 | 235 | getPlayerList: function( ) { |
413 | 236 | return this.playerList; |
414 | 237 | }, |
415 | | - |
| 238 | + |
416 | 239 | /** |
417 | | - * Adds an Element for the embedPlayer to rewrite |
418 | | - * |
419 | | - * uses embedPlayer interface on audio / video elements |
420 | | - * uses mvPlayList interface on playlist elements |
421 | | - * |
422 | | - * Once a player interface is established the following chain of functions are called; |
423 | | - * |
424 | | - * _this.checkPlayerSources() |
425 | | - * _this.checkForTimedText() |
426 | | - * _this.setupSourcePlayer() |
427 | | - * _this.inheritEmbedPlayer() |
428 | | - * _this.selectedPlayer.load() |
429 | | - * _this.showPlayer() |
430 | | - * |
431 | | - * @param {Element} playerElement DOM element to be swapped |
432 | | - * @param {Object} [Optional] attributes Extra attributes to apply to the player interface |
433 | | - */ |
| 240 | + * Adds an Element for the embedPlayer to rewrite |
| 241 | + * |
| 242 | + * uses embedPlayer interface on audio / video elements uses mvPlayList |
| 243 | + * interface on playlist elements |
| 244 | + * |
| 245 | + * Once a player interface is established the following chain of functions |
| 246 | + * are called; |
| 247 | + * |
| 248 | + * _this.checkPlayerSources() _this.checkForTimedText() |
| 249 | + * _this.setupSourcePlayer() _this.inheritEmbedPlayer() |
| 250 | + * _this.selectedPlayer.load() _this.showPlayer() |
| 251 | + * |
| 252 | + * @param {Element} |
| 253 | + * playerElement DOM element to be swapped |
| 254 | + * @param {Object} |
| 255 | + * [Optional] attributes Extra attributes to apply to the player |
| 256 | + * interface |
| 257 | + */ |
434 | 258 | addElement: function( playerElement, attributes ) { |
435 | 259 | var _this = this; |
436 | | - |
| 260 | + |
437 | 261 | if ( !playerElement.id || playerElement.id == '' ) { |
438 | 262 | // give the playerElement an id: |
439 | | - playerElement.id = 'vid' + ( this.playerList.length + 1 ); |
| 263 | + playerElement.id = 'vid' + ( this.playerList.length + 1 ); |
440 | 264 | } |
441 | 265 | mw.log('EmbedPlayerManager: addElement:: ' + playerElement.id ); |
442 | 266 | |
443 | 267 | // Add the element id to playerList |
444 | 268 | this.playerList.push( playerElement.id ); |
445 | | - |
| 269 | + |
446 | 270 | // Check for player attributes such as skins or plugins attributes |
447 | 271 | // that add to the request set |
448 | 272 | var playerDependencyRequest = []; |
449 | | - |
| 273 | + |
450 | 274 | // merge in any custom attributes |
451 | 275 | $j.extend( playerElement, attributes ); |
452 | | - |
| 276 | + |
453 | 277 | // Update the list of dependent libraries for the player |
454 | 278 | // ( allows extensions to add to the dependency list ) |
455 | 279 | mw.embedPlayerUpdateLibraryRequest( playerElement, playerDependencyRequest ); |
456 | | - |
| 280 | + |
457 | 281 | // Load any skins we need then swap in the interface |
458 | 282 | mw.load( playerDependencyRequest, function() { |
459 | 283 | var waitForMeta = true; |
460 | | - |
461 | | - // Be sure to "stop" the target ( sometimes firefox keeps playing the video even |
| 284 | + |
| 285 | + // Be sure to "stop" the target ( sometimes firefox keeps playing |
| 286 | + // the video even |
462 | 287 | // though its been removed from the DOM ) |
463 | 288 | if( playerElement.pause ){ |
464 | 289 | playerElement.pause(); |
465 | 290 | } |
466 | | - |
467 | | - |
| 291 | + |
| 292 | + |
468 | 293 | // Let extensions determine if its worthwhile to wait for metadata: |
469 | 294 | // We pass an object to the trigger to preserve reference values |
470 | | - var eventObject = { |
471 | | - 'playerElement' : playerElement, |
| 295 | + var eventObject = { |
| 296 | + 'playerElement' : playerElement, |
472 | 297 | 'waitForMeta' : waitForMeta |
473 | 298 | }; |
474 | 299 | $j( mw ).trigger( 'addElementWaitForMetaEvent', eventObject ); |
475 | | - |
| 300 | + |
476 | 301 | // update the waitForMeta |
477 | 302 | waitForMeta = eventObject[ 'waitForMeta' ]; |
478 | | - |
479 | | - |
| 303 | + |
| 304 | + |
480 | 305 | // Set the wait for meta flag if unset by extension |
481 | 306 | if( waitForMeta ){ |
482 | 307 | waitForMeta = _this.waitForMetaCheck( playerElement ); |
483 | 308 | } |
484 | | - |
| 309 | + |
485 | 310 | var ranPlayerSwapFlag = false; |
486 | | - |
| 311 | + |
487 | 312 | // Local callback to runPlayer swap once playerElement has metadata |
488 | 313 | function runPlayerSwap() { |
489 | 314 | if( ranPlayerSwapFlag ){ |
— | — | @@ -490,10 +315,10 @@ |
491 | 316 | } |
492 | 317 | ranPlayerSwapFlag = true; |
493 | 318 | mw.log("EmbedPlayer::runPlayerSwap::" + $j( playerElement ).attr('id') ); |
494 | | - |
| 319 | + |
495 | 320 | var playerInterface = new mw.EmbedPlayer( playerElement , attributes); |
496 | 321 | var swapPlayer = _this.swapEmbedPlayerElement( playerElement, playerInterface ); |
497 | | - |
| 322 | + |
498 | 323 | // Copy over any data attributes from the playerElement |
499 | 324 | if( mw.getConfig( 'EmbedPlayer.DataAttributes' ) ) { |
500 | 325 | var dataAttr = mw.getConfig( 'EmbedPlayer.DataAttributes' ); |
— | — | @@ -503,43 +328,46 @@ |
504 | 329 | } |
505 | 330 | } |
506 | 331 | } |
507 | | - |
508 | | - // Pass the id to any hook that needs to interface prior to checkPlayerSources |
| 332 | + |
| 333 | + // Pass the id to any hook that needs to interface prior to |
| 334 | + // checkPlayerSources |
509 | 335 | mw.log("EmbedPlayer::addElement :trigger " + playerInterface.id ); |
510 | 336 | $j( mw ).trigger ( 'newEmbedPlayerEvent', $j( '#' + playerInterface.id ).get(0) ); |
511 | | - |
512 | | - // Issue the checkPlayerSources call to the new player interface: |
| 337 | + |
| 338 | + // Issue the checkPlayerSources call to the new player |
| 339 | + // interface: |
513 | 340 | // make sure to use the element that is in the DOM: |
514 | 341 | $j( '#' + playerInterface.id ).get(0).checkPlayerSources(); |
515 | 342 | } |
516 | | - |
| 343 | + |
517 | 344 | if( waitForMeta ) { |
518 | | - mw.log('EmbedPlayer::WaitForMeta ( video missing height (' + |
519 | | - $j( playerElement ).attr('height') + '), width (' + |
| 345 | + mw.log('EmbedPlayer::WaitForMeta ( video missing height (' + |
| 346 | + $j( playerElement ).attr('height') + '), width (' + |
520 | 347 | $j( playerElement ).attr('width') + ') or duration: ' + |
521 | | - $j( playerElement ).attr('duration') |
| 348 | + $j( playerElement ).attr('duration') |
522 | 349 | ); |
523 | | - |
| 350 | + |
524 | 351 | playerElement.removeEventListener( "loadedmetadata", runPlayerSwap, true ); |
525 | 352 | playerElement.addEventListener( "loadedmetadata", runPlayerSwap, true ); |
526 | | - |
527 | | - // Time-out of 5 seconds ( maybe still playable but no timely metadata ) |
| 353 | + |
| 354 | + // Time-out of 5 seconds ( maybe still playable but no timely |
| 355 | + // metadata ) |
528 | 356 | setTimeout( runPlayerSwap, 5000 ); |
529 | 357 | return ; |
530 | | - } else { |
| 358 | + } else { |
531 | 359 | runPlayerSwap(); |
532 | 360 | return ; |
533 | | - } |
534 | | - }); |
| 361 | + } |
| 362 | + }); |
535 | 363 | }, |
536 | | - |
| 364 | + |
537 | 365 | /** |
538 | | - * Check for bogus resolutions of the media asset that has not loaded. |
539 | | - * @return |
540 | | - * true if the resolution is "likely" to be updated |
541 | | - * by waiting for metadata |
542 | | - * false if the resolution has been set via an attribute or is already loaded |
543 | | - */ |
| 366 | + * Check for bogus resolutions of the media asset that has not loaded. |
| 367 | + * |
| 368 | + * @return true if the resolution is "likely" to be updated by waiting for |
| 369 | + * metadata false if the resolution has been set via an attribute or |
| 370 | + * is already loaded |
| 371 | + */ |
544 | 372 | waitForMetaCheck: function( playerElement ){ |
545 | 373 | var waitForMeta = false; |
546 | 374 | if( !playerElement ) |
— | — | @@ -550,19 +378,19 @@ |
551 | 379 | { |
552 | 380 | return false; |
553 | 381 | } |
554 | | - |
555 | | - |
| 382 | + |
| 383 | + |
556 | 384 | var width = $j( playerElement ).css( 'width' ); |
557 | 385 | var height = $j( playerElement ).css( 'height' ); |
558 | | - |
| 386 | + |
559 | 387 | // Css video defaults |
560 | | - if( $j( playerElement ).css( 'width' ) == '300px' && |
| 388 | + if( $j( playerElement ).css( 'width' ) == '300px' && |
561 | 389 | $j( playerElement ).css( 'height' ) == '150px' |
562 | 390 | ){ |
563 | 391 | waitForMeta = true; |
564 | 392 | } else { |
565 | 393 | // Check if we should wait for duration: |
566 | | - if( $j( playerElement ).attr( 'duration') || |
| 394 | + if( $j( playerElement ).attr( 'duration') || |
567 | 395 | $j( playerElement ).attr('durationHint') |
568 | 396 | ){ |
569 | 397 | // height, width and duration set; do not wait for meta data: |
— | — | @@ -571,30 +399,31 @@ |
572 | 400 | waitForMeta = true; |
573 | 401 | } |
574 | 402 | } |
575 | | - |
576 | | - //Firefox ~ sometimes ~ gives -1 for unloaded media |
| 403 | + |
| 404 | + // Firefox ~ sometimes ~ gives -1 for unloaded media |
577 | 405 | if ( $j(playerElement).attr( 'width' ) == -1 || $j(playerElement).attr( 'height' ) == -1 ) { |
578 | | - waitForMeta = true; |
| 406 | + waitForMeta = true; |
579 | 407 | } |
580 | | - |
| 408 | + |
581 | 409 | // Google Chrome / safari gives 0 width height for unloaded media |
582 | | - if( $j(playerElement).attr( 'width' ) === 0 || |
583 | | - $j(playerElement).attr( 'height' ) === 0 |
| 410 | + if( $j(playerElement).attr( 'width' ) === 0 || |
| 411 | + $j(playerElement).attr( 'height' ) === 0 |
584 | 412 | ) { |
585 | 413 | waitForMeta = true; |
586 | 414 | } |
587 | | - |
| 415 | + |
588 | 416 | // Firefox default width height is ~sometimes~ 150 / 300 |
589 | 417 | if( this.height == 150 && this.width == 300 ){ |
590 | | - waitForMeta = true; |
| 418 | + waitForMeta = true; |
591 | 419 | } |
592 | | - |
| 420 | + |
593 | 421 | // Make sure we have a src attribute or source child |
594 | | - // ( i.e not a video tag to be dynamically populated or looked up from xml resource description ) |
595 | | - if( waitForMeta && |
| 422 | + // ( i.e not a video tag to be dynamically populated or looked up from |
| 423 | + // xml resource description ) |
| 424 | + if( waitForMeta && |
596 | 425 | ( |
597 | 426 | $j( playerElement ).attr('src') || |
598 | | - $j( playerElement ).find("source[src]").length !== 0 |
| 427 | + $j( playerElement ).find("source[src]").length !== 0 |
599 | 428 | ) |
600 | 429 | ) { |
601 | 430 | // Detect src type ( if no type set ) |
— | — | @@ -602,50 +431,54 @@ |
603 | 432 | } else { |
604 | 433 | // playerElement is not likely to update its meta data ( no src ) |
605 | 434 | return false; |
606 | | - } |
| 435 | + } |
607 | 436 | }, |
608 | | - |
| 437 | + |
609 | 438 | /** |
610 | | - * swapEmbedPlayerElement |
611 | | - * |
612 | | - * Takes a video element as input and swaps it out with |
613 | | - * an embed player interface |
614 | | - * |
615 | | - * @param {Element} targetElement Element to be swapped |
616 | | - * @param {Object} playerInterface Interface to swap into the target element |
617 | | - */ |
| 439 | + * swapEmbedPlayerElement |
| 440 | + * |
| 441 | + * Takes a video element as input and swaps it out with an embed player |
| 442 | + * interface |
| 443 | + * |
| 444 | + * @param {Element} |
| 445 | + * targetElement Element to be swapped |
| 446 | + * @param {Object} |
| 447 | + * playerInterface Interface to swap into the target element |
| 448 | + */ |
618 | 449 | swapEmbedPlayerElement: function( targetElement, playerInterface ) { |
619 | 450 | mw.log( 'EmbedPlayer::swapEmbedPlayerElement: ' + targetElement.id ); |
620 | 451 | // Create a new element to swap the player interface into |
621 | 452 | var swapPlayerElement = document.createElement('div'); |
622 | | - |
| 453 | + |
623 | 454 | // Get properties / methods from playerInterface |
624 | 455 | for ( var method in playerInterface ) { |
625 | | - if ( method != 'readyState' ) { // readyState crashes IE ( don't include ) |
| 456 | + if ( method != 'readyState' ) { // readyState crashes IE ( don't |
| 457 | + // include ) |
626 | 458 | swapPlayerElement[ method ] = playerInterface[ method ]; |
627 | 459 | } |
628 | 460 | } |
629 | | - |
630 | | - // Check if we are using native controls ( should keep the video embed around ) |
| 461 | + |
| 462 | + // Check if we are using native controls ( should keep the video embed |
| 463 | + // around ) |
631 | 464 | if( playerInterface.useNativePlayerControls() ) { |
632 | 465 | $j( targetElement ) |
633 | 466 | .attr( 'id', playerInterface.pid ) |
634 | 467 | .addClass( 'nativeEmbedPlayerPid' ) |
635 | 468 | .show() |
636 | | - .after( |
| 469 | + .after( |
637 | 470 | $j( swapPlayerElement ).css( 'display', 'none' ) |
638 | 471 | ); |
639 | 472 | } else { |
640 | 473 | $j( targetElement ).replaceWith( swapPlayerElement ); |
641 | 474 | } |
642 | | - |
643 | | - |
| 475 | + |
| 476 | + |
644 | 477 | // Set swapPlayerElement has height / width set and set to loading: |
645 | 478 | $j( swapPlayerElement ).css( { |
646 | 479 | 'width' : playerInterface.width, |
647 | 480 | 'height' : playerInterface.height |
648 | 481 | } ); |
649 | | - |
| 482 | + |
650 | 483 | // If we don't already have a loadSpiner add one: |
651 | 484 | if( $j('#loadingSpinner_' + playerInterface.id ).length == 0 ){ |
652 | 485 | if( playerInterface.useNativePlayerControls() ) { |
— | — | @@ -653,7 +486,7 @@ |
654 | 487 | .getAbsoluteOverlaySpinner() |
655 | 488 | .attr('id', 'loadingSpinner_' + playerInterface.id ); |
656 | 489 | }else{ |
657 | | - $j( swapPlayerElement ).append( |
| 490 | + $j( swapPlayerElement ).append( |
658 | 491 | $j('<div />') |
659 | 492 | .loadingSpinner() |
660 | 493 | ); |
— | — | @@ -661,29 +494,29 @@ |
662 | 495 | } |
663 | 496 | return swapPlayerElement; |
664 | 497 | }, |
665 | | - |
666 | | - |
| 498 | + |
| 499 | + |
667 | 500 | /** |
668 | | - * Player ready will run the global callbacks |
669 | | - * once players are "ready" |
670 | | - * |
671 | | - * This enables mw.ready event to expose video tag |
672 | | - * elements as if the videotag was supported natively. |
673 | | - * |
674 | | - * @param {Object} player The EmbedPlayer object |
675 | | - */ |
| 501 | + * Player ready will run the global callbacks once players are "ready" |
| 502 | + * |
| 503 | + * This enables mw.ready event to expose video tag elements as if the |
| 504 | + * videotag was supported natively. |
| 505 | + * |
| 506 | + * @param {Object} |
| 507 | + * player The EmbedPlayer object |
| 508 | + */ |
676 | 509 | playerReady: function( player ) { |
677 | 510 | var _this = this; |
678 | 511 | mw.log( 'EmbedPlayer::ReadyToPlay callback player:' + player.id ); |
679 | 512 | player.readyToPlay = true; |
680 | | - |
| 513 | + |
681 | 514 | // Remove the player loader spinner: |
682 | 515 | $j('#loadingSpinner_' + player.id ).remove(); |
683 | | - |
| 516 | + |
684 | 517 | // Run the player ready trigger |
685 | 518 | $j( player ).trigger( 'playerReady' ); |
686 | | - |
687 | | - var is_ready = true; |
| 519 | + |
| 520 | + var is_ready = true; |
688 | 521 | for ( var i = 0; i < this.playerList.length; i++ ) { |
689 | 522 | var currentPlayer = $j( '#' + this.playerList[i] ).get( 0 ); |
690 | 523 | if ( player ) { |
— | — | @@ -694,8 +527,8 @@ |
695 | 528 | if ( is_ready ) { |
696 | 529 | // Be sure to remove any player loader spinners |
697 | 530 | $j('.playerLoadingSpinner').remove(); |
698 | | - |
699 | | - mw.log( "EmbedPlayer::All on-page players ready run playerManager callbacks" ); |
| 531 | + |
| 532 | + mw.log( "EmbedPlayer::All on-page players ready run playerMannager callbacks" ); |
700 | 533 | // Run queued functions |
701 | 534 | if( _this.callbackFunctions ) { |
702 | 535 | while ( _this.callbackFunctions.length ) { |
— | — | @@ -707,10 +540,12 @@ |
708 | 541 | }; |
709 | 542 | |
710 | 543 | /** |
711 | | - * mediaSource class represents a source for a media element. |
712 | | - * @param {Element} element: MIME type of the source. |
713 | | - * @constructor |
714 | | - */ |
| 544 | + * mediaSource class represents a source for a media element. |
| 545 | + * |
| 546 | + * @param {Element} |
| 547 | + * element: MIME type of the source. |
| 548 | + * @constructor |
| 549 | + */ |
715 | 550 | function mediaSource( element ) { |
716 | 551 | this.init( element ); |
717 | 552 | } |
— | — | @@ -718,50 +553,50 @@ |
719 | 554 | mediaSource.prototype = { |
720 | 555 | // MIME type of the source. |
721 | 556 | mimeType:null, |
722 | | - |
| 557 | + |
723 | 558 | // URI of the source. |
724 | 559 | uri:null, |
725 | | - |
| 560 | + |
726 | 561 | // Title of the source. |
727 | 562 | title: null, |
728 | | - |
| 563 | + |
729 | 564 | // True if the source has been marked as the default. |
730 | 565 | markedDefault: false, |
731 | | - |
| 566 | + |
732 | 567 | // True if the source supports url specification of offset and duration |
733 | 568 | URLTimeEncoding:false, |
734 | | - |
| 569 | + |
735 | 570 | // Start offset of the requested segment |
736 | 571 | startOffset: 0, |
737 | | - |
| 572 | + |
738 | 573 | // Duration of the requested segment (0 if not known) |
739 | 574 | duration:0, |
740 | | - |
| 575 | + |
741 | 576 | // Is the source playable |
742 | 577 | is_playable: null, |
743 | | - |
| 578 | + |
744 | 579 | // source id |
745 | 580 | id: null, |
746 | | - |
| 581 | + |
747 | 582 | // Start time in npt format |
748 | 583 | start_npt: null, |
749 | | - |
| 584 | + |
750 | 585 | // End time in npt format |
751 | 586 | end_npt: null, |
752 | | - |
| 587 | + |
753 | 588 | // A provider "id" to identify api request type |
754 | 589 | provider_type : null, |
755 | 590 | |
756 | 591 | // The api url for the provider |
757 | | - provider_url : null, |
758 | | - |
| 592 | + provider_url : null, |
| 593 | + |
759 | 594 | /** |
760 | | - * MediaSource constructor: |
761 | | - */ |
| 595 | + * MediaSource constructor: |
| 596 | + */ |
762 | 597 | init : function( element ) { |
763 | 598 | // mw.log('EmbedPlayer::adding mediaSource: ' + element); |
764 | 599 | this.src = $j( element ).attr( 'src' ); |
765 | | - |
| 600 | + |
766 | 601 | // Set default URLTimeEncoding if we have a time url: |
767 | 602 | // not ideal way to discover if content is on an oggz_chop server. |
768 | 603 | // should check some other way. |
— | — | @@ -769,9 +604,9 @@ |
770 | 605 | if ( typeof pUrl[ 'queryKey' ][ 't' ] != 'undefined' ) { |
771 | 606 | this.URLTimeEncoding = true; |
772 | 607 | } |
773 | | - |
774 | | - var sourceAttr = mw.getConfig( 'embedPlayerSourceAttributes' ); |
775 | | - |
| 608 | + |
| 609 | + var sourceAttr = mw.getConfig( 'EmbedPlayer.SourceAttributes' ); |
| 610 | + |
776 | 611 | for ( var i = 0; i < sourceAttr.length; i++ ) { // array loop: |
777 | 612 | var attr = sourceAttr[ i ]; |
778 | 613 | var attr_value = element.getAttribute( attr ); |
— | — | @@ -779,8 +614,8 @@ |
780 | 615 | this[ attr ] = attr_value; |
781 | 616 | } |
782 | 617 | } |
783 | | - |
784 | | - |
| 618 | + |
| 619 | + |
785 | 620 | // Set the content type: |
786 | 621 | if ( $j( element ).attr( 'type' ) ) { |
787 | 622 | this.mimeType = $j( element ).attr( 'type' ); |
— | — | @@ -792,47 +627,53 @@ |
793 | 628 | } else { |
794 | 629 | this.mimeType = this.detectType( this.src ); |
795 | 630 | } |
796 | | - |
| 631 | + |
797 | 632 | // Conform the mime type to ogg |
798 | 633 | if( this.mimeType == 'video/theora') { |
799 | 634 | this.mimeType = 'video/ogg'; |
800 | 635 | } |
801 | | - |
| 636 | + |
802 | 637 | if( this.mimeType == 'audio/vorbis') { |
803 | 638 | this.mimeType = 'audio/ogg'; |
804 | 639 | } |
805 | | - |
| 640 | + |
806 | 641 | // Check for parent elements ( supplies categories in "track" ) |
807 | 642 | if( $j( element ).parent().attr('category') ) { |
808 | 643 | this.category = $j( element ).parent().attr('category'); |
809 | 644 | } |
810 | | - |
| 645 | + |
811 | 646 | if( $j( element ).attr( 'default' ) ){ |
812 | 647 | this.markedDefault = true; |
813 | 648 | } |
814 | | - |
| 649 | + |
815 | 650 | // Get the url duration ( if applicable ) |
816 | 651 | this.getURLDuration(); |
817 | 652 | }, |
818 | | - |
| 653 | + |
819 | 654 | /** |
820 | | - * Update Source title via Element |
821 | | - * @param {Element} element Source element to update attributes from |
822 | | - */ |
| 655 | + * Update Source title via Element |
| 656 | + * |
| 657 | + * @param {Element} |
| 658 | + * element Source element to update attributes from |
| 659 | + */ |
823 | 660 | updateSource: function( element ) { |
824 | 661 | // for now just update the title: |
825 | 662 | if ( $j( element ).attr( "title" ) ) { |
826 | 663 | this.title = $j( element ).attr( "title" ); |
827 | 664 | } |
828 | 665 | }, |
829 | | - |
| 666 | + |
830 | 667 | /** |
831 | 668 | * Updates the src time and start & end |
832 | | - * @param {String} start_time: in NPT format |
833 | | - * @param {String} end_time: in NPT format |
| 669 | + * |
| 670 | + * @param {String} |
| 671 | + * start_time: in NPT format |
| 672 | + * @param {String} |
| 673 | + * end_time: in NPT format |
834 | 674 | */ |
835 | 675 | updateSrcTime: function ( start_npt, end_npt ) { |
836 | | - // mw.log("f:updateSrcTime: "+ start_npt+'/'+ end_npt + ' from org: ' + this.start_npt+ '/'+this.end_npt); |
| 676 | + // mw.log("f:updateSrcTime: "+ start_npt+'/'+ end_npt + ' from org: ' + |
| 677 | + // this.start_npt+ '/'+this.end_npt); |
837 | 678 | // mw.log("pre uri:" + this.src); |
838 | 679 | // if we have time we can use: |
839 | 680 | if ( this.URLTimeEncoding ) { |
— | — | @@ -840,35 +681,38 @@ |
841 | 682 | if ( !mw.npt2seconds( start_npt ) ) { |
842 | 683 | start_npt = this.start_npt; |
843 | 684 | } |
844 | | - |
| 685 | + |
845 | 686 | if ( !mw.npt2seconds( end_npt ) ) { |
846 | 687 | end_npt = this.end_npt; |
847 | 688 | } |
848 | | - |
849 | | - this.src = mw.replaceUrlParams( this.src, { |
850 | | - 't': start_npt + '/' + end_npt |
| 689 | + |
| 690 | + this.src = mw.replaceUrlParams( this.src, { |
| 691 | + 't': start_npt + '/' + end_npt |
851 | 692 | }); |
852 | | - |
| 693 | + |
853 | 694 | // update the duration |
854 | 695 | this.getURLDuration(); |
855 | 696 | } |
856 | 697 | }, |
857 | | - |
| 698 | + |
858 | 699 | /** |
859 | | - * Sets the duration and sets the end time if unset |
860 | | - * @param {Float} duration: in seconds |
861 | | - */ |
| 700 | + * Sets the duration and sets the end time if unset |
| 701 | + * |
| 702 | + * @param {Float} |
| 703 | + * duration: in seconds |
| 704 | + */ |
862 | 705 | setDuration: function ( duration ) { |
863 | 706 | this.duration = duration; |
864 | 707 | if ( !this.end_npt ) { |
865 | 708 | this.end_npt = mw.seconds2npt( this.startOffset + duration ); |
866 | 709 | } |
867 | 710 | }, |
868 | | - |
| 711 | + |
869 | 712 | /** |
870 | | - * MIME type accessors function. |
871 | | - * @return {String} the MIME type of the source. |
872 | | - */ |
| 713 | + * MIME type accessors function. |
| 714 | + * |
| 715 | + * @return {String} the MIME type of the source. |
| 716 | + */ |
873 | 717 | getMIMEType: function() { |
874 | 718 | if( this.mimeType ) { |
875 | 719 | return this.mimeType; |
— | — | @@ -876,11 +720,15 @@ |
877 | 721 | this.mimeType = this.detectType( this.src ); |
878 | 722 | return this.mimeType; |
879 | 723 | }, |
880 | | - |
881 | | - /** URI function. |
882 | | - * @param {Number} serverSeekTime Int: Used to adjust the URI for url based seeks) |
883 | | - * @return {String} the URI of the source. |
884 | | - */ |
| 724 | + |
| 725 | + /** |
| 726 | + * URI function. |
| 727 | + * |
| 728 | + * @param {Number} |
| 729 | + * serverSeekTime Int: Used to adjust the URI for url based |
| 730 | + * seeks) |
| 731 | + * @return {String} the URI of the source. |
| 732 | + */ |
885 | 733 | getSrc : function( serverSeekTime ) { |
886 | 734 | if ( !serverSeekTime || !this.URLTimeEncoding ) { |
887 | 735 | return this.src; |
— | — | @@ -891,20 +739,21 @@ |
892 | 740 | } |
893 | 741 | return mw.replaceUrlParams( this.src, |
894 | 742 | { |
895 | | - 't' : mw.seconds2npt( serverSeekTime ) + endvar |
896 | | - } |
897 | | - ); |
| 743 | + 't': mw.seconds2npt( serverSeekTime ) + endvar |
| 744 | + } |
| 745 | + ); |
898 | 746 | }, |
899 | | - |
| 747 | + |
900 | 748 | /** |
901 | | - * Title accessor function. |
902 | | - * @return {String} Title of the source. |
903 | | - */ |
| 749 | + * Title accessor function. |
| 750 | + * |
| 751 | + * @return {String} Title of the source. |
| 752 | + */ |
904 | 753 | getTitle : function() { |
905 | 754 | if( this.title ){ |
906 | 755 | return this.title; |
907 | 756 | } |
908 | | - |
| 757 | + |
909 | 758 | // Return a Title based on mime type: |
910 | 759 | switch( this.getMIMEType() ) { |
911 | 760 | case 'video/h264' : |
— | — | @@ -926,21 +775,21 @@ |
927 | 776 | return 'AVI video'; // FIXME: i18n |
928 | 777 | break; |
929 | 778 | } |
930 | | - |
931 | | - // Return Title based on file name: |
| 779 | + |
| 780 | + // Return tilte based on file name: |
932 | 781 | var urlParts = mw.parseUri( this.getSrc() ); |
933 | 782 | if( urlParts.file ){ |
934 | 783 | return urlParts.file; |
935 | 784 | } |
936 | | - |
| 785 | + |
937 | 786 | // Return the mime type string if not known type. |
938 | 787 | return this.mimeType; |
939 | 788 | }, |
940 | | - |
| 789 | + |
941 | 790 | /** |
942 | | - * |
| 791 | + * |
943 | 792 | * Get Duration of the media in milliseconds from the source url. |
944 | | - * |
| 793 | + * |
945 | 794 | * Supports media_url?t=ntp_start/ntp_end url request format |
946 | 795 | */ |
947 | 796 | getURLDuration : function() { |
— | — | @@ -964,57 +813,60 @@ |
965 | 814 | } |
966 | 815 | } |
967 | 816 | }, |
968 | | - |
| 817 | + |
969 | 818 | /** |
970 | | - * Attempts to detect the type of a media file based on the URI. |
971 | | - * @param {String} uri URI of the media file. |
972 | | - * @return {String} The guessed MIME type of the file. |
973 | | - */ |
| 819 | + * Attempts to detect the type of a media file based on the URI. |
| 820 | + * |
| 821 | + * @param {String} |
| 822 | + * uri URI of the media file. |
| 823 | + * @return {String} The guessed MIME type of the file. |
| 824 | + */ |
974 | 825 | detectType: function( uri ) { |
975 | 826 | // NOTE: if media is on the same server as the javascript |
976 | | - // we can issue a HEAD request and read the mime type of the media... |
| 827 | + // we can issue a HEAD request and read the mime type of the media ... |
977 | 828 | // ( this will detect media mime type independently of the url name) |
978 | 829 | // http://www.jibbering.com/2002/4/httprequest.html |
979 | 830 | var end_inx = ( uri.indexOf( '?' ) != -1 ) ? uri.indexOf( '?' ) : uri.length; |
980 | 831 | var no_param_uri = uri.substr( 0, end_inx ); |
981 | | - switch( no_param_uri.substr( no_param_uri.lastIndexOf( '.' ), 4 ).toLowerCase() ) { |
| 832 | + switch( no_param_uri.substr( no_param_uri.lastIndexOf( '.' )+1 ).toLowerCase() ) { |
982 | 833 | case 'smil': |
983 | | - case '.sml': |
984 | | - return 'application/smil'; |
| 834 | + case 'sml': |
| 835 | + return 'application/smil' |
985 | 836 | break; |
986 | | - case '.m4v': |
987 | | - case '.mp4': |
| 837 | + case 'm4v': |
| 838 | + case 'mp4': |
| 839 | + case 'mov': |
988 | 840 | return 'video/h264'; |
989 | 841 | break; |
990 | 842 | case 'webm': |
991 | 843 | return 'video/webm'; |
992 | 844 | break; |
993 | | - case '.srt': |
| 845 | + case 'srt': |
994 | 846 | return 'text/x-srt'; |
995 | 847 | break; |
996 | | - case '.flv': |
| 848 | + case 'flv': |
997 | 849 | return 'video/x-flv'; |
998 | 850 | break; |
999 | | - case '.ogg': |
1000 | | - case '.ogv': |
| 851 | + case 'ogg': |
| 852 | + case 'ogv': |
1001 | 853 | return 'video/ogg'; |
1002 | 854 | break; |
1003 | | - case '.oga': |
| 855 | + case 'oga': |
1004 | 856 | return 'audio/ogg'; |
1005 | 857 | break; |
1006 | | - case '.anx': |
| 858 | + case 'anx': |
1007 | 859 | return 'video/ogg'; |
1008 | 860 | break; |
1009 | | - case '.xml': |
| 861 | + case 'xml': |
1010 | 862 | return 'text/xml'; |
1011 | 863 | break; |
1012 | | - case '.avi': |
| 864 | + case 'avi': |
1013 | 865 | return 'video/x-msvideo'; |
1014 | 866 | break; |
1015 | | - case '.mpg': |
| 867 | + case 'mpg': |
1016 | 868 | return 'video/mpeg'; |
1017 | 869 | break; |
1018 | | - case '.mpeg': |
| 870 | + case 'mpeg': |
1019 | 871 | return 'video/mpeg'; |
1020 | 872 | break; |
1021 | 873 | } |
— | — | @@ -1022,95 +874,103 @@ |
1023 | 875 | }; |
1024 | 876 | |
1025 | 877 | /** |
1026 | | -* A media element corresponding to a <video> element. |
1027 | | -* |
1028 | | -* It is implemented as a collection of mediaSource objects. The media sources |
1029 | | -* will be initialized from the <video> element, its child <source> elements, |
1030 | | -* and/or the ROE file referenced by the <video> element. |
1031 | | -* @param {element} videoElement <video> element used for initialization. |
1032 | | -* @constructor |
1033 | | -*/ |
| 878 | + * A media element corresponding to a <video> element. |
| 879 | + * |
| 880 | + * It is implemented as a collection of mediaSource objects. The media sources |
| 881 | + * will be initialized from the <video> element, its child <source> elements, |
| 882 | + * and/or the ROE file referenced by the <video> element. |
| 883 | + * |
| 884 | + * @param {element} |
| 885 | + * videoElement <video> element used for initialization. |
| 886 | + * @constructor |
| 887 | + */ |
1034 | 888 | function mediaElement( element ) { |
1035 | 889 | this.init( element ); |
1036 | 890 | } |
1037 | 891 | |
1038 | 892 | mediaElement.prototype = { |
1039 | | - |
| 893 | + |
1040 | 894 | // The array of mediaSource elements. |
1041 | 895 | sources: null, |
1042 | | - |
| 896 | + |
1043 | 897 | // flag for ROE data being added. |
1044 | 898 | addedROEData: false, |
1045 | | - |
| 899 | + |
1046 | 900 | // Selected mediaSource element. |
1047 | 901 | selectedSource: null, |
1048 | | - |
| 902 | + |
1049 | 903 | // Media element thumbnail |
1050 | 904 | thumbnail: null, |
1051 | | - |
| 905 | + |
1052 | 906 | // Media element linkback |
1053 | 907 | linkback: null, |
1054 | 908 | |
1055 | 909 | /** |
1056 | | - * Media Element constructor |
1057 | | - * |
1058 | | - * Sets up a mediaElement from a provided top level "video" element |
1059 | | - * adds any child sources that are found |
1060 | | - * |
1061 | | - * @param {Element} videoElement Element that has src attribute or has children source elements |
1062 | | - */ |
| 910 | + * Media Element constructor |
| 911 | + * |
| 912 | + * Sets up a mediaElement from a provided top level "video" element adds any |
| 913 | + * child sources that are found |
| 914 | + * |
| 915 | + * @param {Element} |
| 916 | + * videoElement Element that has src attribute or has children |
| 917 | + * source elements |
| 918 | + */ |
1063 | 919 | init: function( videoElement ) { |
1064 | 920 | var _this = this; |
1065 | 921 | mw.log( "EmbedPlayer::mediaElement:init:" + videoElement.id ); |
1066 | 922 | this.sources = new Array(); |
1067 | | - |
| 923 | + |
1068 | 924 | // Process the videoElement as a source element: |
1069 | 925 | if ( $j( videoElement ).attr( "src" ) ) { |
1070 | 926 | _this.tryAddSource( videoElement ); |
1071 | 927 | } |
1072 | | - |
| 928 | + |
1073 | 929 | // Process elements source children |
1074 | 930 | $j( videoElement ).find( 'source,track' ).each( function( ) { |
1075 | 931 | _this.tryAddSource( this ); |
1076 | 932 | } ); |
1077 | 933 | }, |
1078 | | - |
| 934 | + |
1079 | 935 | /** |
1080 | | - * Updates the time request for all sources that have |
1081 | | - * a standard time request argument (ie &t=start_time/end_time) |
1082 | | - * |
1083 | | - * @param {String} start_npt Start time in npt format |
1084 | | - * @param {String} end_npt End time in npt format |
1085 | | - */ |
| 936 | + * Updates the time request for all sources that have a standard time |
| 937 | + * request argument (ie &t=start_time/end_time) |
| 938 | + * |
| 939 | + * @param {String} |
| 940 | + * start_npt Start time in npt format |
| 941 | + * @param {String} |
| 942 | + * end_npt End time in npt format |
| 943 | + */ |
1086 | 944 | updateSourceTimes: function( start_npt, end_npt ) { |
1087 | 945 | var _this = this; |
1088 | 946 | $j.each( this.sources, function( inx, mediaSource ) { |
1089 | 947 | mediaSource.updateSrcTime( start_npt, end_npt ); |
1090 | 948 | } ); |
1091 | 949 | }, |
1092 | | - |
| 950 | + |
1093 | 951 | /** |
1094 | | - * Check for Timed Text tracks |
1095 | | - * @return {Boolean} True if text tracks exist, false if no text tracks are found |
1096 | | - */ |
| 952 | + * Check for Timed Text tracks |
| 953 | + * |
| 954 | + * @return {Boolean} True if text tracks exist, false if no text tracks are |
| 955 | + * found |
| 956 | + */ |
1097 | 957 | textSourceExists: function() { |
1098 | 958 | for ( var i = 0; i < this.sources.length; i++ ) { |
1099 | | - mw.log('EmbedPlayer::textSourceExists:'+ this.sources[i].mimeType ); |
1100 | | - if ( this.sources[i].mimeType == 'text/cmml' || |
1101 | | - this.sources[i].mimeType == 'text/x-srt' ) |
| 959 | + if ( this.sources[i].mimeType == 'text/cmml' || |
| 960 | + this.sources[i].mimeType == 'text/x-srt' ) |
1102 | 961 | { |
1103 | 962 | return true; |
1104 | 963 | } |
1105 | 964 | }; |
1106 | 965 | return false; |
1107 | 966 | }, |
1108 | | - |
| 967 | + |
1109 | 968 | /** |
1110 | | - * Returns the array of mediaSources of this element. |
1111 | | - * |
1112 | | - * @param {String} [mimeFilter] Filter criteria for set of mediaSources to return |
1113 | | - * @return {Array} mediaSource elements. |
1114 | | - */ |
| 969 | + * Returns the array of mediaSources of this element. |
| 970 | + * |
| 971 | + * @param {String} |
| 972 | + * [mimeFilter] Filter criteria for set of mediaSources to return |
| 973 | + * @return {Array} mediaSource elements. |
| 974 | + */ |
1115 | 975 | getSources: function( mimeFilter ) { |
1116 | 976 | if ( !mimeFilter ) { |
1117 | 977 | return this.sources; |
— | — | @@ -1119,19 +979,21 @@ |
1120 | 980 | var source_set = new Array(); |
1121 | 981 | for ( var i = 0; i < this.sources.length ; i++ ) { |
1122 | 982 | if ( this.sources[i].mimeType && |
1123 | | - this.sources[i].mimeType.indexOf( mimeFilter ) != -1 ) |
| 983 | + this.sources[i].mimeType.indexOf( mimeFilter ) != -1 ) |
1124 | 984 | { |
1125 | 985 | source_set.push( this.sources[i] ); |
1126 | 986 | } |
1127 | 987 | } |
1128 | 988 | return source_set; |
1129 | 989 | }, |
1130 | | - |
| 990 | + |
1131 | 991 | /** |
1132 | | - * Selects a source by id |
1133 | | - * @param {String} source_id Id of the source to select. |
1134 | | - * @return {MediaSource} The selected mediaSource or null if not found |
1135 | | - */ |
| 992 | + * Selects a source by id |
| 993 | + * |
| 994 | + * @param {String} |
| 995 | + * source_id Id of the source to select. |
| 996 | + * @return {MediaSource} The selected mediaSource or null if not found |
| 997 | + */ |
1136 | 998 | getSourceById:function( source_id ) { |
1137 | 999 | for ( var i = 0; i < this.sources.length ; i++ ) { |
1138 | 1000 | if ( this.sources[i].id == source_id ) { |
— | — | @@ -1140,12 +1002,13 @@ |
1141 | 1003 | } |
1142 | 1004 | return null; |
1143 | 1005 | }, |
1144 | | - |
| 1006 | + |
1145 | 1007 | /** |
1146 | | - * Selects a particular source for playback updating the "selectedSource" |
1147 | | - * |
1148 | | - * @param {Number} index Index of source element to set as selectedSource |
1149 | | - */ |
| 1008 | + * Selects a particular source for playback updating the "selectedSource" |
| 1009 | + * |
| 1010 | + * @param {Number} |
| 1011 | + * index Index of source element to set as selectedSource |
| 1012 | + */ |
1150 | 1013 | selectSource:function( index ) { |
1151 | 1014 | mw.log( 'EmbedPlayer::mediaElement:selectSource:' + index ); |
1152 | 1015 | var playableSources = this.getPlayableSources(); |
— | — | @@ -1158,16 +1021,22 @@ |
1159 | 1022 | } |
1160 | 1023 | } |
1161 | 1024 | }, |
1162 | | - |
| 1025 | + |
1163 | 1026 | /** |
1164 | | - * Selects the default source via cookie preference, default marked, or by id order |
1165 | | - */ |
1166 | | - autoSelectSource: function() { |
1167 | | - mw.log( 'EmbedPlayer::mediaElement::autoSelectSource:' + this.id); |
| 1027 | + * Selects the default source via cookie preference, default marked, or by |
| 1028 | + * id order |
| 1029 | + */ |
| 1030 | + autoSelectSource: function() { |
| 1031 | + mw.log( 'EmbedPlayer::mediaElement::autoSelectSource' ); |
1168 | 1032 | // Select the default source |
1169 | 1033 | var playableSources = this.getPlayableSources(); |
1170 | 1034 | var flash_flag = ogg_flag = false; |
1171 | | - |
| 1035 | + |
| 1036 | + // Check if there are any playableSources |
| 1037 | + if( playableSources.length == 0 ){ |
| 1038 | + return false; |
| 1039 | + } |
| 1040 | + |
1172 | 1041 | // Set via user-preference |
1173 | 1042 | for ( var source = 0; source < playableSources.length; source++ ) { |
1174 | 1043 | var mimeType = playableSources[source].mimeType; |
— | — | @@ -1177,7 +1046,7 @@ |
1178 | 1047 | return true; |
1179 | 1048 | } |
1180 | 1049 | } |
1181 | | - |
| 1050 | + |
1182 | 1051 | // Set via marked default: |
1183 | 1052 | for ( var source = 0; source < playableSources.length; source++ ) { |
1184 | 1053 | if ( playableSources[ source ].markedDefault ) { |
— | — | @@ -1186,7 +1055,7 @@ |
1187 | 1056 | return true; |
1188 | 1057 | } |
1189 | 1058 | } |
1190 | | - |
| 1059 | + |
1191 | 1060 | // Prefer native playback |
1192 | 1061 | for ( var source = 0; source < playableSources.length; source++ ) { |
1193 | 1062 | var mimeType = playableSources[source].mimeType; |
— | — | @@ -1197,15 +1066,15 @@ |
1198 | 1067 | return true; |
1199 | 1068 | } |
1200 | 1069 | } |
1201 | | - |
| 1070 | + |
1202 | 1071 | // Set h264 via native or flash fallback |
1203 | 1072 | for ( var source = 0; source < playableSources.length; source++ ) { |
1204 | 1073 | var mimeType = playableSources[source].mimeType; |
1205 | 1074 | var player = mw.EmbedTypes.players.defaultPlayer( mimeType ); |
1206 | | - if ( mimeType == 'video/h264' |
1207 | | - && player |
1208 | | - && ( |
1209 | | - player.library == 'Native' |
| 1075 | + if ( mimeType == 'video/h264' |
| 1076 | + && player |
| 1077 | + && ( |
| 1078 | + player.library == 'Native' |
1210 | 1079 | || |
1211 | 1080 | player.library == 'Kplayer' |
1212 | 1081 | ) |
— | — | @@ -1215,7 +1084,7 @@ |
1216 | 1085 | return true; |
1217 | 1086 | } |
1218 | 1087 | }; |
1219 | | - |
| 1088 | + |
1220 | 1089 | // Else just select first source |
1221 | 1090 | if ( !this.selectedSource ) { |
1222 | 1091 | mw.log( 'set via first source:' + playableSources[0] ); |
— | — | @@ -1225,34 +1094,37 @@ |
1226 | 1095 | // No Source found so no source selected |
1227 | 1096 | return false; |
1228 | 1097 | }, |
1229 | | - |
| 1098 | + |
1230 | 1099 | /** |
1231 | | - * check if the mime is ogg |
1232 | | - */ |
| 1100 | + * check if the mime is ogg |
| 1101 | + */ |
1233 | 1102 | isOgg: function( mimeType ){ |
1234 | | - if ( mimeType == 'video/ogg' |
1235 | | - || mimeType == 'ogg/video' |
1236 | | - || mimeType == 'video/annodex' |
| 1103 | + if ( mimeType == 'video/ogg' |
| 1104 | + || mimeType == 'ogg/video' |
| 1105 | + || mimeType == 'video/annodex' |
1237 | 1106 | || mimeType == 'application/ogg' |
1238 | 1107 | ) { |
1239 | 1108 | return true; |
1240 | 1109 | } |
1241 | 1110 | return false; |
1242 | 1111 | }, |
1243 | | - |
| 1112 | + |
1244 | 1113 | /** |
1245 | | - * Returns the thumbnail URL for the media element. |
1246 | | - * @returns {String} thumbnail URL |
1247 | | - */ |
| 1114 | + * Returns the thumbnail URL for the media element. |
| 1115 | + * |
| 1116 | + * @returns {String} thumbnail URL |
| 1117 | + */ |
1248 | 1118 | getPosterSrc: function( ) { |
1249 | 1119 | return this.poster; |
1250 | 1120 | }, |
1251 | | - |
| 1121 | + |
1252 | 1122 | /** |
1253 | | - * Checks whether there is a stream of a specified MIME type. |
1254 | | - * @param {String} mimeType MIME type to check. |
1255 | | - * @return {Boolean} true if sources include MIME false if not. |
1256 | | - */ |
| 1123 | + * Checks whether there is a stream of a specified MIME type. |
| 1124 | + * |
| 1125 | + * @param {String} |
| 1126 | + * mimeType MIME type to check. |
| 1127 | + * @return {Boolean} true if sources include MIME false if not. |
| 1128 | + */ |
1257 | 1129 | hasStreamOfMIMEType: function( mimeType ) |
1258 | 1130 | { |
1259 | 1131 | for ( var i = 0; i < this.sources.length; i++ ) |
— | — | @@ -1263,10 +1135,10 @@ |
1264 | 1136 | } |
1265 | 1137 | return false; |
1266 | 1138 | }, |
1267 | | - |
| 1139 | + |
1268 | 1140 | /** |
1269 | | - * Checks if media is a playable type |
1270 | | - */ |
| 1141 | + * Checks if media is a playable type |
| 1142 | + */ |
1271 | 1143 | isPlayableType: function( mimeType ) { |
1272 | 1144 | if ( mw.EmbedTypes.players.defaultPlayer( mimeType ) ) { |
1273 | 1145 | return true; |
— | — | @@ -1274,17 +1146,20 @@ |
1275 | 1147 | return false; |
1276 | 1148 | } |
1277 | 1149 | }, |
1278 | | - |
| 1150 | + |
1279 | 1151 | /** |
1280 | | - * Adds a single mediaSource using the provided element if |
1281 | | - * the element has a 'src' attribute. |
1282 | | - * @param {Element} element <video>, <source> or <mediaSource> <text> element. |
1283 | | - */ |
| 1152 | + * Adds a single mediaSource using the provided element if the element has a |
| 1153 | + * 'src' attribute. |
| 1154 | + * |
| 1155 | + * @param {Element} |
| 1156 | + * element <video>, <source> or <mediaSource> <text> element. |
| 1157 | + */ |
1284 | 1158 | tryAddSource: function( element ) { |
1285 | | - //mw.log( 'f:tryAddSource:' + $j( element ).attr( "src" ) ); |
| 1159 | + // mw.log( 'f:tryAddSource:' + $j( element ).attr( "src" ) ); |
1286 | 1160 | var newSrc = $j( element ).attr( 'src' ); |
1287 | 1161 | if ( newSrc ) { |
1288 | | - // make sure an existing element with the same src does not already exist: |
| 1162 | + // make sure an existing element with the same src does not already |
| 1163 | + // exist: |
1289 | 1164 | for ( var i = 0; i < this.sources.length; i++ ) { |
1290 | 1165 | if ( this.sources[i].src == newSrc ) { |
1291 | 1166 | // Source already exists update any new attr: |
— | — | @@ -1295,45 +1170,48 @@ |
1296 | 1171 | } |
1297 | 1172 | // Create a new source |
1298 | 1173 | var source = new mediaSource( element ); |
1299 | | - |
| 1174 | + |
1300 | 1175 | this.sources.push( source ); |
1301 | | - //mw.log( 'tryAddSource: added source ::' + source + 'sl:' + this.sources.length ); |
| 1176 | + // mw.log( 'tryAddSource: added source ::' + source + 'sl:' + |
| 1177 | + // this.sources.length ); |
1302 | 1178 | return source; |
1303 | 1179 | }, |
1304 | | - |
| 1180 | + |
1305 | 1181 | /** |
1306 | | - * Get playable sources |
1307 | | - * |
1308 | | - * @returns {Array} of playable sources |
1309 | | - */ |
| 1182 | + * Get playable sources |
| 1183 | + * |
| 1184 | + * @returns {Array} of playable sources |
| 1185 | + */ |
1310 | 1186 | getPlayableSources: function() { |
1311 | | - var playableSources = []; |
| 1187 | + var playableSources = []; |
1312 | 1188 | for ( var i = 0; i < this.sources.length; i++ ) { |
1313 | 1189 | if ( this.isPlayableType( this.sources[i].mimeType ) ) { |
1314 | 1190 | playableSources.push( this.sources[i] ); |
1315 | 1191 | } else { |
1316 | | - //mw.log( "type " + this.sources[i].mimeType + 'is not playable' ); |
| 1192 | + mw.log( "type " + this.sources[i].mimeType + ' is not playable' ); |
1317 | 1193 | } |
1318 | 1194 | }; |
1319 | 1195 | return playableSources; |
1320 | 1196 | }, |
1321 | | - |
| 1197 | + |
1322 | 1198 | /** |
1323 | | - * Imports media sources from ROE data. |
1324 | | - * @param roe_data ROE data. |
1325 | | - */ |
| 1199 | + * Imports media sources from ROE data. |
| 1200 | + * |
| 1201 | + * @param roe_data |
| 1202 | + * ROE data. |
| 1203 | + */ |
1326 | 1204 | addROE: function( roe_data ) { |
1327 | 1205 | mw.log( 'EmbedPlayer::mediaElement:addROE' ); |
1328 | 1206 | this.addedROEData = true; |
1329 | 1207 | var _this = this; |
1330 | 1208 | if ( roe_data ) { |
1331 | 1209 | var $roeParsed = $j( roe_data.pay_load ); |
1332 | | - |
| 1210 | + |
1333 | 1211 | // Add media sources: |
1334 | 1212 | $roeParsed.find("mediaSource").each( function( inx, source ) { |
1335 | 1213 | _this.tryAddSource( source ); |
1336 | 1214 | } ); |
1337 | | - |
| 1215 | + |
1338 | 1216 | // Set the thumbnail: |
1339 | 1217 | $roeParsed.find( 'img' ).each( function( inx, n ) { |
1340 | 1218 | if ( $j( n ).attr( "id" ) == "stream_thumb" ) { |
— | — | @@ -1341,7 +1219,7 @@ |
1342 | 1220 | _this.poster = $j( n ).attr( "src" ); |
1343 | 1221 | } |
1344 | 1222 | } ); |
1345 | | - |
| 1223 | + |
1346 | 1224 | // Set the linkback: |
1347 | 1225 | $roeParsed.find( 'link' ).each( function( inx, n ) { |
1348 | 1226 | if ( $j( n ).attr( 'id' ) == 'html_linkback' ) { |
— | — | @@ -1357,78 +1235,91 @@ |
1358 | 1236 | |
1359 | 1237 | |
1360 | 1238 | /** |
1361 | | -* Base embedPlayer object |
1362 | | -* @param {Element} element, the element used for initialization. |
1363 | | -* @param {Object} customAttributes Attributes for the video interface |
1364 | | -* that are not already element attributes |
1365 | | -* @constructor |
1366 | | -*/ |
| 1239 | + * Base embedPlayer object |
| 1240 | + * |
| 1241 | + * @param {Element} |
| 1242 | + * element, the element used for initialization. |
| 1243 | + * @param {Object} |
| 1244 | + * customAttributes Attributes for the video interface that are not |
| 1245 | + * already element attributes |
| 1246 | + * @constructor |
| 1247 | + */ |
1367 | 1248 | mw.EmbedPlayer = function( element, customAttributes ) { |
1368 | 1249 | return this.init( element, customAttributes ); |
1369 | 1250 | }; |
1370 | 1251 | |
1371 | 1252 | mw.EmbedPlayer.prototype = { |
1372 | | - |
| 1253 | + |
1373 | 1254 | // The mediaElement object containing all mediaSource objects |
1374 | 1255 | 'mediaElement' : null, |
1375 | | - |
1376 | | - // Object that describes the supported feature set of the underling plugin / player |
| 1256 | + |
| 1257 | + // Object that describes the supported feature set of the underling plugin / |
| 1258 | + // player |
1377 | 1259 | 'supports': { }, |
1378 | | - |
| 1260 | + |
1379 | 1261 | // Preview mode flag, |
1380 | 1262 | // some plugins don't seek accurately but in preview mode we need |
1381 | 1263 | // accurate seeks so we do tricks like hide the image until its ready |
1382 | 1264 | 'preview_mode' : false, |
1383 | | - |
| 1265 | + |
1384 | 1266 | // Ready to play |
1385 | 1267 | // NOTE: we should switch over to setting the html5 video ready state |
1386 | | - 'readyToPlay' : false, |
1387 | | - |
| 1268 | + 'readyToPlay' : false, |
| 1269 | + |
1388 | 1270 | // Stores the loading errors |
1389 | | - 'loadError' : false, |
1390 | | - |
1391 | | - // Thumbnail updating flag ( to avoid rewriting an thumbnail thats already being updated) |
| 1271 | + 'loadError' : false, |
| 1272 | + |
| 1273 | + // Thumbnail updating flag ( to avoid rewriting an thumbnail thats already |
| 1274 | + // being updated) |
1392 | 1275 | 'thumbnail_updating' : false, |
1393 | | - |
| 1276 | + |
1394 | 1277 | // Thumbnail display flag |
1395 | 1278 | 'thumbnail_disp' : true, |
1396 | | - |
| 1279 | + |
1397 | 1280 | // Local variable to hold CMML meeta data about the current clip |
1398 | 1281 | // for more on CMML see: http://wiki.xiph.org/CMML |
1399 | 1282 | 'cmmlData': null, |
1400 | | - |
| 1283 | + |
1401 | 1284 | // Stores the seek time request, Updated by the doSeek function |
1402 | 1285 | 'serverSeekTime' : 0, |
1403 | | - |
| 1286 | + |
1404 | 1287 | // If the embedPlayer is current 'seeking' |
1405 | 1288 | 'seeking' : false, |
1406 | | - |
| 1289 | + |
1407 | 1290 | // Percent of the clip buffered: |
1408 | 1291 | 'bufferedPercent' : 0, |
1409 | | - |
| 1292 | + |
1410 | 1293 | // Holds the timer interval function |
1411 | 1294 | 'monitorTimerId' : null, |
1412 | | - |
| 1295 | + |
1413 | 1296 | // Buffer flags |
1414 | 1297 | 'bufferStartFlag' : false, |
1415 | 1298 | 'bufferEndFlag' : false, |
1416 | | - |
| 1299 | + |
1417 | 1300 | // On done playing |
1418 | | - 'donePlayingCount' : 0, |
1419 | | - |
| 1301 | + 'donePlayingCount' : 0 |
| 1302 | + , |
| 1303 | + // if player events should be Propagated |
| 1304 | + '_propagateEvents': true, |
| 1305 | + |
| 1306 | + |
1420 | 1307 | /** |
1421 | | - * embedPlayer constructor |
1422 | | - * |
1423 | | - * @param {Element} element DOM element that we are building the player interface for. |
1424 | | - * @param {Object} customAttributes Attributes supplied via argument (rather than applied to the element) |
1425 | | - */ |
| 1308 | + * embedPlayer constructor |
| 1309 | + * |
| 1310 | + * @param {Element} |
| 1311 | + * element DOM element that we are building the player interface |
| 1312 | + * for. |
| 1313 | + * @param {Object} |
| 1314 | + * customAttributes Attributes supplied via argument (rather than |
| 1315 | + * applied to the element) |
| 1316 | + */ |
1426 | 1317 | init: function( element, customAttributes ) { |
1427 | 1318 | var _this = this; |
1428 | 1319 | // Set customAttributes if unset: |
1429 | 1320 | if ( !customAttributes ) { |
1430 | 1321 | customAttributes = { }; |
1431 | 1322 | } |
1432 | | - |
| 1323 | + |
1433 | 1324 | var playerAttributes = mw.getConfig( 'EmbedPlayer.Attributes' ); |
1434 | 1325 | // Setup the player Interface from supported attributes: |
1435 | 1326 | for ( var attr in playerAttributes ) { |
— | — | @@ -1449,17 +1340,17 @@ |
1450 | 1341 | if( this[ attr ] == "true" ) this[attr] = true; |
1451 | 1342 | } |
1452 | 1343 | // |
1453 | | - |
1454 | | - |
| 1344 | + |
| 1345 | + |
1455 | 1346 | if( this.apiTitleKey ){ |
1456 | 1347 | this.apiTitleKey = decodeURI( this.apiTitleKey ); |
1457 | 1348 | } |
1458 | | - |
| 1349 | + |
1459 | 1350 | // Hide "controls" if using native player controls: |
1460 | 1351 | if( this.useNativePlayerControls() ){ |
1461 | 1352 | _this.controls = false; |
1462 | 1353 | } |
1463 | | - |
| 1354 | + |
1464 | 1355 | // Set the poster: |
1465 | 1356 | if ( $j( element ).attr( 'thumbnail' ) ) { |
1466 | 1357 | _this.poster = $j( element ).attr( 'thumbnail' ); |
— | — | @@ -1467,10 +1358,10 @@ |
1468 | 1359 | if ( $j( element ).attr( 'poster' ) ) { |
1469 | 1360 | _this.poster = $j( element ).attr( 'poster' ); |
1470 | 1361 | } |
1471 | | - |
| 1362 | + |
1472 | 1363 | // Set the skin name from the class |
1473 | 1364 | var sn = $j(element).attr( 'class' ); |
1474 | | - |
| 1365 | + |
1475 | 1366 | if ( sn && sn != '' ) { |
1476 | 1367 | for ( var n = 0; n < mw.validSkins.length; n++ ) { |
1477 | 1368 | if ( sn.indexOf( mw.validSkins[n].toLowerCase() ) !== -1 ) { |
— | — | @@ -1478,55 +1369,56 @@ |
1479 | 1370 | } |
1480 | 1371 | } |
1481 | 1372 | } |
1482 | | - |
| 1373 | + |
1483 | 1374 | // Set the default skin if unset: |
1484 | 1375 | if ( !this.skinName ) { |
1485 | 1376 | this.skinName = mw.getConfig( 'EmbedPlayer.SkinName' ); |
1486 | 1377 | } |
1487 | | - |
| 1378 | + |
1488 | 1379 | if( !this.monitorRate ){ |
1489 | 1380 | this.monitorRate = mw.getConfig( 'EmbedPlayer.MonitorRate' ); |
1490 | 1381 | } |
1491 | | - |
| 1382 | + |
1492 | 1383 | // Make sure startOffset is cast as an float: |
1493 | 1384 | if ( this.startOffset && this.startOffset.split( ':' ).length >= 2 ) { |
1494 | 1385 | this.startOffset = parseFloat( mw.npt2seconds( this.startOffset ) ); |
1495 | 1386 | } |
1496 | | - |
| 1387 | + |
1497 | 1388 | // Make sure offset is in float: |
1498 | 1389 | this.startOffset = parseFloat( this.startOffset ); |
1499 | | - |
1500 | | - // Set the source duration ( if provided in the element metaData or durationHint ) |
| 1390 | + |
| 1391 | + // Set the source duration ( if provided in the element metaData or |
| 1392 | + // durationHint ) |
1501 | 1393 | if ( $j( element ).attr( 'duration' ) ) { |
1502 | 1394 | _this.duration = $j( element ).attr( 'duration' ); |
1503 | 1395 | } |
1504 | | - |
| 1396 | + |
1505 | 1397 | if ( !_this.duration && $j( element ).attr( 'durationHint' ) ) { |
1506 | 1398 | _this.durationHint = $j( element ).attr( 'durationHint' ); |
1507 | 1399 | // Convert duration hint if needed: |
1508 | 1400 | _this.duration = mw.npt2seconds( _this.durationHint ); |
1509 | | - } |
1510 | | - |
| 1401 | + } |
| 1402 | + |
1511 | 1403 | // Make sure duration is a float: |
1512 | 1404 | this.duration = parseFloat( this.duration ); |
1513 | 1405 | mw.log( 'EmbedPlayer::mediaElement:' + this.id + " duration is: " + this.duration ); |
1514 | | - |
| 1406 | + |
1515 | 1407 | // Set the player size attributes based loaded video element: |
1516 | | - this.setPlayerSize( element ); |
1517 | | - |
| 1408 | + this.setPlayerSize( element ); |
| 1409 | + |
1518 | 1410 | // Set the plugin id |
1519 | 1411 | this.pid = 'pid_' + this.id; |
1520 | 1412 | |
1521 | 1413 | // Grab any innerHTML and set it to missing_plugin_html |
1522 | 1414 | // NOTE: we should strip "source" tags instead of checking and skipping |
1523 | 1415 | if ( element.innerHTML != '' && element.getElementsByTagName( 'source' ).length == 0 ) { |
1524 | | - //mw.log( 'innerHTML: ' + element.innerHTML ); |
| 1416 | + // mw.log( 'innerHTML: ' + element.innerHTML ); |
1525 | 1417 | this.user_missing_plugin_html = element.innerHTML; |
1526 | 1418 | } |
1527 | | - |
| 1419 | + |
1528 | 1420 | // Add the mediaElement object with the elements sources: |
1529 | 1421 | this.mediaElement = new mediaElement( element ); |
1530 | | - |
| 1422 | + |
1531 | 1423 | // Process attribute "sources" for dynamic embedding |
1532 | 1424 | if( customAttributes.sources && customAttributes.sources.length ){ |
1533 | 1425 | for( var i =0; i < customAttributes.sources.length ; i ++ ){ |
— | — | @@ -1534,7 +1426,8 @@ |
1535 | 1427 | if( customSource.src ){ |
1536 | 1428 | var $source = $j('<source />') |
1537 | 1429 | .attr( 'src', customSource.src ); |
1538 | | - // xxx todo pull list of valid source attributes from mediaSource prototype |
| 1430 | + // xxx todo pull list of valid source attributes from |
| 1431 | + // mediaSource prototype |
1539 | 1432 | if( customSource.type ){ |
1540 | 1433 | $source.attr('type', customSource.type ); |
1541 | 1434 | } |
— | — | @@ -1546,59 +1439,67 @@ |
1547 | 1440 | } |
1548 | 1441 | } |
1549 | 1442 | }, |
1550 | | - |
| 1443 | + |
| 1444 | + stopEventPropagation: function(){ |
| 1445 | + this._propagateEvents = false; |
| 1446 | + }, |
| 1447 | + restoreEventPropagation: function(){ |
| 1448 | + this._propagateEvents = true; |
| 1449 | + }, |
1551 | 1450 | /** |
1552 | 1451 | * for plugin-players to update supported features |
1553 | 1452 | */ |
1554 | 1453 | updateFeatureSupport: function(){ |
1555 | 1454 | return ; |
1556 | 1455 | }, |
1557 | | - |
| 1456 | + |
1558 | 1457 | /** |
1559 | | - * Set the width & height from css style attribute, element attribute, or by default value |
1560 | | - * if no css or attribute is provided set a callback to resize. |
1561 | | - * |
1562 | | - * Updates this.width & this.height |
1563 | | - * |
1564 | | - * @param {Element} element Source element to grab size from |
1565 | | - */ |
| 1458 | + * Set the width & height from css style attribute, element attribute, or by |
| 1459 | + * default value if no css or attribute is provided set a callback to |
| 1460 | + * resize. |
| 1461 | + * |
| 1462 | + * Updates this.width & this.height |
| 1463 | + * |
| 1464 | + * @param {Element} |
| 1465 | + * element Source element to grab size from |
| 1466 | + */ |
1566 | 1467 | setPlayerSize: function( element ) { |
1567 | | - |
| 1468 | + |
1568 | 1469 | this.height = $j(element).css( 'height' ); |
1569 | 1470 | this.width = $j(element).css( 'width' ); |
1570 | | - |
| 1471 | + |
1571 | 1472 | // Set to parent size ( resize events will cause player size updates) |
1572 | 1473 | if( this.height.indexOf('100%') != -1 || this.width.indexOf('100%') != -1 ){ |
1573 | | - $relativeParent = $j(element).parents().filter(function() { |
1574 | | - // reduce to only relative position or "body" elements |
1575 | | - return $j( this ).is( 'body' ) || $j(this).css( 'position' ) == 'relative'; |
| 1474 | + $relativeParent = $j(element).parents().filter(function() { |
| 1475 | + // reduce to only relative position or "body" elements |
| 1476 | + return $j(this).is('body') || $j(this).css('position') == 'relative'; |
1576 | 1477 | }).slice(0,1); // grab only the "first" |
1577 | 1478 | this.width = $relativeParent.width(); |
1578 | 1479 | this.height = $relativeParent.height(); |
1579 | 1480 | } |
1580 | | - |
1581 | | - // Make sure height and width are a number |
| 1481 | + // make sure height and width are a number |
1582 | 1482 | this.height = parseInt( this.height ); |
1583 | 1483 | this.width = parseInt( this.width ); |
1584 | 1484 | |
1585 | | - |
1586 | | - |
1587 | | - // Set via attribute if CSS is zero or NaN and we have an attribute value: |
1588 | | - this.height = ( ( this.height==0 || isNaN( this.height ) ) |
1589 | | - && $j(element).attr( 'height' ) ) ? |
| 1485 | + // Set via attribute if CSS is zero or NaN and we have an attribute |
| 1486 | + // value: |
| 1487 | + this.height = ( this.height==0 || isNaN( this.height ) |
| 1488 | + && $j(element).attr( 'height' ) ) ? |
1590 | 1489 | parseInt( $j(element).attr( 'height' ) ): this.height; |
1591 | | - this.width = ( ( this.width == 0 || isNaN( this.width ) ) |
1592 | | - && $j(element).attr( 'width' ) )? |
| 1490 | + this.width = ( this.width == 0 || isNaN( this.width ) |
| 1491 | + && $j(element).attr( 'width' ) )? |
1593 | 1492 | parseInt( $j(element).attr( 'width' ) ): this.width; |
1594 | | - |
1595 | | - |
| 1493 | + |
| 1494 | + |
1596 | 1495 | // Special case for audio |
1597 | | - // Firefox sets audio height to "0px" while webkit uses 32px .. force zero: |
| 1496 | + // Firefox sets audio height to "0px" while webkit uses 32px .. force |
| 1497 | + // zero: |
1598 | 1498 | if( element.tagName.toLowerCase() == 'audio' && this.height == '32' ) { |
1599 | 1499 | this.height = 0; |
1600 | 1500 | } |
1601 | | - |
1602 | | - // Use default aspect ration to get height or width ( if rewriting a non-audio player ) |
| 1501 | + |
| 1502 | + // Use default aspect ration to get height or width ( if rewriting a |
| 1503 | + // non-audio player ) |
1603 | 1504 | if( element.tagName.toLowerCase() != 'audio' && this.videoAspect ) { |
1604 | 1505 | var aspect = this.videoAspect.split( ':' ); |
1605 | 1506 | if( this.height && !this.width ) { |
— | — | @@ -1609,20 +1510,23 @@ |
1610 | 1511 | this.height = parseInt( this.width * ( aspect[1] / aspect[0] ) ); |
1611 | 1512 | } |
1612 | 1513 | } |
1613 | | - |
1614 | | - // On load sometimes attr is temporally -1 as we don't have video metadata yet. |
| 1514 | + |
| 1515 | + // On load sometimes attr is temporally -1 as we don't have video |
| 1516 | + // metadata yet. |
1615 | 1517 | // or in IE we get NaN for width height |
1616 | | - // |
1617 | | - // NOTE: browsers that do support height width should set "waitForMeta" flag in addElement |
| 1518 | + // |
| 1519 | + // NOTE: browsers that do support height width should set "waitForMeta" |
| 1520 | + // flag in addElement |
1618 | 1521 | if( ( isNaN( this.height ) && isNaN( this.width ) ) || |
1619 | | - ( this.height == -1 || this.width == -1 ) || |
| 1522 | + ( this.height == -1 || this.width == -1 ) || |
1620 | 1523 | // Check for firefox defaults |
1621 | | - // Note: ideally firefox would not do random guesses at css values |
| 1524 | + // Note: ideally firefox would not do random guesses at css |
| 1525 | + // values |
1622 | 1526 | ( (this.height == 150 || this.height == 64 ) && this.width == 300 ) |
1623 | 1527 | ) { |
1624 | 1528 | var defaultSize = mw.getConfig( 'EmbedPlayer.DefaultSize' ).split( 'x' ); |
1625 | 1529 | this.width = defaultSize[0]; |
1626 | | - |
| 1530 | + |
1627 | 1531 | // Special height default for audio tag ( if not set ) |
1628 | 1532 | if( element.tagName.toLowerCase() == 'audio' ) { |
1629 | 1533 | this.height = 0; |
— | — | @@ -1630,65 +1534,62 @@ |
1631 | 1535 | this.height = defaultSize[1]; |
1632 | 1536 | } |
1633 | 1537 | } |
| 1538 | + |
1634 | 1539 | }, |
1635 | 1540 | /** |
1636 | | - * Resize the player to a new size |
1637 | | - * @param {object} size The width height size of the player |
| 1541 | + * Resize the player to a new size preserving aspect ratio Wraps the |
| 1542 | + * controlBuilder.resizePlayer function |
1638 | 1543 | */ |
1639 | 1544 | resizePlayer: function( size , animate){ |
1640 | 1545 | mw.log("EmbedPlayer::resizePlayer:" + size.width + ' x ' + size.height ); |
1641 | | - this.width = size.width; |
1642 | | - this.height = size.height; |
1643 | | - var playerSize = {'width' : this.width, 'height' : this.height }; |
1644 | | - // check if height needs to include interface controls |
1645 | | - if( ! this.controlBuilder.checkOverlayControls() ){ |
1646 | | - size.height = size.height + this.controlBuilder.height; |
| 1546 | + |
| 1547 | + // Check if we are native display then resize the playerElement directly |
| 1548 | + if( this.useNativePlayerControls() ){ |
| 1549 | + if( animate ){ |
| 1550 | + $j( this.getPlayerElement() ).animate( size ); |
| 1551 | + } else { |
| 1552 | + $j( this.getPlayerElement() ).css( size ); |
| 1553 | + } |
| 1554 | + } else { |
| 1555 | + this.controlBuilder.resizePlayer( size, animate); |
1647 | 1556 | } |
1648 | | - |
1649 | | - if( animate ){ |
1650 | | - $j(this).animate(playerSize); |
1651 | | - this.$interface.animate( size ); |
1652 | | - }else{ |
1653 | | - $j(this).css(playerSize); |
1654 | | - this.$interface.css( size ); |
1655 | | - } |
1656 | 1557 | }, |
1657 | | - |
| 1558 | + |
1658 | 1559 | /** |
1659 | | - * Get the player pixel width not including controls |
1660 | | - * |
1661 | | - * @return {Number} pixel height of the video |
1662 | | - */ |
| 1560 | + * Get the player pixel width not including controls |
| 1561 | + * |
| 1562 | + * @return {Number} pixel height of the video |
| 1563 | + */ |
1663 | 1564 | getPlayerWidth: function() { |
1664 | 1565 | return $j( this ).width(); |
1665 | 1566 | }, |
1666 | | - |
| 1567 | + |
1667 | 1568 | /** |
1668 | | - * Get the player pixel height not including controls |
1669 | | - * |
1670 | | - * @return {Number} pixel height of the video |
1671 | | - */ |
| 1569 | + * Get the player pixel height not including controls |
| 1570 | + * |
| 1571 | + * @return {Number} pixel height of the video |
| 1572 | + */ |
1672 | 1573 | getPlayerHeight: function() { |
1673 | 1574 | return $j( this ).height(); |
1674 | 1575 | }, |
1675 | 1576 | |
1676 | 1577 | /** |
1677 | | - * Check player for sources. |
1678 | | - * If we need to get media sources form an external file |
1679 | | - * that request is issued here |
1680 | | - */ |
| 1578 | + * Check player for sources. If we need to get media sources form an |
| 1579 | + * external file that request is issued here |
| 1580 | + */ |
1681 | 1581 | checkPlayerSources: function() { |
1682 | 1582 | mw.log( 'EmbedPlayer::checkPlayerSources: ' + this.id ); |
1683 | 1583 | var _this = this; |
1684 | | - |
1685 | | - // Scope the end of check for player sources so it can be called in a callback |
| 1584 | + |
| 1585 | + // Scope the end of check for player sources so it can be called in a |
| 1586 | + // callback |
1686 | 1587 | var finishCheckPlayerSources = function(){ |
1687 | 1588 | // Run embedPlayer sources hook |
1688 | 1589 | mw.runTriggersCallback( _this, 'checkPlayerSourcesEvent', function(){ |
1689 | 1590 | _this.checkForTimedText(); |
1690 | 1591 | }); |
1691 | 1592 | }; |
1692 | | - |
| 1593 | + |
1693 | 1594 | // NOTE: Should could be moved to mediaWiki Api support module |
1694 | 1595 | // only load from api if sources are empty: |
1695 | 1596 | if ( _this.apiTitleKey && this.mediaElement.sources.length == 0) { |
— | — | @@ -1698,38 +1599,40 @@ |
1699 | 1600 | finishCheckPlayerSources(); |
1700 | 1601 | } ); |
1701 | 1602 | return ; |
1702 | | - } else { |
| 1603 | + } else { |
1703 | 1604 | finishCheckPlayerSources(); |
1704 | 1605 | } |
1705 | 1606 | }, |
1706 | | - |
| 1607 | + |
1707 | 1608 | /** |
1708 | 1609 | * Insert and play a video source ( useful for ads or bumper videos ) |
1709 | | - * |
1710 | | - * Only works while video is in active play back. |
1711 | | - * Only tested with native playback atm. |
| 1610 | + * |
| 1611 | + * Only works while video is in active play back. Only tested with native |
| 1612 | + * playback atm. |
1712 | 1613 | */ |
1713 | 1614 | insertAndPlaySource: function( source ){ |
1714 | 1615 | mw.log("Error: only native playback supports insertAndPlaySource right now"); |
1715 | 1616 | }, |
1716 | | - |
| 1617 | + |
1717 | 1618 | /** |
1718 | | - * Load Source video info from mediaWiki Api title key ( this.apiTitleKey ) |
1719 | | - * @@todo move this to mediaWiki 'api' module |
1720 | | - * @param {Function} callback Function called once loading is complete |
1721 | | - */ |
| 1619 | + * Load Source video info from mediaWiki Api title key ( this.apiTitleKey ) |
| 1620 | + * |
| 1621 | + * @@todo move this to mediaWiki 'api' module |
| 1622 | + * @param {Function} |
| 1623 | + * callback Function called once loading is complete |
| 1624 | + */ |
1722 | 1625 | loadSourceFromApi: function( callback ){ |
1723 | 1626 | var _this = this; |
1724 | 1627 | if( !_this.apiTitleKey ){ |
1725 | 1628 | mw.log( 'Error no apiTitleKey'); |
1726 | 1629 | return false; |
1727 | 1630 | } |
1728 | | - |
| 1631 | + |
1729 | 1632 | // Set local apiProvider via config if not defined |
1730 | 1633 | if( !_this.apiProvider ) { |
1731 | 1634 | _this.apiProvider = mw.getConfig( 'EmbedPlayer.ApiProvider' ); |
1732 | 1635 | } |
1733 | | - |
| 1636 | + |
1734 | 1637 | // Setup the request |
1735 | 1638 | var request = { |
1736 | 1639 | 'prop': 'imageinfo', |
— | — | @@ -1760,40 +1663,40 @@ |
1761 | 1664 | return ; |
1762 | 1665 | } |
1763 | 1666 | var imageinfo = page.imageinfo[0]; |
1764 | | - |
| 1667 | + |
1765 | 1668 | // Set the poster |
1766 | 1669 | _this.poster = imageinfo.thumburl; |
1767 | | - |
| 1670 | + |
1768 | 1671 | // Add the media src |
1769 | 1672 | _this.mediaElement.tryAddSource( |
1770 | 1673 | $j('<source />') |
1771 | 1674 | .attr( 'src', imageinfo.url ) |
1772 | 1675 | .get( 0 ) |
1773 | 1676 | ); |
1774 | | - |
| 1677 | + |
1775 | 1678 | // Set the duration |
1776 | 1679 | if( imageinfo.metadata[2]['name'] == 'length' ) { |
1777 | 1680 | _this.duration = imageinfo.metadata[2]['value']; |
1778 | 1681 | } |
1779 | | - |
| 1682 | + |
1780 | 1683 | // Set the width height |
1781 | 1684 | // Make sure we have an accurate aspect ratio |
1782 | 1685 | if( imageinfo.height != 0 && imageinfo.width != 0 ) { |
1783 | 1686 | _this.height = parseInt( _this.width * ( imageinfo.height / imageinfo.width ) ); |
1784 | 1687 | } |
1785 | | - |
| 1688 | + |
1786 | 1689 | // Update the css for the player interface |
1787 | 1690 | $j( _this ).css( 'height', _this.height); |
1788 | | - |
| 1691 | + |
1789 | 1692 | callback(); |
1790 | 1693 | }); |
1791 | 1694 | }, |
1792 | | - |
| 1695 | + |
1793 | 1696 | /** |
1794 | | - * Check if we should load the timedText interface or not. |
1795 | | - * |
1796 | | - * Note we check for text sources outside of |
1797 | | - */ |
| 1697 | + * Check if we should load the timedText interface or not. |
| 1698 | + * |
| 1699 | + * Note we check for text sources outside of |
| 1700 | + */ |
1798 | 1701 | isTimedTextSupported: function() { |
1799 | 1702 | // Check for timed text sources or api/ roe url |
1800 | 1703 | if ( ( this.roe || this.apiTitleKey || |
— | — | @@ -1803,11 +1706,10 @@ |
1804 | 1707 | return false; |
1805 | 1708 | } |
1806 | 1709 | }, |
1807 | | - |
| 1710 | + |
1808 | 1711 | /** |
1809 | | - * Check for timed Text support |
1810 | | - * and load necessary libraries |
1811 | | - */ |
| 1712 | + * Check for timed Text support and load necessary libraries |
| 1713 | + */ |
1812 | 1714 | checkForTimedText: function( ) { |
1813 | 1715 | var _this = this; |
1814 | 1716 | mw.log( 'EmbedPlayer::checkForTimedText: ' + _this.id ); |
— | — | @@ -1821,19 +1723,19 @@ |
1822 | 1724 | } |
1823 | 1725 | _this.setupSourcePlayer(); |
1824 | 1726 | }, |
1825 | | - |
| 1727 | + |
1826 | 1728 | /** |
1827 | | - * Set up the select source player |
1828 | | - * |
1829 | | - * issues autoSelectSource call |
1830 | | - * |
1831 | | - * Sets load error if no source is playable |
1832 | | - */ |
| 1729 | + * Set up the select source player |
| 1730 | + * |
| 1731 | + * issues autoSelectSource call |
| 1732 | + * |
| 1733 | + * Sets load error if no source is playable |
| 1734 | + */ |
1833 | 1735 | setupSourcePlayer: function() { |
1834 | | - mw.log("EmbedPlayer::setupSourcePlayer: " + this.id ); |
| 1736 | + mw.log("EmbedPlayer::setupSourcePlayer: " + this.id + ' sources: ' + this.mediaElement.sources.length ); |
1835 | 1737 | // Autoseletct the media source |
1836 | 1738 | this.mediaElement.autoSelectSource(); |
1837 | | - |
| 1739 | + |
1838 | 1740 | // Auto select player based on default order |
1839 | 1741 | if ( !this.mediaElement.selectedSource ) { |
1840 | 1742 | // check for parent clip: |
— | — | @@ -1848,39 +1750,25 @@ |
1849 | 1751 | } else { |
1850 | 1752 | this.selectedPlayer = mw.EmbedTypes.players.defaultPlayer( this.mediaElement.selectedSource.mimeType ); |
1851 | 1753 | } |
1852 | | - |
| 1754 | + |
1853 | 1755 | if ( this.selectedPlayer ) { |
1854 | 1756 | // Inherit the playback system of the selected player: |
1855 | 1757 | this.inheritEmbedPlayer(); |
1856 | 1758 | } else { |
1857 | | - // No source's playable |
1858 | | - var missingType = ''; |
1859 | | - var or = ''; |
1860 | | - for ( var i = 0; i < this.mediaElement.sources.length; i++ ) { |
1861 | | - missingType += or + this.mediaElement.sources[i].mimeType; |
1862 | | - or = ' or '; |
1863 | | - } |
1864 | | - // Get from parent playlist if set: |
1865 | | - if ( this.pc ){ |
1866 | | - missingType = this.pc.type; |
1867 | | - } |
1868 | | - |
1869 | | - mw.log( 'No player found for given source type ' + missingType ); |
1870 | | - this.showPluginMissingHTML( missingType ); |
1871 | | - |
1872 | | - // Call the global player manager to inform this video interface is "ready" for page callback to be proccessed. |
1873 | | - mw.playerManager.playerReady( this ); |
| 1759 | + this.showPluginMissingHTML(); |
1874 | 1760 | } |
1875 | 1761 | }, |
1876 | | - |
| 1762 | + |
1877 | 1763 | /** |
1878 | | - * Load and inherit methods from the selected player interface |
1879 | | - * |
1880 | | - * @param {Function} callback Function to be called once playback-system has been inherited |
1881 | | - */ |
| 1764 | + * Load and inherit methods from the selected player interface |
| 1765 | + * |
| 1766 | + * @param {Function} |
| 1767 | + * callback Function to be called once playback-system has been |
| 1768 | + * inherited |
| 1769 | + */ |
1882 | 1770 | inheritEmbedPlayer: function( callback ) { |
1883 | 1771 | mw.log( "EmbedPlayer::inheritEmbedPlayer:duration is: " + this.getDuration() + ' p: ' + this.id ); |
1884 | | - |
| 1772 | + |
1885 | 1773 | // Clear out any non-base embedObj methods: |
1886 | 1774 | if ( this.instanceOf ) { |
1887 | 1775 | eval( 'var tmpObj = mw.EmbedPlayer' + this.instanceOf ); |
— | — | @@ -1892,55 +1780,59 @@ |
1893 | 1781 | } |
1894 | 1782 | } |
1895 | 1783 | } |
1896 | | - |
| 1784 | + |
1897 | 1785 | // Set up the new embedObj |
1898 | 1786 | mw.log( 'EmbedPlayer::inheritEmbedPlayer: embedding with ' + this.selectedPlayer.library ); |
1899 | 1787 | var _this = this; |
1900 | | - |
| 1788 | + |
1901 | 1789 | // Load the selected player |
1902 | 1790 | this.selectedPlayer.load( function() { |
1903 | 1791 | mw.log( 'EmbedPlayer::inheritEmbedPlayer ' + _this.selectedPlayer.library + " player loaded for " + _this.id ); |
1904 | | - |
| 1792 | + |
1905 | 1793 | // Get embed library player Interface |
1906 | 1794 | var playerInterface = mw[ 'EmbedPlayer' + _this.selectedPlayer.library ]; |
1907 | | - |
1908 | | - for ( var method in playerInterface ) { |
| 1795 | + |
| 1796 | + for ( var method in playerInterface ) { |
1909 | 1797 | if ( _this[method] && !_this['parent_' + method] ) { |
1910 | 1798 | _this['parent_' + method] = _this[method]; |
1911 | 1799 | } |
1912 | 1800 | _this[ method ] = playerInterface[method]; |
1913 | 1801 | } |
1914 | | - |
| 1802 | + |
1915 | 1803 | // Update feature support |
1916 | 1804 | _this.updateFeatureSupport(); |
1917 | | - |
| 1805 | + |
1918 | 1806 | _this.getDuration(); |
1919 | | - |
| 1807 | + |
1920 | 1808 | _this.showPlayer(); |
1921 | | - // Call the global player manager to inform this video interface is ready: |
| 1809 | + |
| 1810 | + // Call the global player manager to inform this video interface is |
| 1811 | + // ready: |
1922 | 1812 | mw.playerManager.playerReady( _this ); |
1923 | | - |
| 1813 | + |
1924 | 1814 | // Run the callback if provided |
1925 | 1815 | if ( typeof callback == 'function' ){ |
1926 | 1816 | callback(); |
1927 | 1817 | } |
1928 | 1818 | } ); |
1929 | 1819 | }, |
1930 | | - |
| 1820 | + |
1931 | 1821 | /** |
1932 | | - * Select a player playback system |
1933 | | - * |
1934 | | - * @param {Object} player Player playback system to be selected |
1935 | | - * player playback system include vlc, native, java etc. |
1936 | | - */ |
| 1822 | + * Select a player playback system |
| 1823 | + * |
| 1824 | + * @param {Object} |
| 1825 | + * player Player playback system to be selected player playback |
| 1826 | + * system include vlc, native, java etc. |
| 1827 | + */ |
1937 | 1828 | selectPlayer: function( player ) { |
1938 | 1829 | var _this = this; |
1939 | 1830 | if ( this.selectedPlayer.id != player.id ) { |
1940 | 1831 | this.selectedPlayer = player; |
1941 | 1832 | this.inheritEmbedPlayer( function(){ |
1942 | 1833 | // Hide / remove track container |
1943 | | - _this.$interface.find( '.track' ).remove(); |
1944 | | - // We have to re-bind hoverIntent ( has to happen in this scope ) |
| 1834 | + _this.$interface.find( '.track' ).remove(); |
| 1835 | + // We have to re-bind hoverIntent ( has to happen in this scope |
| 1836 | + // ) |
1945 | 1837 | if( _this.controls && _this.controlBuilder.checkOverlayControls() ){ |
1946 | 1838 | _this.controlBuilder.showControlBar(); |
1947 | 1839 | _this.$interface.hoverIntent({ |
— | — | @@ -1952,17 +1844,17 @@ |
1953 | 1845 | 'out' : function(){ |
1954 | 1846 | _this.controlBuilder.hideControlBar(); |
1955 | 1847 | } |
1956 | | - }); |
| 1848 | + }) |
1957 | 1849 | } |
1958 | 1850 | }); |
1959 | 1851 | } |
1960 | 1852 | }, |
1961 | | - |
| 1853 | + |
1962 | 1854 | /** |
1963 | | - * Get a time range from the media start and end time |
1964 | | - * |
1965 | | - * @return start_npt and end_npt time if present |
1966 | | - */ |
| 1855 | + * Get a time range from the media start and end time |
| 1856 | + * |
| 1857 | + * @return start_npt and end_npt time if present |
| 1858 | + */ |
1967 | 1859 | getTimeRange: function() { |
1968 | 1860 | var end_time = (this.controlBuilder.longTimeDisp)? '/' + mw.seconds2npt( this.getDuration() ) : ''; |
1969 | 1861 | var default_time_range = '0:00:00' + end_time; |
— | — | @@ -1974,53 +1866,55 @@ |
1975 | 1867 | return default_time_range; |
1976 | 1868 | return this.mediaElement.selectedSource.start_npt + this.mediaElement.selectedSource.end_npt; |
1977 | 1869 | }, |
1978 | | - |
| 1870 | + |
1979 | 1871 | /** |
1980 | | - * Get the duration of the embed player |
1981 | | - */ |
| 1872 | + * Get the duration of the embed player |
| 1873 | + */ |
1982 | 1874 | getDuration: function() { |
1983 | 1875 | return this.duration; |
1984 | 1876 | }, |
1985 | | - |
| 1877 | + |
1986 | 1878 | /** |
1987 | | - * Get the player height |
1988 | | - */ |
| 1879 | + * Get the player height |
| 1880 | + */ |
1989 | 1881 | getHeight: function() { |
1990 | 1882 | return this.height; |
1991 | 1883 | }, |
1992 | | - |
| 1884 | + |
1993 | 1885 | /** |
1994 | | - * Get the player width |
1995 | | - */ |
| 1886 | + * Get the player width |
| 1887 | + */ |
1996 | 1888 | getWidth: function(){ |
1997 | 1889 | return this.width; |
1998 | 1890 | }, |
1999 | | - |
| 1891 | + |
2000 | 1892 | /** |
2001 | | - * Check if the selected source is an audio element: |
2002 | | - */ |
| 1893 | + * Check if the selected source is an audio element: |
| 1894 | + */ |
2003 | 1895 | isAudio: function(){ |
2004 | 1896 | return ( this.mediaElement.selectedSource.mimeType.indexOf('audio/') !== -1 ); |
2005 | 1897 | }, |
2006 | | - |
| 1898 | + |
2007 | 1899 | /** |
2008 | | - * Get the plugin embed html ( should be implemented by embed player interface ) |
2009 | | - */ |
| 1900 | + * Get the plugin embed html ( should be implemented by embed player |
| 1901 | + * interface ) |
| 1902 | + */ |
2010 | 1903 | doEmbedHTML: function() { |
2011 | 1904 | return 'Error: function doEmbedHTML should be implemented by embed player interface '; |
2012 | 1905 | }, |
2013 | | - |
| 1906 | + |
2014 | 1907 | /** |
2015 | | - * Seek function ( should be implemented by embedPlayer interface playerNative, playerKplayer etc. ) |
2016 | | - * embedPlayer doSeek only handles URL time seeks |
2017 | | - */ |
| 1908 | + * Seek function ( should be implemented by embedPlayer interface |
| 1909 | + * playerNative, playerKplayer etc. ) embedPlayer doSeek only handles URL |
| 1910 | + * time seeks |
| 1911 | + */ |
2018 | 1912 | doSeek: function( percent ) { |
2019 | 1913 | var _this = this; |
2020 | | - |
| 1914 | + |
2021 | 1915 | this.seeking = true; |
2022 | 1916 | // Run the seeking hook |
2023 | 1917 | $j( this.embedPlayer ).trigger( 'onSeek' ); |
2024 | | - |
| 1918 | + |
2025 | 1919 | // See if we should do a server side seek ( player independent ) |
2026 | 1920 | if ( this.supportsURLTimeEncoding() ) { |
2027 | 1921 | mw.log( 'EmbedPlayer::doSeek:: updated serverSeekTime: ' + mw.seconds2npt ( this.serverSeekTime ) ); |
— | — | @@ -2031,137 +1925,148 @@ |
2032 | 1926 | // Update the slider |
2033 | 1927 | this.updatePlayHead( percent ); |
2034 | 1928 | } |
2035 | | - |
2036 | | - // Do play request in 100ms ( give the dom time to swap out the embed player ) |
| 1929 | + |
| 1930 | + // Do play request in 100ms ( give the dom time to swap out the embed |
| 1931 | + // player ) |
2037 | 1932 | setTimeout( function() { |
2038 | 1933 | _this.seeking = false; |
2039 | | - _this.play(); |
| 1934 | + _this.play() |
2040 | 1935 | _this.monitor(); |
2041 | 1936 | }, 100 ); |
2042 | | - |
| 1937 | + |
2043 | 1938 | // Run the onSeeking interface update |
2044 | 1939 | // NOTE controlBuilder should really bind to html5 events rather |
2045 | 1940 | // than explicitly calling it or inheriting stuff. |
2046 | | - this.controlBuilder.onSeek(); |
| 1941 | + this.controlBuilder.onSeek(); |
2047 | 1942 | }, |
2048 | | - |
| 1943 | + |
2049 | 1944 | /** |
2050 | | - * Seeks to the requested time and issues a callback when ready |
2051 | | - * (should be overwritten by client that supports frame serving) |
| 1945 | + * Seeks to the requested time and issues a callback when ready (should be |
| 1946 | + * overwritten by client that supports frame serving) |
2052 | 1947 | */ |
2053 | 1948 | setCurrentTime: function( time, callback ) { |
2054 | 1949 | mw.log( 'Error: base embed setCurrentTime can not frame serve (override via plugin)' ); |
2055 | 1950 | }, |
2056 | | - |
| 1951 | + |
2057 | 1952 | /** |
2058 | | - * On clip done action. Called once a clip is done playing |
2059 | | - */ |
| 1953 | + * On clip done action. Called once a clip is done playing |
| 1954 | + */ |
2060 | 1955 | onClipDone: function() { |
2061 | 1956 | mw.log( 'EmbedPlayer::onClipDone:' + this.id + ' doneCount:' + this.donePlayingCount + ' stop state:' +this.isStopped() ); |
2062 | 1957 | var _this = this; |
2063 | | - |
| 1958 | + |
2064 | 1959 | // Only run stopped once: |
2065 | 1960 | if( !this.isStopped() ){ |
2066 | 1961 | // Stop the monitor: |
2067 | 1962 | this.stopMonitor(); |
2068 | | - |
| 1963 | + |
2069 | 1964 | // Show the control bar: |
2070 | 1965 | this.controlBuilder.showControlBar(); |
2071 | | - |
| 1966 | + |
2072 | 1967 | // Update the clip done playing count: |
2073 | 1968 | this.donePlayingCount ++; |
2074 | | - |
| 1969 | + |
2075 | 1970 | // Fire the html5 ended binding |
2076 | 1971 | var onDoneActionObject = { |
2077 | 1972 | 'runBaseControlDone' : true |
2078 | | - }; |
2079 | | - |
2080 | | - // Run the ended trigger ( allow the ended object to prevent default actions ) |
| 1973 | + } |
| 1974 | + |
| 1975 | + // Run the ended trigger ( allow the ended object to prevent default |
| 1976 | + // actions ) |
2081 | 1977 | mw.log("EmbedPlayer::onClipDone:Trigger ended"); |
2082 | 1978 | $j( this ).trigger( 'ended', onDoneActionObject ); |
2083 | | - |
| 1979 | + |
2084 | 1980 | if( onDoneActionObject.runBaseControlDone ){ |
2085 | | - |
| 1981 | + |
2086 | 1982 | // Check if we have the "loop" property set |
2087 | 1983 | if( this.loop ) { |
2088 | 1984 | this.stop(); |
2089 | 1985 | this.play(); |
2090 | | - return; |
| 1986 | + return; |
2091 | 1987 | } |
2092 | | - |
| 1988 | + |
2093 | 1989 | // Stop the clip (load the thumbnail etc) |
2094 | 1990 | this.stop(); |
2095 | 1991 | this.serverSeekTime = 0; |
2096 | 1992 | this.updatePlayHead( 0 ); |
2097 | | - |
2098 | | - // Make sure we are not in preview mode( no end clip actions in preview mode) |
| 1993 | + |
| 1994 | + // Make sure we are not in preview mode( no end clip actions in |
| 1995 | + // preview mode) |
2099 | 1996 | if ( this.preview_mode ) { |
2100 | 1997 | return ; |
2101 | 1998 | } |
2102 | | - |
| 1999 | + |
2103 | 2000 | // Do the controlBuilder onClip done interface |
2104 | 2001 | this.controlBuilder.onClipDone(); |
2105 | 2002 | } |
2106 | 2003 | } |
| 2004 | + |
2107 | 2005 | }, |
2108 | | - |
2109 | | - |
| 2006 | + |
| 2007 | + |
2110 | 2008 | /** |
2111 | | - * Shows the video Thumbnail, updates pause state |
2112 | | - */ |
| 2009 | + * Shows the video Thumbnail, updates pause state |
| 2010 | + */ |
2113 | 2011 | showThumbnail: function() { |
2114 | 2012 | var _this = this; |
2115 | 2013 | mw.log( 'EmbedPlayer::showThumbnail' + this.thumbnail_disp ); |
2116 | | - |
| 2014 | + |
2117 | 2015 | // Close Menu Overlay: |
2118 | 2016 | this.controlBuilder.closeMenuOverlay(); |
2119 | | - |
| 2017 | + |
2120 | 2018 | // update the thumbnail html: |
2121 | 2019 | this.updatePosterHTML(); |
2122 | | - |
| 2020 | + |
2123 | 2021 | this.paused = true; |
2124 | 2022 | this.thumbnail_disp = true; |
2125 | 2023 | // Make sure the controlBuilder bindings are up-to-date |
2126 | 2024 | this.controlBuilder.addControlBindings(); |
2127 | | - |
2128 | | - // Once the thumbnail is shown run the mediaReady trigger (if not using native controls) |
| 2025 | + |
| 2026 | + // Once the thumbnail is shown run the mediaReady trigger (if not using |
| 2027 | + // native controls) |
2129 | 2028 | if( !this.useNativePlayerControls() ){ |
2130 | 2029 | mw.log("mediaLoaded"); |
2131 | 2030 | $j( this ).trigger( 'mediaLoaded' ); |
2132 | 2031 | } |
2133 | 2032 | }, |
2134 | | - |
| 2033 | + |
2135 | 2034 | /** |
2136 | | - * Show the player |
2137 | | - */ |
| 2035 | + * Show the player |
| 2036 | + */ |
2138 | 2037 | showPlayer : function () { |
2139 | 2038 | mw.log( 'EmbedPlayer:: Show player: ' + this.id + ' interace: w:' + this.width + ' h:' + this.height); |
2140 | 2039 | var _this = this; |
2141 | 2040 | // Set-up the local controlBuilder instance: |
2142 | 2041 | this.controlBuilder = new mw.PlayerControlBuilder( this ); |
2143 | 2042 | var _this = this; |
2144 | | - |
| 2043 | + |
2145 | 2044 | // Make sure we have mwplayer_interface |
2146 | 2045 | if( $j( this ).parent( '.mwplayer_interface' ).length == 0 ) { |
2147 | 2046 | // Select "player" |
2148 | 2047 | $j( this ) |
2149 | | - .wrap( |
| 2048 | + .wrap( |
2150 | 2049 | $j('<div>') |
2151 | 2050 | .addClass( 'mwplayer_interface ' + this.controlBuilder.playerClass ) |
2152 | 2051 | .css({ |
2153 | | - 'width' : this.width + 'px', |
2154 | | - 'height' : this.height + 'px', |
2155 | | - 'position' : 'relative' |
| 2052 | + 'width' : this.width, |
| 2053 | + 'height' : this.height, |
| 2054 | + 'position' : 'relative', |
| 2055 | + 'background' : '#000' |
2156 | 2056 | }) |
2157 | | - ); |
| 2057 | + ) |
| 2058 | + // position the "player" absolute inside the relative interface |
| 2059 | + // parent: |
| 2060 | + .css('position', 'absolute'); |
2158 | 2061 | } |
2159 | 2062 | |
2160 | | - //Set up local jQuery object reference to "mwplayer_interface" |
| 2063 | + |
| 2064 | + |
| 2065 | + // Set up local jQuery object reference to "mwplayer_interface" |
2161 | 2066 | this.$interface = $j( this ).parent( '.mwplayer_interface' ); |
2162 | | - |
| 2067 | + |
2163 | 2068 | // Update Thumbnail for the "player" |
2164 | 2069 | this.updatePosterHTML(); |
2165 | | - |
| 2070 | + |
2166 | 2071 | // Add controls if enabled: |
2167 | 2072 | if ( this.controls ) { |
2168 | 2073 | this.controlBuilder.addControls(); |
— | — | @@ -2170,35 +2075,44 @@ |
2171 | 2076 | // Interface is hidden if controls are "off" |
2172 | 2077 | this.$interface.hide(); |
2173 | 2078 | } |
2174 | | - |
| 2079 | + |
2175 | 2080 | if ( this.autoplay ) { |
2176 | 2081 | mw.log( 'EmbedPlayer::showPlayer::activating autoplay' ); |
2177 | 2082 | // Issue a non-blocking play request |
2178 | 2083 | setTimeout(function(){ |
2179 | 2084 | _this.play(); |
2180 | | - },0); |
| 2085 | + },0) |
2181 | 2086 | } |
2182 | | - |
| 2087 | + |
2183 | 2088 | }, |
2184 | | - |
| 2089 | + |
2185 | 2090 | /** |
2186 | | - * Get missing plugin html (check for user included code) |
2187 | | - * @param {String} [misssingType] missing type mime |
2188 | | - */ |
2189 | | - showPluginMissingHTML: function( misssingType ) { |
| 2091 | + * Get missing plugin html (check for user included code) |
| 2092 | + * |
| 2093 | + * @param {String} |
| 2094 | + * [misssingType] missing type mime |
| 2095 | + */ |
| 2096 | + showPluginMissingHTML: function( ) { |
| 2097 | + // Get mime type for unsuported formats: |
| 2098 | + var missingType = ''; |
| 2099 | + var or = ''; |
| 2100 | + for ( var i = 0; i < this.mediaElement.sources.length; i++ ) { |
| 2101 | + missingType += or + this.mediaElement.sources[i].mimeType; |
| 2102 | + or = ' or '; |
| 2103 | + } |
2190 | 2104 | // Remove the loading spinner if present: |
2191 | 2105 | $j('.playerLoadingSpinner').remove(); |
2192 | | - |
| 2106 | + |
2193 | 2107 | // If the native video is already displayed hide it: |
2194 | 2108 | if( $j( '#' + this.pid ).length != 0 ){ |
2195 | 2109 | $j('#loadingSpinner_' + this.id ).remove(); |
2196 | | - $j( '#' + this.pid ).hide(); |
| 2110 | + $j( '#' + this.pid ).hide() |
2197 | 2111 | } |
2198 | 2112 | if( this.mediaElement.sources.length == 0 ){ |
2199 | | - // Hide the pid if present: |
| 2113 | + // hide the pid if present: |
2200 | 2114 | $j( '#pid_' + this.id ).hide(); |
2201 | 2115 | $j( this ).show().html( |
2202 | | - $j('<span />').text( |
| 2116 | + $j('<span />').text( |
2203 | 2117 | gM('mwe-embedplayer-missing-source') |
2204 | 2118 | ) |
2205 | 2119 | ); |
— | — | @@ -2207,53 +2121,53 @@ |
2208 | 2122 | var source = this.mediaElement.sources[0]; |
2209 | 2123 | // Check if we have user defined missing html msg: |
2210 | 2124 | if ( this.user_missing_plugin_html ) { |
2211 | | - $j( this ).html( this.user_missing_plugin_html ); |
2212 | | - } else { |
2213 | | - if ( !misssingType ){ |
2214 | | - misssingType = ''; |
2215 | | - } |
2216 | | - $j( this ).html( |
2217 | | - $j('<div />').append( |
2218 | | - gM( 'mwe-embedplayer-generic_missing_plugin', misssingType ), |
2219 | | - $j( '<br />' ), |
2220 | | - $j( '<a />' ) |
2221 | | - .attr( { |
2222 | | - 'title' : gM( 'mwe-embedplayer-download_clip' ), |
2223 | | - 'href' : source.src |
2224 | | - }) |
2225 | | - .text( gM( 'mwe-embedplayer-download_clip' ) ) |
2226 | | - ) |
2227 | | - ); |
| 2125 | + $j( this ).html( this.user_missing_plugin_html ); |
| 2126 | + } else { |
| 2127 | + $j( this ).html( |
| 2128 | + $j('<div />').append( |
| 2129 | + gM( 'mwe-embedplayer-generic_missing_plugin', missingType ), |
| 2130 | + $j( '<br />' ), |
| 2131 | + $j( '<a />' ) |
| 2132 | + .attr( { |
| 2133 | + 'title' : gM( 'mwe-embedplayer-download_clip' ), |
| 2134 | + 'href' : source.src |
| 2135 | + }) |
| 2136 | + .text( gM( 'mwe-embedplayer-download_clip' ) ) |
| 2137 | + ) |
| 2138 | + ); |
2228 | 2139 | } |
2229 | 2140 | // hide |
2230 | 2141 | }, |
2231 | | - |
| 2142 | + |
2232 | 2143 | /** |
2233 | | - * Update the video time request via a time request string |
2234 | | - * @param {String} time_req |
2235 | | - */ |
| 2144 | + * Update the video time request via a time request string |
| 2145 | + * |
| 2146 | + * @param {String} |
| 2147 | + * time_req |
| 2148 | + */ |
2236 | 2149 | updateVideoTimeReq: function( time_req ) { |
2237 | 2150 | mw.log( 'EmbedPlayer::updateVideoTimeReq:' + time_req ); |
2238 | 2151 | var time_parts = time_req.split( '/' ); |
2239 | 2152 | this.updateVideoTime( time_parts[0], time_parts[1] ); |
2240 | 2153 | }, |
2241 | | - |
| 2154 | + |
2242 | 2155 | /** |
2243 | | - * Update Video time from provided start_npt and end_npt values |
2244 | | - * |
2245 | | - * @param {String} start_npt the new start time in npt format |
2246 | | - * @pamra {String} end_npt the new end time in npt format |
2247 | | - */ |
| 2156 | + * Update Video time from provided start_npt and end_npt values |
| 2157 | + * |
| 2158 | + * @param {String} |
| 2159 | + * start_npt the new start time in npt format |
| 2160 | + * @pamra {String} end_npt the new end time in npt format |
| 2161 | + */ |
2248 | 2162 | updateVideoTime: function( start_npt, end_npt ) { |
2249 | 2163 | // update media |
2250 | 2164 | this.mediaElement.updateSourceTimes( start_npt, end_npt ); |
2251 | | - |
| 2165 | + |
2252 | 2166 | // update mv_time |
2253 | 2167 | this.controlBuilder.setStatus( start_npt + '/' + end_npt ); |
2254 | | - |
| 2168 | + |
2255 | 2169 | // reset slider |
2256 | 2170 | this.updatePlayHead( 0 ); |
2257 | | - |
| 2171 | + |
2258 | 2172 | // reset seek_offset: |
2259 | 2173 | if ( this.mediaElement.selectedSource.URLTimeEncoding ) { |
2260 | 2174 | this.serverSeekTime = 0; |
— | — | @@ -2262,19 +2176,23 @@ |
2263 | 2177 | } |
2264 | 2178 | }, |
2265 | 2179 | |
2266 | | - |
| 2180 | + |
2267 | 2181 | /** |
2268 | | - * Update Thumb time with npt formated time |
2269 | | - * @param {String} time NPT formated time to update thumbnail |
2270 | | - */ |
| 2182 | + * Update Thumb time with npt formated time |
| 2183 | + * |
| 2184 | + * @param {String} |
| 2185 | + * time NPT formated time to update thumbnail |
| 2186 | + */ |
2271 | 2187 | updateThumbTimeNPT: function( time ) { |
2272 | 2188 | this.updateThumbTime( mw.npt2seconds( time ) - parseInt( this.startOffset ) ); |
2273 | 2189 | }, |
2274 | | - |
| 2190 | + |
2275 | 2191 | /** |
2276 | | - * Update the thumb with a new time |
2277 | | - * @param {Float} floatSeconds Time to update the thumb to |
2278 | | - */ |
| 2192 | + * Update the thumb with a new time |
| 2193 | + * |
| 2194 | + * @param {Float} |
| 2195 | + * floatSeconds Time to update the thumb to |
| 2196 | + */ |
2279 | 2197 | updateThumbTime:function( floatSeconds ) { |
2280 | 2198 | // mw.log('updateThumbTime:'+floatSeconds); |
2281 | 2199 | var _this = this; |
— | — | @@ -2283,8 +2201,8 @@ |
2284 | 2202 | } |
2285 | 2203 | if ( this.org_thum_src.indexOf( 't=' ) !== -1 ) { |
2286 | 2204 | this.last_thumb_url = mw.replaceUrlParams( this.org_thum_src, |
2287 | | - { |
2288 | | - 't' : mw.seconds2npt( floatSeconds + parseInt( this.startOffset ) ) |
| 2205 | + { |
| 2206 | + 't' : mw.seconds2npt( floatSeconds + parseInt( this.startOffset ) ) |
2289 | 2207 | } |
2290 | 2208 | ); |
2291 | 2209 | if ( !this.thumbnail_updating ) { |
— | — | @@ -2293,23 +2211,26 @@ |
2294 | 2212 | } |
2295 | 2213 | } |
2296 | 2214 | }, |
2297 | | - |
| 2215 | + |
2298 | 2216 | /** |
2299 | | - * Updates the displayed thumbnail via percent of the stream |
2300 | | - * @param {Float} percent Percent of duration to update thumb |
2301 | | - */ |
| 2217 | + * Updates the displayed thumbnail via percent of the stream |
| 2218 | + * |
| 2219 | + * @param {Float} |
| 2220 | + * percent Percent of duration to update thumb |
| 2221 | + */ |
2302 | 2222 | updateThumbPerc:function( percent ) { |
2303 | 2223 | return this.updateThumbTime( ( this.getDuration() * percent ) ); |
2304 | 2224 | }, |
2305 | | - |
| 2225 | + |
2306 | 2226 | /** |
2307 | | - * Updates the thumbnail if the thumbnail is being displayed |
2308 | | - * |
2309 | | - * @param {String} src New src of thumbnail |
2310 | | - * @param {Boolean} quick_switch |
2311 | | - * true switch happens instantly |
2312 | | - * false / undefined animated cross fade |
2313 | | - */ |
| 2227 | + * Updates the thumbnail if the thumbnail is being displayed |
| 2228 | + * |
| 2229 | + * @param {String} |
| 2230 | + * src New src of thumbnail |
| 2231 | + * @param {Boolean} |
| 2232 | + * quick_switch true switch happens instantly false / undefined |
| 2233 | + * animated cross fade |
| 2234 | + */ |
2314 | 2235 | updatePosterSrc: function( src, quick_switch ) { |
2315 | 2236 | // make sure we don't go to the same url if we are not already updating: |
2316 | 2237 | if ( !this.thumbnail_updating && $j( '#img_thumb_' + this.id ).attr( 'src' ) == src ) |
— | — | @@ -2317,9 +2238,9 @@ |
2318 | 2239 | // if we are already updating don't issue a new update: |
2319 | 2240 | if ( this.thumbnail_updating && $j( '#new_img_thumb_' + this.id ).attr( 'src' ) == src ) |
2320 | 2241 | return false; |
2321 | | - |
| 2242 | + |
2322 | 2243 | mw.log( 'update thumb: ' + src ); |
2323 | | - |
| 2244 | + |
2324 | 2245 | if ( quick_switch ) { |
2325 | 2246 | $j( '#img_thumb_' + this.id ).attr( 'src', src ); |
2326 | 2247 | } else { |
— | — | @@ -2327,11 +2248,11 @@ |
2328 | 2249 | // if still animating remove new_img_thumb_ |
2329 | 2250 | if ( this.thumbnail_updating == true ) |
2330 | 2251 | $j( '#new_img_thumb_' + this.id ).stop().remove(); |
2331 | | - |
| 2252 | + |
2332 | 2253 | if ( this.thumbnail_disp ) { |
2333 | 2254 | mw.log( 'set to thumb:' + src ); |
2334 | 2255 | this.thumbnail_updating = true; |
2335 | | - $j( this ).append( |
| 2256 | + $j( this ).append( |
2336 | 2257 | $j('<img />') |
2337 | 2258 | .attr({ |
2338 | 2259 | 'src' : src, |
— | — | @@ -2342,7 +2263,7 @@ |
2343 | 2264 | .css( { |
2344 | 2265 | 'display' : 'none', |
2345 | 2266 | 'position' : 'absolute', |
2346 | | - 'z-index' : 2, |
| 2267 | + 'zindex' : 2, |
2347 | 2268 | 'top' : '0px', |
2348 | 2269 | 'left' : '0px' |
2349 | 2270 | }) |
— | — | @@ -2352,9 +2273,10 @@ |
2353 | 2274 | // once faded in remove org and rename new: |
2354 | 2275 | $j( '#img_thumb_' + _this.id ).remove(); |
2355 | 2276 | $j( '#new_img_thumb_' + _this.id ).attr( 'id', 'img_thumb_' + _this.id ); |
2356 | | - $j( '#img_thumb_' + _this.id ).css( 'z-index', '1' ); |
| 2277 | + $j( '#img_thumb_' + _this.id ).css( 'zindex', '1' ); |
2357 | 2278 | _this.thumbnail_updating = false; |
2358 | | - // mw.log("done fadding in "+ $j('#img_thumb_'+_this.id).attr("src")); |
| 2279 | + // mw.log("done fadding in "+ |
| 2280 | + // $j('#img_thumb_'+_this.id).attr("src")); |
2359 | 2281 | |
2360 | 2282 | // if we have a thumb queued update to that |
2361 | 2283 | if ( _this.last_thumb_url ) { |
— | — | @@ -2366,30 +2288,30 @@ |
2367 | 2289 | } |
2368 | 2290 | } |
2369 | 2291 | }, |
2370 | | - |
| 2292 | + |
2371 | 2293 | /** |
2372 | | - * Returns the HTML code for the video when it is in thumbnail mode. |
2373 | | - * playing, configuring the player, inline cmml display, HTML linkback, |
2374 | | - * download, and embed code. |
2375 | | - */ |
| 2294 | + * Returns the HTML code for the video when it is in thumbnail mode. |
| 2295 | + * playing, configuring the player, inline cmml display, HTML linkback, |
| 2296 | + * download, and embed code. |
| 2297 | + */ |
2376 | 2298 | updatePosterHTML: function () { |
2377 | 2299 | mw.log( 'EmbedPlayer:updatePosterHTML::' + this.id ); |
2378 | 2300 | var thumb_html = ''; |
2379 | 2301 | var class_atr = ''; |
2380 | 2302 | var style_atr = ''; |
2381 | | - |
2382 | | - |
| 2303 | + |
| 2304 | + |
2383 | 2305 | if( this.useNativePlayerControls() ){ |
2384 | 2306 | this.showNativePlayer(); |
2385 | 2307 | return ; |
2386 | 2308 | } |
2387 | | - |
| 2309 | + |
2388 | 2310 | // Set by default thumb value if not found |
2389 | | - var posterSrc = ( this.poster ) ? this.poster : |
| 2311 | + var posterSrc = ( this.poster ) ? this.poster : |
2390 | 2312 | mw.getConfig( 'imagesPath' ) + 'vid_default_thumb.jpg'; |
2391 | | - |
| 2313 | + |
2392 | 2314 | // Poster support is not very consistent in browsers |
2393 | | - // use a jpeg poster image: |
| 2315 | + // use a jpg poster image: |
2394 | 2316 | $j( this ).html( |
2395 | 2317 | $j( '<img />' ) |
2396 | 2318 | .css({ |
— | — | @@ -2403,59 +2325,60 @@ |
2404 | 2326 | }) |
2405 | 2327 | .addClass( 'playerPoster' ) |
2406 | 2328 | ); |
2407 | | - |
2408 | | - if ( this.controls |
2409 | | - && this.height > this.controlBuilder.getComponentHeight( 'playButtonLarge' ) |
| 2329 | + |
| 2330 | + if ( this.controls |
| 2331 | + && this.height > this.controlBuilder.getComponentHeight( 'playButtonLarge' ) |
2410 | 2332 | ) { |
2411 | 2333 | $j( this ).append( |
2412 | 2334 | this.controlBuilder.getComponent( 'playButtonLarge' ) |
2413 | 2335 | ); |
2414 | 2336 | } |
2415 | 2337 | }, |
2416 | | - |
| 2338 | + |
2417 | 2339 | /** |
2418 | 2340 | * Checks if native controls should be used |
2419 | | - * |
2420 | | - * @param [player] Object Optional player object to check controls attribute |
| 2341 | + * |
| 2342 | + * @param [player] |
| 2343 | + * Object Optional player object to check controls attribute |
2421 | 2344 | * @returns boolean true if the mwEmbed player interface should be used |
2422 | | - * false if the mwEmbed player interface should not be used |
| 2345 | + * false if the mwEmbed player interface should not be used |
2423 | 2346 | */ |
2424 | 2347 | useNativePlayerControls: function() { |
2425 | 2348 | if( this.usenativecontrols === true ){ |
2426 | 2349 | return true; |
2427 | 2350 | } |
2428 | | - |
| 2351 | + |
2429 | 2352 | if( mw.getConfig('EmbedPlayer.NativeControls') === true ) { |
2430 | 2353 | return true; |
2431 | 2354 | } |
2432 | 2355 | if( mw.getConfig('EmbedPlayer.NativeControlsMobileSafari' ) && |
2433 | | - mw.isMobileHTML5() |
| 2356 | + mw.isHTML5FallForwardNative() |
2434 | 2357 | ){ |
2435 | 2358 | return true; |
2436 | | - } |
| 2359 | + } |
2437 | 2360 | return false; |
2438 | 2361 | }, |
2439 | | - |
2440 | | - |
| 2362 | + |
| 2363 | + |
2441 | 2364 | /** |
2442 | 2365 | * Show the native player embed code |
2443 | | - * |
| 2366 | + * |
2444 | 2367 | * This is for cases where the main library needs to "get out of the way" |
2445 | | - * since the device only supports a limited subset of the html5 and |
2446 | | - * won't work with an html javascirpt interface |
| 2368 | + * since the device only supports a limited subset of the html5 and won't |
| 2369 | + * work with an html javascirpt interface |
2447 | 2370 | */ |
2448 | 2371 | showNativePlayer: function(){ |
2449 | 2372 | var _this = this; |
2450 | 2373 | // Empty the player |
2451 | 2374 | $j(this).empty(); |
2452 | | - |
| 2375 | + |
2453 | 2376 | // Remove the player loader spinner if it exists |
2454 | 2377 | $j('#loadingSpinner_' + this.id ).remove(); |
2455 | | - |
2456 | | - |
| 2378 | + |
| 2379 | + |
2457 | 2380 | // Check if we need to refresh mobile safari |
2458 | 2381 | var mobileSafariNeedsRefresh = false; |
2459 | | - |
| 2382 | + |
2460 | 2383 | // Unhide the original video element if not part of a playerThemer embed |
2461 | 2384 | if( !$j( '#' + this.pid ).hasClass('PlayerThemer') ){ |
2462 | 2385 | $j( '#' + this.pid ) |
— | — | @@ -2464,27 +2387,28 @@ |
2465 | 2388 | } ) |
2466 | 2389 | .show() |
2467 | 2390 | .attr('controls', 'true'); |
2468 | | - |
| 2391 | + |
2469 | 2392 | mobileSafariNeedsRefresh = true; |
2470 | 2393 | } |
2471 | | - |
| 2394 | + |
2472 | 2395 | // iPad does not handle video tag update for attributes like "controls" |
2473 | | - // so we have to do a full replace ( if controls are not included initially ) |
2474 | | - if( mw.isMobileHTML5() && mobileSafariNeedsRefresh ) { |
| 2396 | + // so we have to do a full replace ( if controls are not included |
| 2397 | + // initially ) |
| 2398 | + if( mw.isHTML5FallForwardNative() && mobileSafariNeedsRefresh ) { |
2475 | 2399 | var source = this.mediaElement.getSources( 'video/h264' )[0]; |
2476 | 2400 | // XXX note this should be updated once mobile supports h.264 |
2477 | 2401 | if( !source || !source.src ){ |
2478 | 2402 | mw.log( 'Warning: Your probably fakeing the iPhone userAgent ( no h.264 source )' ); |
2479 | 2403 | source = this.mediaElement.getSources( 'video/ogg' )[0]; |
2480 | 2404 | } |
2481 | | - |
| 2405 | + |
2482 | 2406 | var videoAttribues = { |
2483 | 2407 | 'id' : _this.pid, |
2484 | 2408 | 'poster': _this.poster, |
2485 | 2409 | 'src' : source.src, |
2486 | 2410 | 'controls' : 'true' |
2487 | | - }; |
2488 | | - |
| 2411 | + } |
| 2412 | + |
2489 | 2413 | if( this.loop ){ |
2490 | 2414 | videoAttribues[ 'loop' ] = 'true'; |
2491 | 2415 | } |
— | — | @@ -2492,17 +2416,18 @@ |
2493 | 2417 | 'width' : _this.width, |
2494 | 2418 | 'height' : _this.height |
2495 | 2419 | }; |
2496 | | - $j( '#' + this.pid ).replaceWith( |
| 2420 | + $j( '#' + this.pid ).replaceWith( |
2497 | 2421 | _this.getNativePlayerHtml( videoAttribues, cssStyle ) |
2498 | | - ); |
| 2422 | + ) |
2499 | 2423 | // Bind native events: |
2500 | 2424 | this.applyMediaElementBindings(); |
2501 | 2425 | } |
2502 | | - // Android only can play with a special play button ( no native controls in the dom , and no auto-play ) |
| 2426 | + // Android only can play with a special play button ( no native controls |
| 2427 | + // in the dom , and no auto-play ) |
2503 | 2428 | // and only with 'native display' |
2504 | 2429 | if( mw.isAndroid2() ){ |
2505 | 2430 | $j( '#' + _this.pid ).siblings('.play-btn-large').remove(); |
2506 | | - $j( '#' + _this.pid ).after( |
| 2431 | + $j( '#' + _this.pid ).after( |
2507 | 2432 | $j('<div />') |
2508 | 2433 | .css({ |
2509 | 2434 | 'position' : 'relative', |
— | — | @@ -2515,30 +2440,31 @@ |
2516 | 2441 | } ) |
2517 | 2442 | .click( function() { |
2518 | 2443 | _this.play(); |
2519 | | - // no need to hide the play button since android plays fullscreen |
| 2444 | + // no need to hide the play button since android plays |
| 2445 | + // fullscreen |
2520 | 2446 | } ) |
2521 | | - ); |
| 2447 | + ) |
2522 | 2448 | } |
2523 | 2449 | return ; |
2524 | 2450 | }, |
2525 | 2451 | /** |
2526 | | - * Should be set via native embed support |
2527 | | - */ |
| 2452 | + * Should be set via native embed support |
| 2453 | + */ |
2528 | 2454 | getNativePlayerHtml: function(){ |
2529 | 2455 | return $j('<div />' ) |
2530 | 2456 | .css( 'width', this.getWidth() ) |
2531 | 2457 | .html( 'Error: Trying to get native html5 player without native support for codec' ); |
2532 | 2458 | }, |
2533 | 2459 | /** |
2534 | | - * Should be set via native embed support |
2535 | | - */ |
| 2460 | + * Should be set via native embed support |
| 2461 | + */ |
2536 | 2462 | applyMediaElementBindings: function(){ |
2537 | 2463 | return ; |
2538 | 2464 | }, |
2539 | | - |
| 2465 | + |
2540 | 2466 | /** |
2541 | | - * Gets code to embed the player remotely for "share" this player links |
2542 | | - */ |
| 2467 | + * Gets code to embed the player remotely for "share" this player links |
| 2468 | + */ |
2543 | 2469 | getEmbeddingHTML: function() { |
2544 | 2470 | switch( mw.getConfig( 'EmbedPlayer.ShareEmbedMode' ) ){ |
2545 | 2471 | case 'iframe': |
— | — | @@ -2549,7 +2475,7 @@ |
2550 | 2476 | break; |
2551 | 2477 | } |
2552 | 2478 | }, |
2553 | | - |
| 2479 | + |
2554 | 2480 | /** |
2555 | 2481 | * Get the share embed object code |
2556 | 2482 | * |
— | — | @@ -2608,50 +2534,50 @@ |
2609 | 2535 | // Return the embed code |
2610 | 2536 | return embedCode; |
2611 | 2537 | }, |
2612 | | - |
| 2538 | + |
2613 | 2539 | /** |
2614 | | - * Get the share embed Video tag code |
2615 | | - */ |
| 2540 | + * Get the share embed Video tag code |
| 2541 | + */ |
2616 | 2542 | getShareEmbedVideoJs: function(){ |
2617 | | - |
| 2543 | + |
2618 | 2544 | // Set the embed tag type: |
2619 | 2545 | var embedtag = ( this.isAudio() )? 'audio': 'video'; |
2620 | | - |
| 2546 | + |
2621 | 2547 | // Set up the mwEmbed js include: |
2622 | 2548 | var embedCode = '<script type="text/javascript" ' + |
2623 | | - 'src="' + |
2624 | | - mw.escapeQuotesHTML( |
2625 | | - mw.absoluteUrl( |
2626 | | - mw.getMwEmbedSrc() |
| 2549 | + 'src="' + |
| 2550 | + mw.escapeQuotesHTML( |
| 2551 | + mw.absoluteUrl( |
| 2552 | + mw.getMwEmbedSrc() |
2627 | 2553 | ) |
2628 | 2554 | ) + '"></script>' + |
2629 | 2555 | '<' + embedtag + ' '; |
2630 | 2556 | |
2631 | 2557 | if( this.poster ) { |
2632 | 2558 | embedCode += 'poster="' + |
2633 | | - mw.escapeQuotesHTML( mw.absoluteUrl( this.poster ) ) + |
| 2559 | + mw.escapeQuotesHTML( mw.absoluteUrl( this.poster ) ) + |
2634 | 2560 | '" '; |
2635 | 2561 | } |
2636 | | - |
| 2562 | + |
2637 | 2563 | // Set the skin if set to something other than default |
2638 | 2564 | if( this.skinName ){ |
2639 | 2565 | embedCode += 'class="' + |
2640 | | - mw.escapeQuotesHTML( this.skinName ) + |
| 2566 | + mw.escapeQuotesHTML( this.skinName ) + |
2641 | 2567 | '" '; |
2642 | 2568 | } |
2643 | | - |
| 2569 | + |
2644 | 2570 | if( this.duration ) { |
2645 | 2571 | embedCode +='durationHint="' + parseFloat( this.duration ) + '" '; |
2646 | 2572 | } |
2647 | | - |
| 2573 | + |
2648 | 2574 | if( this.width || this.height ){ |
2649 | 2575 | embedCode +='style="'; |
2650 | 2576 | embedCode += ( this.width )? 'width:' + this.width +'px;': ''; |
2651 | 2577 | embedCode += ( this.height )? 'height:' + this.height +'px;': ''; |
2652 | 2578 | embedCode += '" '; |
2653 | 2579 | } |
2654 | | - |
2655 | | - |
| 2580 | + |
| 2581 | + |
2656 | 2582 | if ( this.roe ) { |
2657 | 2583 | embedCode += 'roe="' + mw.escapeQuotesHTML( this.roe ) + '" '; |
2658 | 2584 | } else if( this.apiTitleKey ) { |
— | — | @@ -2661,30 +2587,30 @@ |
2662 | 2588 | } |
2663 | 2589 | // close the video tag |
2664 | 2590 | embedCode += '></video>'; |
2665 | | - |
| 2591 | + |
2666 | 2592 | } else { |
2667 | | - //Close the video attr |
| 2593 | + // Close the video attr |
2668 | 2594 | embedCode += '>'; |
2669 | | - |
| 2595 | + |
2670 | 2596 | // Output all the video sources: |
2671 | 2597 | for( var i=0; i < this.mediaElement.sources.length; i++ ){ |
2672 | 2598 | var source = this.mediaElement.sources[i]; |
2673 | 2599 | if( source.src ) { |
2674 | | - embedCode +='<source src="' + |
2675 | | - mw.absoluteUrl( source.src ) + |
2676 | | - '" <>/source>'; |
| 2600 | + embedCode +='<source src="' + |
| 2601 | + mw.absoluteUrl( source.src ) + |
| 2602 | + '" ></source>'; |
2677 | 2603 | } |
2678 | 2604 | } |
2679 | 2605 | // Close the video tag |
2680 | 2606 | embedCode += '</video>'; |
2681 | 2607 | } |
2682 | | - |
| 2608 | + |
2683 | 2609 | return embedCode; |
2684 | 2610 | }, |
2685 | | - |
| 2611 | + |
2686 | 2612 | /** |
2687 | | - * Follows a linkback. Loads the ROE xml if no linkback is found |
2688 | | - */ |
| 2613 | + * Follows a linkback. Loads the ROE xml if no linkback is found |
| 2614 | + */ |
2689 | 2615 | doLinkBack: function() { |
2690 | 2616 | if ( ! this.linkback && this.roe && this.mediaElement.addedROEData == false ) { |
2691 | 2617 | var _this = this; |
— | — | @@ -2703,40 +2629,36 @@ |
2704 | 2630 | } |
2705 | 2631 | } |
2706 | 2632 | }, |
2707 | | - |
| 2633 | + |
2708 | 2634 | /** |
2709 | | - * Base Embed Controls |
2710 | | - */ |
2711 | | - |
| 2635 | + * Base Embed Controls |
| 2636 | + */ |
| 2637 | + |
2712 | 2638 | /** |
2713 | | - * The Play Action |
2714 | | - * |
2715 | | - * Handles play requests, updates relevant states: |
2716 | | - * seeking =false |
2717 | | - * paused = false |
2718 | | - * Updates pause button |
2719 | | - * Starts the "monitor" |
2720 | | - */ |
| 2639 | + * The Play Action |
| 2640 | + * |
| 2641 | + * Handles play requests, updates relevant states: seeking =false paused = |
| 2642 | + * false Updates pause button Starts the "monitor" |
| 2643 | + */ |
2721 | 2644 | play: function() { |
2722 | 2645 | var _this = this; |
2723 | | - // Run play hook (if we we did not bind the native player ) |
2724 | | - if( this.paused && this.useNativePlayerControls() ){ |
| 2646 | + |
| 2647 | + if( this.paused && this.bubbleEventCheck() ){ |
2725 | 2648 | this.paused = false; |
2726 | | - mw.log("trigger play event::" + !this.paused); |
2727 | | - $j( this ).trigger( 'play' ); |
| 2649 | + mw.log("trigger play event::" + !this.paused); |
| 2650 | + $j( this ).trigger( 'play' ); |
2728 | 2651 | } |
2729 | 2652 | this.paused = false; |
2730 | | - |
2731 | | - mw.log( "EmbedPlayer:: play" ); |
| 2653 | + |
| 2654 | + mw.log( "EmbedPlayer:: play" ); |
2732 | 2655 | // Hide any overlay: |
2733 | 2656 | this.controlBuilder.closeMenuOverlay(); |
2734 | | - |
| 2657 | + |
2735 | 2658 | // Check if thumbnail is being displayed and embed html |
2736 | 2659 | if ( this.thumbnail_disp ) { |
2737 | 2660 | if ( !this.selectedPlayer ) { |
2738 | | - mw.log( 'no selectedPlayer' ); |
2739 | 2661 | this.showPluginMissingHTML(); |
2740 | | - return; |
| 2662 | + return; |
2741 | 2663 | } else { |
2742 | 2664 | this.thumbnail_disp = false; |
2743 | 2665 | this.doEmbedHTML(); |
— | — | @@ -2745,31 +2667,42 @@ |
2746 | 2668 | // the plugin is already being displayed |
2747 | 2669 | this.seeking = false; |
2748 | 2670 | } |
2749 | | - |
2750 | | - |
| 2671 | + |
| 2672 | + |
2751 | 2673 | this.$interface.find('.play-btn span') |
2752 | 2674 | .removeClass( 'ui-icon-play' ) |
2753 | 2675 | .addClass( 'ui-icon-pause' ); |
2754 | | - |
| 2676 | + |
2755 | 2677 | this.$interface.find( '.play-btn' ) |
2756 | 2678 | .unbind() |
2757 | 2679 | .buttonHover() |
2758 | 2680 | .click( function( ) { |
2759 | 2681 | _this.pause(); |
2760 | | - } ) |
2761 | | - .attr( 'title', gM( 'mwe-embedplayer-pause_clip' ) ); |
| 2682 | + } ) |
| 2683 | + .attr( 'title', gM( 'mwe-embedplayer-pause_clip' ) ); |
2762 | 2684 | |
2763 | 2685 | // Start the monitor if not already started |
2764 | 2686 | this.monitor(); |
2765 | | - |
2766 | | - // If we previously finished playing this clip run the "replay hook" |
2767 | | - if( this.donePlayingCount > 0 ) { |
2768 | | - mw.log("replayEvent"); |
2769 | | - $j( this ).trigger( 'replayEvent' ); |
| 2687 | + |
| 2688 | + // If we previously finished playing this clip run the "replay hook" |
| 2689 | + if( this.donePlayingCount > 0 ) { |
| 2690 | + mw.log("replayEvent"); |
| 2691 | + $j( this ).trigger( 'replayEvent' ); |
| 2692 | + } |
| 2693 | + }, |
| 2694 | + /** |
| 2695 | + * Returns true if the event should be triggered or false if not |
| 2696 | + * |
| 2697 | + * @@FIXME:: firefox nightlies now Do NOT bubble events. Once release tag |
| 2698 | + * every version after that |
| 2699 | + */ |
| 2700 | + bubbleEventCheck: function(){ |
| 2701 | + if( $j.browser.webkit ){ |
| 2702 | + return true; |
2770 | 2703 | } |
| 2704 | + return false; |
2771 | 2705 | }, |
2772 | | - |
2773 | | - |
| 2706 | + |
2774 | 2707 | /** |
2775 | 2708 | * Maps the html5 load request. There is no general way to "load" clips so |
2776 | 2709 | * underling plugin-player libs should override. |
— | — | @@ -2778,29 +2711,29 @@ |
2779 | 2712 | // should be done by child (no base way to pre-buffer video) |
2780 | 2713 | mw.log( 'baseEmbed:load call' ); |
2781 | 2714 | }, |
2782 | | - |
| 2715 | + |
2783 | 2716 | /** |
2784 | | - * Base embed pause |
2785 | | - * Updates the play/pause button state. |
2786 | | - * |
2787 | | - * There is no general way to pause the video |
2788 | | - * must be overwritten by embed object to support this functionality. |
2789 | | - */ |
| 2717 | + * Base embed pause Updates the play/pause button state. |
| 2718 | + * |
| 2719 | + * There is no general way to pause the video must be overwritten by embed |
| 2720 | + * object to support this functionality. |
| 2721 | + */ |
2790 | 2722 | pause: function( event ) { |
2791 | 2723 | var _this = this; |
2792 | | - // Trigger the pause event if not already paused and using native controls: |
2793 | | - if( this.paused === false && this.useNativePlayerControls() ){ |
| 2724 | + // Trigger the pause event if not already paused and using native |
| 2725 | + // controls: |
| 2726 | + if( this.paused === false && this.bubbleEventCheck() ){ |
2794 | 2727 | this.paused = true; |
2795 | 2728 | mw.log('EmbedPlayer:trigger pause:' + this.paused); |
2796 | 2729 | $j( this ).trigger('pause' ); |
2797 | 2730 | } |
2798 | 2731 | this.paused = true; |
2799 | | - |
| 2732 | + |
2800 | 2733 | // update the ctrl "paused state" |
2801 | 2734 | this.$interface.find('.play-btn span' ) |
2802 | 2735 | .removeClass( 'ui-icon-pause' ) |
2803 | 2736 | .addClass( 'ui-icon-play' ); |
2804 | | - |
| 2737 | + |
2805 | 2738 | this.$interface.find('.play-btn' ) |
2806 | 2739 | .unbind() |
2807 | 2740 | .buttonHover() |
— | — | @@ -2809,26 +2742,23 @@ |
2810 | 2743 | } ) |
2811 | 2744 | .attr( 'title', gM( 'mwe-embedplayer-play_clip' ) ); |
2812 | 2745 | }, |
2813 | | - |
| 2746 | + |
2814 | 2747 | /** |
2815 | | - * Base embed stop |
2816 | | - * |
2817 | | - * Updates the player to the stop state |
2818 | | - * shows Thumbnail |
2819 | | - * resets Buffer |
2820 | | - * resets Playhead slider |
2821 | | - * resets Status |
2822 | | - */ |
| 2748 | + * Base embed stop |
| 2749 | + * |
| 2750 | + * Updates the player to the stop state shows Thumbnail resets Buffer resets |
| 2751 | + * Playhead slider resets Status |
| 2752 | + */ |
2823 | 2753 | stop: function() { |
2824 | 2754 | var _this = this; |
2825 | 2755 | mw.log( 'EmbedPlayer::stop:' + this.id ); |
2826 | | - |
| 2756 | + |
2827 | 2757 | // no longer seeking: |
2828 | 2758 | this.didSeekJump = false; |
2829 | | - |
| 2759 | + |
2830 | 2760 | // Reset current time and prev time and seek offset |
2831 | | - this.currentTime = this.previousTime = this.serverSeekTime = 0; |
2832 | | - |
| 2761 | + this.currentTime = this.previousTime = this.serverSeekTime = 0; |
| 2762 | + |
2833 | 2763 | // Issue pause to update interface (only call this parent) |
2834 | 2764 | if( !this.paused ){ |
2835 | 2765 | this.paused = true; |
— | — | @@ -2839,30 +2769,30 @@ |
2840 | 2770 | this.pause(); |
2841 | 2771 | } |
2842 | 2772 | } |
2843 | | - |
| 2773 | + |
2844 | 2774 | // Rewrite the html to thumbnail disp |
2845 | 2775 | this.showThumbnail(); |
2846 | 2776 | this.bufferedPercent = 0; // reset buffer state |
2847 | 2777 | this.controlBuilder.setStatus( this.getTimeRange() ); |
2848 | | - |
| 2778 | + |
2849 | 2779 | // Reset the playhead |
2850 | | - mw.log("EmbedPlayer::Stop:: Reset play head"); |
| 2780 | + mw.log("EmbedPlayer::Stop:: Reset play head") |
2851 | 2781 | this.updatePlayHead( 0 ); |
2852 | | - |
2853 | | - //Bind play-btn-large play |
| 2782 | + |
| 2783 | + // Bind play-btn-large play |
2854 | 2784 | this.$interface.find( '.play-btn-large' ) |
2855 | 2785 | .unbind( 'click' ) |
2856 | 2786 | .click( function() { |
2857 | 2787 | _this.play(); |
2858 | 2788 | } ); |
2859 | 2789 | }, |
2860 | | - |
| 2790 | + |
2861 | 2791 | /** |
2862 | | - * Base Embed mute |
2863 | | - * |
2864 | | - * Handles interface updates for toggling mute. |
2865 | | - * Plug-in / player interface must handle the actual media player update |
2866 | | - */ |
| 2792 | + * Base Embed mute |
| 2793 | + * |
| 2794 | + * Handles interface updates for toggling mute. Plug-in / player interface |
| 2795 | + * must handle the actual media player update |
| 2796 | + */ |
2867 | 2797 | toggleMute: function() { |
2868 | 2798 | mw.log( 'f:toggleMute:: (old state:) ' + this.muted ); |
2869 | 2799 | if ( this.muted ) { |
— | — | @@ -2877,11 +2807,13 @@ |
2878 | 2808 | // Update the interface |
2879 | 2809 | this.setInterfaceVolume( percent ); |
2880 | 2810 | }, |
2881 | | - |
| 2811 | + |
2882 | 2812 | /** |
2883 | | - * Update volume function ( called from interface updates ) |
2884 | | - * @param {float} percent Percent of full volume |
2885 | | - */ |
| 2813 | + * Update volume function ( called from interface updates ) |
| 2814 | + * |
| 2815 | + * @param {float} |
| 2816 | + * percent Percent of full volume |
| 2817 | + */ |
2886 | 2818 | setVolume: function( percent ) { |
2887 | 2819 | // ignore NaN percent: |
2888 | 2820 | if( isNaN( percent ) ){ |
— | — | @@ -2889,78 +2821,81 @@ |
2890 | 2822 | } |
2891 | 2823 | // Set the local volume attribute |
2892 | 2824 | this.previousVolume = this.volume = percent; |
2893 | | - |
| 2825 | + |
2894 | 2826 | // Un-mute if setting positive volume |
2895 | 2827 | if( percent != 0 ){ |
2896 | 2828 | this.muted = false; |
2897 | 2829 | } |
2898 | | - |
| 2830 | + |
2899 | 2831 | // Update the playerElement volume |
2900 | 2832 | this.setPlayerElementVolume( percent ); |
2901 | | - |
2902 | | - //mw.log(" setVolume:: " + percent + ' this.volume is: ' + this.volume); |
| 2833 | + |
| 2834 | + // mw.log(" setVolume:: " + percent + ' this.volume is: ' + |
| 2835 | + // this.volume); |
2903 | 2836 | $j( this ).trigger('volumeChanged', percent ); |
2904 | 2837 | }, |
2905 | | - |
| 2838 | + |
2906 | 2839 | /** |
2907 | | - * Updates the interface volume |
2908 | | - * TODO should move to controlBuilder |
2909 | | - * @param {float} percent Percentage volume to update interface |
2910 | | - */ |
| 2840 | + * Updates the interface volume TODO should move to controlBuilder |
| 2841 | + * |
| 2842 | + * @param {float} |
| 2843 | + * percent Percentage volume to update interface |
| 2844 | + */ |
2911 | 2845 | setInterfaceVolume: function( percent ) { |
2912 | | - if( this.supports[ 'volumeControl' ] && |
2913 | | - this.$interface.find( '.volume-slider' ).length |
| 2846 | + if( this.supports[ 'volumeControl' ] && |
| 2847 | + this.$interface.find( '.volume-slider' ).length |
2914 | 2848 | ) { |
2915 | 2849 | this.$interface.find( '.volume-slider' ).slider( 'value', percent * 100 ); |
2916 | 2850 | } |
2917 | 2851 | }, |
2918 | | - |
| 2852 | + |
2919 | 2853 | /** |
2920 | | - * Abstract Update volume Method must be override by plug-in / player interface |
2921 | | - */ |
| 2854 | + * Abstract Update volume Method must be override by plug-in / player |
| 2855 | + * interface |
| 2856 | + */ |
2922 | 2857 | setPlayerElementVolume: function( percent ) { |
2923 | 2858 | mw.log('Error player does not support volume adjustment' ); |
2924 | 2859 | }, |
2925 | | - |
| 2860 | + |
2926 | 2861 | /** |
2927 | | - * Abstract get volume Method must be override by plug-in / player interface |
2928 | | - * (if player does not override we return the abstract player value ) |
2929 | | - */ |
| 2862 | + * Abstract get volume Method must be override by plug-in / player interface |
| 2863 | + * (if player does not override we return the abstract player value ) |
| 2864 | + */ |
2930 | 2865 | getPlayerElementVolume: function(){ |
2931 | | - //mw.log(' error player does not support getting volume property' ); |
| 2866 | + // mw.log(' error player does not support getting volume property' ); |
2932 | 2867 | return this.volume; |
2933 | 2868 | }, |
2934 | | - |
| 2869 | + |
2935 | 2870 | /** |
2936 | | - * Abstract get volume muted property must be overwritten by plug-in / player interface |
2937 | | - * (if player does not override we return the abstract player value ) |
2938 | | - */ |
| 2871 | + * Abstract get volume muted property must be overwritten by plug-in / |
| 2872 | + * player interface (if player does not override we return the abstract |
| 2873 | + * player value ) |
| 2874 | + */ |
2939 | 2875 | getPlayerElementMuted: function(){ |
2940 | | - //mw.log(' error player does not support getting mute property' ); |
| 2876 | + // mw.log(' error player does not support getting mute property' ); |
2941 | 2877 | return this.muted; |
2942 | 2878 | }, |
2943 | | - |
| 2879 | + |
2944 | 2880 | /** |
2945 | | - * Passes a fullscreen request to the controlBuilder interface |
2946 | | - */ |
| 2881 | + * Passes a fullscreen request to the controlBuilder interface |
| 2882 | + */ |
2947 | 2883 | fullscreen: function() { |
2948 | 2884 | this.controlBuilder.toggleFullscreen(); |
2949 | 2885 | }, |
2950 | | - |
| 2886 | + |
2951 | 2887 | /** |
2952 | | - * Abstract method to be run post embedding the player |
2953 | | - * Generally should be overwritten by the plug-in / player |
2954 | | - */ |
| 2888 | + * Abstract method to be run post embedding the player Generally should be |
| 2889 | + * overwritten by the plug-in / player |
| 2890 | + */ |
2955 | 2891 | postEmbedJS:function() { |
2956 | 2892 | return ; |
2957 | 2893 | }, |
2958 | | - |
| 2894 | + |
2959 | 2895 | /** |
2960 | | - * Checks the player state based on thumbnail display & paused state |
2961 | | - * @return {Boolean} |
2962 | | - * true if playing |
2963 | | - * false if not playing |
2964 | | - */ |
| 2896 | + * Checks the player state based on thumbnail display & paused state |
| 2897 | + * |
| 2898 | + * @return {Boolean} true if playing false if not playing |
| 2899 | + */ |
2965 | 2900 | isPlaying : function() { |
2966 | 2901 | if ( this.thumbnail_disp ) { |
2967 | 2902 | // in stopped state |
— | — | @@ -2972,35 +2907,33 @@ |
2973 | 2908 | return true; |
2974 | 2909 | } |
2975 | 2910 | }, |
2976 | | - |
| 2911 | + |
2977 | 2912 | /** |
2978 | | - * Get paused state |
2979 | | - * @return {Boolean} |
2980 | | - * true if playing |
2981 | | - * false if not playing |
2982 | | - */ |
| 2913 | + * Get paused state |
| 2914 | + * |
| 2915 | + * @return {Boolean} true if playing false if not playing |
| 2916 | + */ |
2983 | 2917 | isPaused: function() { |
2984 | 2918 | return this.paused; |
2985 | 2919 | }, |
2986 | | - |
| 2920 | + |
2987 | 2921 | /** |
2988 | | - * Get Stopped state |
2989 | | - * @return {Boolean} |
2990 | | - * true if stopped |
2991 | | - * false if playing |
2992 | | - */ |
| 2922 | + * Get Stopped state |
| 2923 | + * |
| 2924 | + * @return {Boolean} true if stopped false if playing |
| 2925 | + */ |
2993 | 2926 | isStopped: function() { |
2994 | 2927 | return this.thumbnail_disp; |
2995 | 2928 | }, |
2996 | | - |
| 2929 | + |
2997 | 2930 | // xxx temporary hack we need a better stop monitor system |
2998 | 2931 | stopMonitor: function(){ |
2999 | 2932 | this.thumbnail_disp = true; |
3000 | 2933 | }, |
3001 | | - |
| 2934 | + |
3002 | 2935 | /** |
3003 | | - * Checks if the currentTime was updated outside of |
3004 | | - * the getPlayerElementTime function |
| 2936 | + * Checks if the currentTime was updated outside of the getPlayerElementTime |
| 2937 | + * function |
3005 | 2938 | */ |
3006 | 2939 | checkForCurrentTimeSeek: function(){ |
3007 | 2940 | var _this = this; |
— | — | @@ -3015,51 +2948,54 @@ |
3016 | 2949 | } |
3017 | 2950 | } |
3018 | 2951 | }, |
3019 | | - |
| 2952 | + |
3020 | 2953 | /** |
3021 | | - * Monitor playback and update interface components. |
3022 | | - * underling plugin objects are responsible for updating currentTime |
3023 | | - */ |
| 2954 | + * Monitor playback and update interface components. underling plugin |
| 2955 | + * objects are responsible for updating currentTime |
| 2956 | + */ |
3024 | 2957 | monitor: function() { |
3025 | 2958 | var _this = this; |
3026 | | - |
| 2959 | + |
3027 | 2960 | // Check for current time update outside of embed player |
3028 | 2961 | this.checkForCurrentTimeSeek(); |
3029 | | - |
| 2962 | + |
3030 | 2963 | // Update currentTime via embedPlayer |
3031 | 2964 | _this.currentTime = _this.getPlayerElementTime(); |
3032 | | - |
| 2965 | + |
3033 | 2966 | // Update any offsets from server seek |
3034 | 2967 | if( _this.serverSeekTime && _this.supportsURLTimeEncoding ){ |
3035 | | - _this.currentTime = _this.serverSeekTime + _this.getPlayerElementTime(); |
| 2968 | + _this.currentTime = _this.serverSeekTime + _this.getPlayerElementTime() |
3036 | 2969 | } |
3037 | 2970 | |
3038 | | - // Update the previousTime ( so we can know if the user-javascript changed currentTime ) |
| 2971 | + // Update the previousTime ( so we can know if the user-javascript |
| 2972 | + // changed currentTime ) |
3039 | 2973 | _this.previousTime = _this.currentTime; |
3040 | | - |
3041 | | - |
| 2974 | + |
| 2975 | + |
3042 | 2976 | // Check if volume was set outside of embed player function |
3043 | | - //mw.log( ' this.volume: ' + _this.volume + ' prev Volume:: ' + _this.previousVolume ); |
| 2977 | + // mw.log( ' this.volume: ' + _this.volume + ' prev Volume:: ' + |
| 2978 | + // _this.previousVolume ); |
3044 | 2979 | if( Math.round( _this.volume * 100 ) != Math.round( _this.previousVolume * 100 ) ) { |
3045 | 2980 | _this.setInterfaceVolume( _this.volume ); |
3046 | 2981 | $j( this ).trigger('volumeChanged', _this.volume ); |
3047 | 2982 | } |
3048 | | - |
| 2983 | + |
3049 | 2984 | // Update the previous volume |
3050 | 2985 | _this.previousVolume = _this.volume; |
3051 | | - |
| 2986 | + |
3052 | 2987 | // Update the volume from the player element |
3053 | 2988 | _this.volume = this.getPlayerElementVolume(); |
3054 | | - |
| 2989 | + |
3055 | 2990 | // update the mute state from the player element |
3056 | 2991 | if( _this.muted != _this.getPlayerElementMuted() && ! _this.isStopped() ){ |
3057 | 2992 | mw.log( "EmbedPlayer::monitor: muted does not mach embed player" ); |
3058 | 2993 | _this.toggleMute(); |
3059 | 2994 | // Make sure they match: |
3060 | | - _this.muted = _this.getPlayerElementMuted(); |
| 2995 | + _this.muted = _this.getPlayerElementMuted(); |
3061 | 2996 | } |
3062 | | - |
3063 | | - //mw.log( 'Monitor:: ' + this.currentTime + ' duration: ' + ( parseInt( this.getDuration() ) + 1 ) + ' is seeking: ' + this.seeking ); |
| 2997 | + |
| 2998 | + // mw.log( 'Monitor:: ' + this.currentTime + ' duration: ' + ( parseInt( |
| 2999 | + // this.getDuration() ) + 1 ) + ' is seeking: ' + this.seeking ); |
3064 | 3000 | if ( this.currentTime >= 0 && this.duration ) { |
3065 | 3001 | if ( !this.userSlide && !this.seeking ) { |
3066 | 3002 | if ( parseInt( this.startOffset ) != 0 ) { |
— | — | @@ -3095,55 +3031,57 @@ |
3096 | 3032 | this.controlBuilder.setStatus( this.getTimeRange() ); |
3097 | 3033 | } |
3098 | 3034 | } |
3099 | | - |
| 3035 | + |
3100 | 3036 | // Update buffer information |
3101 | 3037 | this.updateBufferStatus(); |
3102 | | - |
| 3038 | + |
3103 | 3039 | // run the "native" progress event on the virtual html5 object if set |
3104 | 3040 | if( this.progressEventData ) { |
3105 | | - //mw.log("trigger:progress event on html5 proxy"); |
| 3041 | + // mw.log("trigger:progress event on html5 proxy"); |
3106 | 3042 | $j( this ).trigger( 'progress', this.progressEventData ); |
3107 | 3043 | } |
3108 | | - |
3109 | | - // Call monitor at 250ms interval. ( use setInterval to avoid stacking monitor requests ) |
| 3044 | + |
| 3045 | + // Call monitor at 250ms interval. ( use setInterval to avoid stacking |
| 3046 | + // monitor requests ) |
3110 | 3047 | if( ! this.isStopped() ) { |
3111 | 3048 | if( !this.monitorInterval ){ |
3112 | 3049 | this.monitorInterval = setInterval( function(){ |
3113 | 3050 | if( _this.monitor ) |
3114 | 3051 | _this.monitor(); |
3115 | | - }, this.monitorRate ); |
| 3052 | + }, this.monitorRate ) |
3116 | 3053 | } |
3117 | 3054 | } else { |
3118 | 3055 | // If stopped "stop" monitor: |
3119 | 3056 | clearInterval( this.monitorInterval ); |
3120 | 3057 | this.monitorInterval = 0; |
3121 | 3058 | } |
3122 | | - |
3123 | | - //mw.log('trigger:monitor:: ' + this.currentTime ); |
| 3059 | + |
| 3060 | + // mw.log('trigger:monitor:: ' + this.currentTime ); |
3124 | 3061 | $j( this ).trigger( 'monitorEvent' ); |
3125 | 3062 | }, |
3126 | | - |
| 3063 | + |
3127 | 3064 | /** |
3128 | 3065 | * Abstract getPlayerElementTime function |
3129 | 3066 | */ |
3130 | 3067 | getPlayerElementTime: function(){ |
3131 | 3068 | mw.log("Error: getPlayerElementTime should be implemented by embed library"); |
3132 | 3069 | }, |
3133 | | - |
| 3070 | + |
3134 | 3071 | /** |
3135 | | - * Update the Buffer status based on the local bufferedPercent var |
3136 | | - */ |
| 3072 | + * Update the Buffer status based on the local bufferedPercent var |
| 3073 | + */ |
3137 | 3074 | updateBufferStatus: function() { |
3138 | | - |
| 3075 | + |
3139 | 3076 | // Get the buffer target based for playlist vs clip |
3140 | 3077 | $buffer = this.$interface.find( '.mw_buffer' ); |
3141 | 3078 | // Update the buffer progress bar (if available ) |
3142 | 3079 | if ( this.bufferedPercent != 0 ) { |
3143 | | - //mw.log('Update buffer css: ' + ( this.bufferedPercent * 100 ) + '% ' + $buffer.length ); |
| 3080 | + // mw.log('Update buffer css: ' + ( this.bufferedPercent * 100 ) + |
| 3081 | + // '% ' + $buffer.length ); |
3144 | 3082 | if ( this.bufferedPercent > 1 ){ |
3145 | 3083 | this.bufferedPercent = 1; |
3146 | 3084 | } |
3147 | | - |
| 3085 | + |
3148 | 3086 | $buffer.css({ |
3149 | 3087 | "width" : ( this.bufferedPercent * 100 ) + '%' |
3150 | 3088 | }); |
— | — | @@ -3151,26 +3089,27 @@ |
3152 | 3090 | } else { |
3153 | 3091 | $buffer.css( "width", '0px' ); |
3154 | 3092 | } |
3155 | | - |
| 3093 | + |
3156 | 3094 | // if we have not already run the buffer start hook |
3157 | 3095 | if( this.bufferedPercent > 0 && !this.bufferStartFlag ) { |
3158 | 3096 | this.bufferStartFlag = true; |
3159 | 3097 | mw.log("bufferStart"); |
3160 | 3098 | $j( this ).trigger( 'bufferStartEvent' ); |
3161 | 3099 | } |
3162 | | - |
| 3100 | + |
3163 | 3101 | // if we have not already run the buffer end hook |
3164 | 3102 | if( this.bufferedPercent == 1 && !this.bufferEndFlag){ |
3165 | 3103 | this.bufferEndFlag = true; |
3166 | 3104 | $j( this ).trigger( 'bufferEndEvent' ); |
3167 | 3105 | } |
3168 | 3106 | }, |
3169 | | - |
| 3107 | + |
3170 | 3108 | /** |
3171 | | - * Update the player playhead |
3172 | | - * |
3173 | | - * @param {Float} perc Value between 0 and 1 for position of playhead |
3174 | | - */ |
| 3109 | + * Update the player playhead |
| 3110 | + * |
| 3111 | + * @param {Float} |
| 3112 | + * perc Value between 0 and 1 for position of playhead |
| 3113 | + */ |
3175 | 3114 | updatePlayHead: function( perc ) { |
3176 | 3115 | $playHead = this.$interface.find( '.play_head' ); |
3177 | 3116 | if ( this.controls && $playHead.length != 0 ) { |
— | — | @@ -3180,12 +3119,13 @@ |
3181 | 3120 | // @@todo should have 'progress' trigger the same as html5 |
3182 | 3121 | $j( this ).trigger('updatePlayHeadPercent', perc); |
3183 | 3122 | }, |
3184 | | - |
| 3123 | + |
3185 | 3124 | /** |
3186 | | - * Highlight a section of video on the playhead |
3187 | | - * |
3188 | | - * @param {Object} options Provides "start" time & "end" time to highlight |
3189 | | - */ |
| 3125 | + * Highlight a section of video on the playhead |
| 3126 | + * |
| 3127 | + * @param {Object} |
| 3128 | + * options Provides "start" time & "end" time to highlight |
| 3129 | + */ |
3190 | 3130 | highlightPlaySection:function( options ) { |
3191 | 3131 | mw.log( 'highlightPlaySection' ); |
3192 | 3132 | var eid = ( this.pc ) ? this.pc.pp.id:this.id; |
— | — | @@ -3194,8 +3134,8 @@ |
3195 | 3135 | rel_start_sec = mw.npt2seconds( options['start'] ); |
3196 | 3136 | // remove the startOffset if relevent: |
3197 | 3137 | if ( this.startOffset ) |
3198 | | - rel_start_sec = rel_start_sec - this.startOffset; |
3199 | | - |
| 3138 | + rel_start_sec = rel_start_sec - this.startOffset |
| 3139 | + |
3200 | 3140 | var slider_perc = 0; |
3201 | 3141 | if ( rel_start_sec <= 0 ) { |
3202 | 3142 | left_perc = 0; |
— | — | @@ -3206,22 +3146,23 @@ |
3207 | 3147 | left_perc = parseInt( ( rel_start_sec / dur ) * 100 ) ; |
3208 | 3148 | slider_perc = ( left_perc / 100 ); |
3209 | 3149 | } |
3210 | | - |
| 3150 | + |
3211 | 3151 | mw.log( "slider perc:" + slider_perc ); |
3212 | 3152 | if ( ! this.isPlaying() ) { |
3213 | 3153 | this.updatePlayHead( slider_perc ); |
3214 | 3154 | } |
3215 | | - |
| 3155 | + |
3216 | 3156 | width_perc = parseInt( ( ( mw.npt2seconds( options['end'] ) - mw.npt2seconds( options['start'] ) ) / dur ) * 100 ) ; |
3217 | 3157 | if ( ( width_perc + left_perc ) > 100 ) { |
3218 | 3158 | width_perc = 100 - left_perc; |
3219 | 3159 | } |
3220 | | - // mw.log('should hl: '+rel_start_sec+ '/' + dur + ' re:' + rel_end_sec+' lp:' + left_perc + ' width: ' + width_perc); |
| 3160 | + // mw.log('should hl: '+rel_start_sec+ '/' + dur + ' re:' + |
| 3161 | + // rel_end_sec+' lp:' + left_perc + ' width: ' + width_perc); |
3221 | 3162 | $j( '#mv_seeker_' + eid + ' .mv_highlight' ).css( { |
3222 | 3163 | 'left' : left_perc + '%', |
3223 | 3164 | 'width' : width_perc + '%' |
3224 | 3165 | } ).show(); |
3225 | | - |
| 3166 | + |
3226 | 3167 | this.jump_time = options['start']; |
3227 | 3168 | this.serverSeekTime = mw.npt2seconds( options['start'] ); |
3228 | 3169 | // trim output to |
— | — | @@ -3229,56 +3170,58 @@ |
3230 | 3171 | mw.log( 'DO update: ' + this.jump_time ); |
3231 | 3172 | this.updateThumbTime( rel_start_sec ); |
3232 | 3173 | }, |
3233 | | - |
| 3174 | + |
3234 | 3175 | /** |
3235 | | - * Hides the playhead highlight |
3236 | | - */ |
| 3176 | + * Hides the playhead highlight |
| 3177 | + */ |
3237 | 3178 | hideHighlight: function() { |
3238 | 3179 | var eid = ( this.pc ) ? this.pc.pp.id:this.id; |
3239 | 3180 | $j( '#mv_seeker_' + eid + ' .mv_highlight' ).hide(); |
3240 | 3181 | this.controlBuilder.setStatus( this.getTimeRange() ); |
3241 | 3182 | }, |
3242 | | - |
3243 | | - |
| 3183 | + |
| 3184 | + |
3244 | 3185 | /** |
3245 | | - * Helper Functions for selected source |
3246 | | - */ |
3247 | | - |
| 3186 | + * Helper Functions for selected source |
| 3187 | + */ |
| 3188 | + |
3248 | 3189 | /** |
3249 | | - * Get the current selected media source |
3250 | | - * |
3251 | | - * @return src url |
3252 | | - */ |
| 3190 | + * Get the current selected media source |
| 3191 | + * |
| 3192 | + * @return src url |
| 3193 | + */ |
3253 | 3194 | getSrc: function() { |
3254 | 3195 | if( this.mediaElement.selectedSource ){ |
3255 | 3196 | return this.mediaElement.selectedSource.getSrc( this.serverSeekTime ); |
3256 | 3197 | } |
3257 | 3198 | return false; |
3258 | 3199 | }, |
3259 | | - |
| 3200 | + |
3260 | 3201 | /** |
3261 | | - * If the selected src supports URL time encoding |
3262 | | - * |
3263 | | - * @return {Boolean} |
3264 | | - * ture if the src supports url time requests |
3265 | | - * false if the src does not support url time requests |
3266 | | - */ |
| 3202 | + * If the selected src supports URL time encoding |
| 3203 | + * |
| 3204 | + * @return {Boolean} ture if the src supports url time requests false if the |
| 3205 | + * src does not support url time requests |
| 3206 | + */ |
3267 | 3207 | supportsURLTimeEncoding: function() { |
3268 | 3208 | // do head request if on the same domain |
3269 | 3209 | return this.mediaElement.selectedSource.URLTimeEncoding; |
3270 | 3210 | } |
3271 | | -}; |
| 3211 | +} |
3272 | 3212 | |
3273 | 3213 | |
3274 | 3214 | |
3275 | 3215 | /** |
3276 | | - * mediaPlayer represents a media player plugin. |
3277 | | - * |
3278 | | - * @param {String} id id used for the plugin. |
3279 | | - * @param {Array} supported_types an array of supported MIME types. |
3280 | | - * @param {String} library external script containing the plugin interface code. |
3281 | | - * @constructor |
3282 | | - */ |
| 3216 | + * mediaPlayer represents a media player plugin. |
| 3217 | + * |
| 3218 | + * @param {String} |
| 3219 | + * id id used for the plugin. |
| 3220 | + * @param {Array} |
| 3221 | + * supported_types an array of supported MIME types. |
| 3222 | + * @param {String} |
| 3223 | + * library external script containing the plugin interface code. |
| 3224 | + * @constructor |
| 3225 | + */ |
3283 | 3226 | function mediaPlayer( id, supported_types, library ) |
3284 | 3227 | { |
3285 | 3228 | this.id = id; |
— | — | @@ -3291,24 +3234,24 @@ |
3292 | 3235 | mediaPlayer.prototype = { |
3293 | 3236 | // Id of the mediaPlayer |
3294 | 3237 | id:null, |
3295 | | - |
| 3238 | + |
3296 | 3239 | // Mime types supported by this player |
3297 | 3240 | supported_types:null, |
3298 | | - |
| 3241 | + |
3299 | 3242 | // Player library ie: native, vlc, java etc. |
3300 | 3243 | library:null, |
3301 | | - |
| 3244 | + |
3302 | 3245 | // Flag stores the mediaPlayer load state |
3303 | 3246 | loaded:false, |
3304 | | - |
| 3247 | + |
3305 | 3248 | /** |
3306 | | - * Checks support for a given MIME type |
3307 | | - * |
3308 | | - * @param {String} type Mime type to check against supported_types |
3309 | | - * @return {Boolean} |
3310 | | - * true if mime type is supported |
3311 | | - * false if mime type is unsupported |
3312 | | - */ |
| 3249 | + * Checks support for a given MIME type |
| 3250 | + * |
| 3251 | + * @param {String} |
| 3252 | + * type Mime type to check against supported_types |
| 3253 | + * @return {Boolean} true if mime type is supported false if mime type is |
| 3254 | + * unsupported |
| 3255 | + */ |
3313 | 3256 | supportsMIMEType: function( type ) { |
3314 | 3257 | for ( var i = 0; i < this.supported_types.length; i++ ) { |
3315 | 3258 | if ( this.supported_types[i] == type ) |
— | — | @@ -3316,39 +3259,41 @@ |
3317 | 3260 | } |
3318 | 3261 | return false; |
3319 | 3262 | }, |
3320 | | - |
| 3263 | + |
3321 | 3264 | /** |
3322 | | - * Get the "name" of the player from a predictable msg key |
3323 | | - */ |
| 3265 | + * Get the "name" of the player from a predictable msg key |
| 3266 | + */ |
3324 | 3267 | getName: function() { |
3325 | 3268 | return gM( 'mwe-embedplayer-ogg-player-' + this.id ); |
3326 | 3269 | }, |
3327 | | - |
| 3270 | + |
3328 | 3271 | /** |
3329 | | - * Loads the player library & player skin config ( if needed ) and then calls the callback. |
3330 | | - * |
3331 | | - * @param {Function} callback Function to be called once player library is loaded. |
3332 | | - */ |
| 3272 | + * Loads the player library & player skin config ( if needed ) and then |
| 3273 | + * calls the callback. |
| 3274 | + * |
| 3275 | + * @param {Function} |
| 3276 | + * callback Function to be called once player library is loaded. |
| 3277 | + */ |
3333 | 3278 | load: function( callback ) { |
3334 | | - //Load player library ( upper case the first letter of the library ) |
| 3279 | + // Load player library ( upper case the first letter of the library ) |
3335 | 3280 | mw.load( [ |
3336 | 3281 | 'mw.EmbedPlayer' + this.library.substr(0,1).toUpperCase() + this.library.substr(1) |
3337 | 3282 | ], function() { |
3338 | 3283 | callback(); |
3339 | 3284 | } ); |
3340 | 3285 | } |
3341 | | -}; |
| 3286 | +} |
3342 | 3287 | |
3343 | 3288 | /** |
3344 | | -* players and supported mime types |
3345 | | -* In an ideal world we would query the plugin to get what mime |
3346 | | -* types it supports in practice not always reliable/available |
3347 | | -* |
3348 | | -* We can't cleanly store these values per library since player library is loaded post player detection |
3349 | | -* |
3350 | | -*/ |
| 3289 | + * players and supported mime types In an ideal world we would query the plugin |
| 3290 | + * to get what mime types it supports in practice not always reliable/available |
| 3291 | + * |
| 3292 | + * We can't cleanly store these values per library since player library is |
| 3293 | + * loaded post player detection |
| 3294 | + * |
| 3295 | + */ |
3351 | 3296 | |
3352 | | -//Flash based players: |
| 3297 | +// Flash based players: |
3353 | 3298 | |
3354 | 3299 | var kplayer = new mediaPlayer('kplayer', ['video/x-flv', 'video/h264'], 'Kplayer'); |
3355 | 3300 | |
— | — | @@ -3373,6 +3318,7 @@ |
3374 | 3319 | |
3375 | 3320 | /** |
3376 | 3321 | * mediaPlayers is a collection of mediaPlayer objects supported by the client. |
| 3322 | + * |
3377 | 3323 | * @constructor |
3378 | 3324 | */ |
3379 | 3325 | function mediaPlayers() |
— | — | @@ -3384,44 +3330,46 @@ |
3385 | 3331 | { |
3386 | 3332 | // The list of players supported |
3387 | 3333 | players : null, |
3388 | | - |
| 3334 | + |
3389 | 3335 | // Store per mime-type prefrences for players |
3390 | 3336 | preference : { }, |
3391 | | - |
| 3337 | + |
3392 | 3338 | // Stores the default set of players for a given mime type |
3393 | 3339 | defaultPlayers : { }, |
3394 | | - |
| 3340 | + |
3395 | 3341 | /** |
3396 | | - * Initializartion function sets the default order for players for |
3397 | | - * a given mime type |
3398 | | - */ |
| 3342 | + * Initializartion function sets the default order for players for a given |
| 3343 | + * mime type |
| 3344 | + */ |
3399 | 3345 | init: function() { |
3400 | 3346 | this.players = new Array(); |
3401 | 3347 | this.loadPreferences(); |
3402 | | - |
| 3348 | + |
3403 | 3349 | // set up default players order for each library type |
3404 | 3350 | this.defaultPlayers['video/x-flv'] = ['Kplayer', 'Vlc']; |
3405 | 3351 | this.defaultPlayers['video/h264'] = ['Native', 'Kplayer', 'Vlc']; |
3406 | | - |
| 3352 | + |
3407 | 3353 | this.defaultPlayers['video/ogg'] = ['Native', 'Vlc', 'Java', 'Generic']; |
| 3354 | + this.defaultPlayers['video/webm'] = ['Native', 'Vlc']; |
3408 | 3355 | this.defaultPlayers['application/ogg'] = ['Native', 'Vlc', 'Java', 'Generic']; |
3409 | 3356 | this.defaultPlayers['audio/ogg'] = ['Native', 'Vlc', 'Java' ]; |
3410 | 3357 | this.defaultPlayers['video/mp4'] = ['Vlc']; |
3411 | 3358 | this.defaultPlayers['video/mpeg'] = ['Vlc']; |
3412 | 3359 | this.defaultPlayers['video/x-msvideo'] = ['Vlc']; |
3413 | | - |
| 3360 | + |
3414 | 3361 | this.defaultPlayers['text/html'] = ['Html']; |
3415 | 3362 | this.defaultPlayers['image/jpeg'] = ['Html']; |
3416 | 3363 | this.defaultPlayers['image/png'] = ['Html']; |
3417 | 3364 | this.defaultPlayers['image/svg'] = ['Html']; |
3418 | | - |
| 3365 | + |
3419 | 3366 | }, |
3420 | | - |
| 3367 | + |
3421 | 3368 | /** |
3422 | | - * Adds a Player to the player list |
3423 | | - * |
3424 | | - * @param {Object} player Player object to be added |
3425 | | - */ |
| 3369 | + * Adds a Player to the player list |
| 3370 | + * |
| 3371 | + * @param {Object} |
| 3372 | + * player Player object to be added |
| 3373 | + */ |
3426 | 3374 | addPlayer: function( player ) { |
3427 | 3375 | for ( var i = 0; i < this.players.length; i++ ) { |
3428 | 3376 | if ( this.players[i].id == player.id ) { |
— | — | @@ -3429,12 +3377,12 @@ |
3430 | 3378 | return ; |
3431 | 3379 | } |
3432 | 3380 | } |
3433 | | - |
3434 | | - |
| 3381 | + |
| 3382 | + |
3435 | 3383 | // Add the player: |
3436 | 3384 | this.players.push( player ); |
3437 | 3385 | }, |
3438 | | - |
| 3386 | + |
3439 | 3387 | /** |
3440 | 3388 | * Checks if a player is supported by id |
3441 | 3389 | */ |
— | — | @@ -3446,14 +3394,14 @@ |
3447 | 3395 | } |
3448 | 3396 | return false; |
3449 | 3397 | }, |
3450 | | - |
| 3398 | + |
3451 | 3399 | /** |
3452 | | - * get players that support a given mimeType |
3453 | | - * |
3454 | | - * @param {String} mimeType Mime type of player set |
3455 | | - * @return {Array} |
3456 | | - * Array of players that support a the requested mime type |
3457 | | - */ |
| 3400 | + * get players that support a given mimeType |
| 3401 | + * |
| 3402 | + * @param {String} |
| 3403 | + * mimeType Mime type of player set |
| 3404 | + * @return {Array} Array of players that support a the requested mime type |
| 3405 | + */ |
3458 | 3406 | getMIMETypePlayers: function( mimeType ) { |
3459 | 3407 | var mimePlayers = new Array(); |
3460 | 3408 | var _this = this; |
— | — | @@ -3469,17 +3417,16 @@ |
3470 | 3418 | } |
3471 | 3419 | return mimePlayers; |
3472 | 3420 | }, |
3473 | | - |
| 3421 | + |
3474 | 3422 | /** |
3475 | | - * Default player for a given mime type |
3476 | | - * |
3477 | | - * @param {String} mimeType Mime type of the requested player |
3478 | | - * @return |
3479 | | - * Player for mime type |
3480 | | - * null if no player found |
3481 | | - */ |
| 3423 | + * Default player for a given mime type |
| 3424 | + * |
| 3425 | + * @param {String} |
| 3426 | + * mimeType Mime type of the requested player |
| 3427 | + * @return Player for mime type null if no player found |
| 3428 | + */ |
3482 | 3429 | defaultPlayer : function( mimeType ) { |
3483 | | - //mw.log( "get defaultPlayer for " + mimeType ); |
| 3430 | + // mw.log( "get defaultPlayer for " + mimeType ); |
3484 | 3431 | var mimePlayers = this.getMIMETypePlayers( mimeType ); |
3485 | 3432 | if ( mimePlayers.length > 0 ) |
3486 | 3433 | { |
— | — | @@ -3492,26 +3439,29 @@ |
3493 | 3440 | // (it will be chosen according to the defaultPlayers list |
3494 | 3441 | return mimePlayers[0]; |
3495 | 3442 | } |
3496 | | - //mw.log( 'No default player found for ' + mimeType ); |
| 3443 | + // mw.log( 'No default player found for ' + mimeType ); |
3497 | 3444 | return null; |
3498 | 3445 | }, |
3499 | | - |
| 3446 | + |
3500 | 3447 | /** |
3501 | | - * Sets the format preference. |
3502 | | - * |
3503 | | - * @param {String} mimeFormat Prefered format |
3504 | | - */ |
| 3448 | + * Sets the format preference. |
| 3449 | + * |
| 3450 | + * @param {String} |
| 3451 | + * mimeFormat Prefered format |
| 3452 | + */ |
3505 | 3453 | setFormatPreference : function ( mimeFormat ) { |
3506 | 3454 | this.preference['format_preference'] = mimeFormat; |
3507 | | - mw.setUserConfig( 'playerPref', this.preference); |
| 3455 | + mw.setUserConfig( 'playerPref', this.preference); |
3508 | 3456 | }, |
3509 | | - |
| 3457 | + |
3510 | 3458 | /** |
3511 | | - * Sets the player preference |
3512 | | - * |
3513 | | - * @param {String} playerId Prefered player id |
3514 | | - * @param {String} mimeType Mime type for the associated player stream |
3515 | | - */ |
| 3459 | + * Sets the player preference |
| 3460 | + * |
| 3461 | + * @param {String} |
| 3462 | + * playerId Prefered player id |
| 3463 | + * @param {String} |
| 3464 | + * mimeType Mime type for the associated player stream |
| 3465 | + */ |
3516 | 3466 | setPlayerPreference : function( playerId, mimeType ) { |
3517 | 3467 | var selectedPlayer = null; |
3518 | 3468 | for ( var i = 0; i < this.players.length; i++ ) { |
— | — | @@ -3525,7 +3475,7 @@ |
3526 | 3476 | } |
3527 | 3477 | // Update All the player instances: |
3528 | 3478 | if ( selectedPlayer ) { |
3529 | | - var playerList = mw.playerManager.getPlayerList(); |
| 3479 | + var playerList = mw.playerManager.getPlayerList(); |
3530 | 3480 | for ( var i = 0; i < playerList.length; i++ ) { |
3531 | 3481 | var embed = $j( '#' + playerList[i] ).get( 0 ); |
3532 | 3482 | if ( embed.mediaElement.selectedSource && ( embed.mediaElement.selectedSource.mimeType == mimeType ) ) |
— | — | @@ -3536,11 +3486,11 @@ |
3537 | 3487 | } |
3538 | 3488 | } |
3539 | 3489 | }, |
3540 | | - |
| 3490 | + |
3541 | 3491 | /** |
3542 | | - * Loads the user preference settings from a cookie |
3543 | | - */ |
3544 | | - loadPreferences : function ( ) { |
| 3492 | + * Loads the user preference settings from a cookie |
| 3493 | + */ |
| 3494 | + loadPreferences : function ( ) { |
3545 | 3495 | this.preference = { }; |
3546 | 3496 | // see if we have a cookie set to a clientSupported type: |
3547 | 3497 | preferenceConfig = mw.getUserConfig( 'playerPref' ); |
— | — | @@ -3559,78 +3509,84 @@ |
3560 | 3510 | |
3561 | 3511 | // List of players supported |
3562 | 3512 | players: null, |
3563 | | - |
| 3513 | + |
3564 | 3514 | // Detect flag for completion |
3565 | 3515 | detect_done:false, |
3566 | | - |
| 3516 | + |
3567 | 3517 | /** |
3568 | | - * Runs the detect method and update the detect_done flag |
3569 | | - * @constructor |
3570 | | - */ |
| 3518 | + * Runs the detect method and update the detect_done flag |
| 3519 | + * |
| 3520 | + * @constructor |
| 3521 | + */ |
3571 | 3522 | init: function() { |
3572 | 3523 | // detect supported types |
3573 | 3524 | this.detect(); |
3574 | 3525 | this.detect_done = true; |
3575 | 3526 | }, |
3576 | | - |
| 3527 | + |
3577 | 3528 | /** |
3578 | | - * If the browsers supports a given mimetype |
3579 | | - * |
3580 | | - * @param {String} mimeType Mime type for browser plug-in check |
3581 | | - */ |
| 3529 | + * If the browsers supports a given mimetype |
| 3530 | + * |
| 3531 | + * @param {String} |
| 3532 | + * mimeType Mime type for browser plug-in check |
| 3533 | + */ |
3582 | 3534 | supportedMimeType: function( mimeType ) { |
3583 | 3535 | for ( var i =0; i < navigator.plugins.length; i++ ) { |
3584 | 3536 | var plugin = navigator.plugins[i]; |
3585 | 3537 | if ( typeof plugin[ mimeType ] != "undefined" ) |
3586 | | - return true; |
| 3538 | + return true; |
3587 | 3539 | } |
3588 | 3540 | return false; |
3589 | 3541 | }, |
3590 | | - |
| 3542 | + |
3591 | 3543 | /** |
3592 | | - * Detects what plug-ins the client supports |
3593 | | - */ |
| 3544 | + * Detects what plug-ins the client supports |
| 3545 | + */ |
3594 | 3546 | detect: function() { |
3595 | 3547 | mw.log( "embedPlayer: running detect" ); |
3596 | 3548 | this.players = new mediaPlayers(); |
3597 | 3549 | // every browser supports html rendering: |
3598 | 3550 | this.players.addPlayer( htmlPlayer ); |
3599 | | - // In Mozilla, navigator.javaEnabled() only tells us about preferences, we need to |
| 3551 | + // In Mozilla, navigator.javaEnabled() only tells us about preferences, |
| 3552 | + // we need to |
3600 | 3553 | // search navigator.mimeTypes to see if it's installed |
3601 | 3554 | try{ |
3602 | 3555 | var javaEnabled = navigator.javaEnabled(); |
3603 | 3556 | } catch ( e ){ |
3604 | | - |
| 3557 | + |
3605 | 3558 | } |
3606 | 3559 | // Some browsers filter out duplicate mime types, hiding some plugins |
3607 | 3560 | var uniqueMimesOnly = $j.browser.opera || $j.browser.safari; |
3608 | | - |
3609 | | - // Opera will switch off javaEnabled in preferences if java can't be found. |
3610 | | - // And it doesn't register an application/x-java-applet mime type like Mozilla does. |
| 3561 | + |
| 3562 | + // Opera will switch off javaEnabled in preferences if java can't be |
| 3563 | + // found. |
| 3564 | + // And it doesn't register an application/x-java-applet mime type like |
| 3565 | + // Mozilla does. |
3611 | 3566 | if ( javaEnabled && ( navigator.appName == 'Opera' ) ) { |
3612 | 3567 | this.players.addPlayer( cortadoPlayer ); |
3613 | 3568 | } |
3614 | | - |
| 3569 | + |
3615 | 3570 | // ActiveX plugins |
3616 | 3571 | if ( $j.browser.msie ) { |
3617 | 3572 | // check for flash |
3618 | 3573 | if ( this.testActiveX( 'ShockwaveFlash.ShockwaveFlash' ) ) { |
3619 | 3574 | this.players.addPlayer( kplayer ); |
3620 | | - //this.players.addPlayer( flowPlayer ); |
| 3575 | + // this.players.addPlayer( flowPlayer ); |
3621 | 3576 | } |
3622 | 3577 | // VLC |
3623 | 3578 | if ( this.testActiveX( 'VideoLAN.VLCPlugin.2' ) ) { |
3624 | 3579 | this.players.addPlayer( vlcPlayer ); |
3625 | 3580 | } |
3626 | | - |
| 3581 | + |
3627 | 3582 | // Java ActiveX |
3628 | 3583 | if ( this.testActiveX( 'JavaWebStart.isInstalled' ) ) { |
3629 | 3584 | this.players.addPlayer( cortadoPlayer ); |
3630 | 3585 | } |
3631 | | - |
| 3586 | + |
3632 | 3587 | // quicktime (currently off) |
3633 | | - // if ( this.testActiveX( 'QuickTimeCheckObject.QuickTimeCheck.1' ) ) |
3634 | | - // this.players.addPlayer(quicktimeActiveXPlayer); |
| 3588 | + // if ( this.testActiveX( |
| 3589 | + // 'QuickTimeCheckObject.QuickTimeCheck.1' ) ) |
| 3590 | + // this.players.addPlayer(quicktimeActiveXPlayer); |
3635 | 3591 | } |
3636 | 3592 | // <video> element |
3637 | 3593 | if ( typeof HTMLVideoElement == 'object' // Firefox, Safari |
— | — | @@ -3642,23 +3598,24 @@ |
3643 | 3599 | if( dummyvid.canPlayType ) { |
3644 | 3600 | // Add the webm player |
3645 | 3601 | if( dummyvid.canPlayType('video/webm; codecs="vp8, vorbis"') ){ |
3646 | | - this.players.addPlayer( webmNativePlayer ); |
| 3602 | + this.players.addPlayer( webmNativePlayer ); |
3647 | 3603 | } |
3648 | | - |
| 3604 | + |
3649 | 3605 | // Test for h264: |
3650 | 3606 | if ( dummyvid.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"' ) ) { |
3651 | 3607 | this.players.addPlayer( h264NativePlayer ); |
3652 | 3608 | } |
3653 | | - // For now if Android assume we support h264Native (FIXME test on real devices ) |
| 3609 | + // For now if Android assume we support h264Native (FIXME |
| 3610 | + // test on real devices ) |
3654 | 3611 | if ( mw.isAndroid2() ){ |
3655 | 3612 | this.players.addPlayer( h264NativePlayer ); |
3656 | 3613 | } |
3657 | | - |
3658 | | - // Test for ogg |
| 3614 | + |
| 3615 | + // Test for ogg |
3659 | 3616 | if ( dummyvid.canPlayType( 'video/ogg; codecs="theora,vorbis"' ) ) { |
3660 | 3617 | this.players.addPlayer( oggNativePlayer ); |
3661 | 3618 | // older versions of safari do not support canPlayType, |
3662 | | - // but xiph qt registers mimetype via quicktime plugin |
| 3619 | + // but xiph qt registers mimetype via quicktime plugin |
3663 | 3620 | } else if ( this.supportedMimeType( 'video/ogg' ) ) { |
3664 | 3621 | this.players.addPlayer( oggNativePlayer ); |
3665 | 3622 | } |
— | — | @@ -3666,8 +3623,8 @@ |
3667 | 3624 | } catch ( e ) { |
3668 | 3625 | mw.log( 'could not run canPlayType ' + e ); |
3669 | 3626 | } |
3670 | | - } |
3671 | | - |
| 3627 | + } |
| 3628 | + |
3672 | 3629 | // "navigator" plugins |
3673 | 3630 | if ( navigator.mimeTypes && navigator.mimeTypes.length > 0 ) { |
3674 | 3631 | for ( var i = 0; i < navigator.mimeTypes.length; i++ ) { |
— | — | @@ -3676,7 +3633,7 @@ |
3677 | 3634 | if ( semicolonPos > -1 ) { |
3678 | 3635 | type = type.substr( 0, semicolonPos ); |
3679 | 3636 | } |
3680 | | - // mw.log('on type: '+type); |
| 3637 | + // mw.log( 'on type: ' + type ); |
3681 | 3638 | var pluginName = navigator.mimeTypes[i].enabledPlugin ? navigator.mimeTypes[i].enabledPlugin.name : ''; |
3682 | 3639 | if ( !pluginName ) { |
3683 | 3640 | // In case it is null or undefined |
— | — | @@ -3686,22 +3643,22 @@ |
3687 | 3644 | this.players.addPlayer( vlcPlayer ); |
3688 | 3645 | continue; |
3689 | 3646 | } |
3690 | | - |
| 3647 | + |
3691 | 3648 | if ( type == 'application/x-java-applet' ) { |
3692 | 3649 | this.players.addPlayer( cortadoPlayer ); |
3693 | 3650 | continue; |
3694 | 3651 | } |
3695 | | - |
| 3652 | + |
3696 | 3653 | if ( (type == 'video/mpeg' || type=='video/x-msvideo') && |
3697 | | - pluginName.toLowerCase() == 'vlc multimedia plugin' ) { |
3698 | | - this.players.addPlayer( vlcMozillaPlayer ); |
3699 | | - } |
3700 | | - |
| 3654 | + pluginName.toLowerCase() == 'vlc multimedia plugin' ) { |
| 3655 | + this.players.addPlayer( vlcMozillaPlayer ); |
| 3656 | + } |
| 3657 | + |
3701 | 3658 | if ( type == 'application/ogg' ) { |
3702 | 3659 | if ( pluginName.toLowerCase() == 'vlc multimedia plugin' ) { |
3703 | 3660 | this.players.addPlayer( vlcMozillaPlayer ); |
3704 | 3661 | // else if ( pluginName.indexOf( 'QuickTime' ) > -1 ) |
3705 | | - // this.players.addPlayer(quicktimeMozillaPlayer); |
| 3662 | + // this.players.addPlayer(quicktimeMozillaPlayer); |
3706 | 3663 | } else { |
3707 | 3664 | this.players.addPlayer( oggPluginPlayer ); |
3708 | 3665 | } |
— | — | @@ -3715,12 +3672,12 @@ |
3716 | 3673 | continue; |
3717 | 3674 | } |
3718 | 3675 | } |
3719 | | - |
| 3676 | + |
3720 | 3677 | if ( type == 'application/x-shockwave-flash' ) { |
3721 | | - |
| 3678 | + |
3722 | 3679 | this.players.addPlayer( kplayer ); |
3723 | | - //this.players.addPlayer( flowPlayer ); |
3724 | | - |
| 3680 | + // this.players.addPlayer( flowPlayer ); |
| 3681 | + |
3725 | 3682 | // check version to add omtk: |
3726 | 3683 | if( navigator.plugins["Shockwave Flash"] ){ |
3727 | 3684 | var flashDescription = navigator.plugins["Shockwave Flash"].description; |
— | — | @@ -3733,18 +3690,19 @@ |
3734 | 3691 | } |
3735 | 3692 | } |
3736 | 3693 | } |
3737 | | - |
| 3694 | + |
3738 | 3695 | // Allow extensions to detect and add their own "players" |
3739 | 3696 | mw.log("trigger::embedPlayerUpdateMediaPlayersEvent"); |
3740 | 3697 | $j( mw ).trigger( 'embedPlayerUpdateMediaPlayersEvent' , this.players ); |
3741 | | - |
| 3698 | + |
3742 | 3699 | }, |
3743 | | - |
| 3700 | + |
3744 | 3701 | /** |
3745 | | - * Test IE for activeX by name |
3746 | | - * |
3747 | | - * @param {String} name Name of ActiveXObject to look for |
3748 | | - */ |
| 3702 | + * Test IE for activeX by name |
| 3703 | + * |
| 3704 | + * @param {String} |
| 3705 | + * name Name of ActiveXObject to look for |
| 3706 | + */ |
3749 | 3707 | testActiveX : function ( name ) { |
3750 | 3708 | mw.log("EmbedPlayer::detect: test testActiveX: " + name); |
3751 | 3709 | var hasObj = true; |
Index: branches/MwEmbedStandAlone/modules/EmbedPlayer/mw.IFramePlayerApiClient.js |
— | — | @@ -1,53 +1,129 @@ |
2 | 2 | /** |
3 | | -* iFrame api mapping support |
4 | | -* |
5 | | -* Client side ( binds a given iFrames to expose the player api ) |
| 3 | +* iFrame api mapping support |
| 4 | +* |
| 5 | +* Client side ( binds a given iFrames to expose the player api ) |
6 | 6 | */ |
7 | | - |
8 | | -// Add the jQuery binding |
9 | | -( function( $ ) { |
10 | | - $.fn.iFramePlayer = function( options ){ |
11 | | - |
12 | | - var iframe = $(this.selector).get(0); |
13 | | - var cat = new mw.IFramePlayerApiClient( iframe, options ); |
14 | | - }; |
15 | | - |
16 | | -} )( jQuery ); |
17 | | - |
18 | | -mw.IFramePlayerApiClient = function( iframe, options ){ |
19 | | - return this.init( iframe , options); |
| 7 | +mw.IFramePlayerApiClient = function( iframe, playerProxy, options ){ |
| 8 | + return this.init( iframe , playerProxy, options); |
20 | 9 | } |
21 | 10 | mw.IFramePlayerApiClient.prototype = { |
22 | | - exportedMethods: [ |
| 11 | + 'exportedMethods': [ |
23 | 12 | 'play', |
24 | 13 | 'pause' |
25 | 14 | ], |
26 | | - exportedBindings: [ |
27 | | - 'ended' |
28 | | - ], |
29 | | - init: function( iframe , options ){ |
| 15 | + // Local store of the post message ( not updated by user js ) |
| 16 | + '_prevPlayerProxy': {}, |
| 17 | + // Stores the current playerProxy ( can be updated by user js ) |
| 18 | + 'init': function( iframe , playerProxy, options ){ |
30 | 19 | this.iframe = iframe; |
31 | | - if( !options.targetOrigin ){ |
32 | | - mw.log("Error: IFramePlayerApiClient please supply a target origin"); |
33 | | - return ; |
34 | | - } else { |
35 | | - this.targetOrigin = options.targetOrigin; |
36 | | - } |
37 | | - this.addPlayerApi(); |
| 20 | + this.playerProxy = playerProxy; |
| 21 | + // Set the iframe server |
| 22 | + var srcParts = mw.parseUri( mw.absoluteUrl( $j(this.iframe).attr('src') ) ); |
| 23 | + this.iframeServer = srcParts.protocol + '://' + srcParts.authority; |
| 24 | + this.addPlayerSendApi(); |
| 25 | + this.addPlayerReciveApi(); |
38 | 26 | }, |
39 | | - addPlayerApi: function(){ |
| 27 | + 'addPlayerSendApi': function(){ |
40 | 28 | var _this = this; |
41 | 29 | $j.each( this.exportedMethods, function(na, method){ |
42 | | - _this.iframe[ method ] = function(){ |
43 | | - _this.postMethod( method, arguments ); |
| 30 | + _this.playerProxy[ method ] = function(){ |
| 31 | + _this.postMessage( { |
| 32 | + 'method' : method, |
| 33 | + 'args' : arguments |
| 34 | + } ); |
44 | 35 | }; |
45 | 36 | }); |
46 | 37 | }, |
47 | | - postMethod: function( method , args){ |
48 | | - mw.log("IFramePlayer:: Post method: '" + method + "' with " + args.length + " arguments"); |
49 | | - var methodMsg = { |
50 | | - 'method' : method |
| 38 | + 'addPlayerReciveApi': function(){ |
| 39 | + var _this = this; |
| 40 | + $j.receiveMessage( function( event ){ |
| 41 | + _this.hanldeReciveMsg( event ) |
| 42 | + }); |
| 43 | + }, |
| 44 | + /** |
| 45 | + * Handle received events |
| 46 | + */ |
| 47 | + 'hanldeReciveMsg': function( event ){ |
| 48 | + var _this = this; |
| 49 | + //mw.log("IframePlayerApiClient:: hanldeReciveMsg "); |
| 50 | + // Confirm the event is coming for the target host: |
| 51 | + if( event.origin != this.iframeServer){ |
| 52 | + mw.log("Skip msg from host does not match iFrame player: " + event.origin + |
| 53 | + ' != iframe Server: ' + this.iframeServer ) |
| 54 | + return ; |
51 | 55 | }; |
52 | | - this.iframe.contentWindow.postMessage( JSON.stringify( methodMsg ), this.targetOrigin ); |
| 56 | + // Decode the message |
| 57 | + var msgObject = JSON.parse( event.data ); |
| 58 | + var playerAttributes = mw.getConfig( 'EmbedPlayer.Attributes' ); |
| 59 | + // Before we update local attributes check that the object has not been updated by user js |
| 60 | + for( var attrName in playerAttributes ){ |
| 61 | + if( attrName != 'id' ){ |
| 62 | + if( _this._prevPlayerProxy[ attrName ] != _this.playerProxy[ attrName ] ){ |
| 63 | + mw.log( "IFramePlayerApiClient:: User js update:" + attrName + ' set to: ' + this.playerProxy[ attrName ] + ' != old: ' + _this._prevPlayerProxy[ attrName ] ); |
| 64 | + // Send the updated attribute back to the iframe: |
| 65 | + _this.postMessage({ |
| 66 | + 'attrName' : attrName, |
| 67 | + 'attrValue' : _this.playerProxy[ attrName ] |
| 68 | + }); |
| 69 | + } |
| 70 | + } |
| 71 | + } |
| 72 | + // Update any attributes |
| 73 | + if( msgObject.attributes ){ |
| 74 | + for( var i in msgObject.attributes ){ |
| 75 | + if( i != 'id' && i != 'class' && i != 'style' ){ |
| 76 | + try{ |
| 77 | + this.playerProxy[ i ] = msgObject.attributes[i]; |
| 78 | + this._prevPlayerProxy[i] = msgObject.attributes[i]; |
| 79 | + } catch( e ){ |
| 80 | + mw.log("Error could not set:" + i ); |
| 81 | + } |
| 82 | + } |
| 83 | + } |
| 84 | + } |
| 85 | + // Trigger any binding events |
| 86 | + if( typeof msgObject.triggerName != 'undefined' && msgObject.triggerArgs != 'undefined') { |
| 87 | + mw.log('IFramePlayerApiClient:: trigger: ' + msgObject.triggerName ); |
| 88 | + $j( _this.playerProxy ).trigger( msgObject.triggerName, msgObject.triggerArgs ); |
| 89 | + } |
| 90 | + // @@TODO:: Allow extending modules to wrap these api events ( kaltura kdp javascript emulation ? ) |
| 91 | + }, |
| 92 | + 'postMessage': function( msgObj ){ |
| 93 | + mw.log( "IFramePlayerApiClient:: postMessage(): " + JSON.stringify( msgObj ) ); |
| 94 | + $j.postMessage( |
| 95 | + JSON.stringify( msgObj ), |
| 96 | + mw.absoluteUrl( $j( this.iframe ).attr('src') ), |
| 97 | + this.iframe.contentWindow |
| 98 | + ); |
53 | 99 | } |
54 | 100 | }; |
| 101 | + |
| 102 | +//Add the jQuery binding |
| 103 | +( function( $ ) { |
| 104 | + $.fn.iFramePlayer = function( options ){ |
| 105 | + if( ! this.selector ){ |
| 106 | + this.selector = $j( this ).get(0); |
| 107 | + } |
| 108 | + // Append '_ifp' ( iframe player ) to id of real iframe so that 'id', and 'src' attributes don't conflict |
| 109 | + var originalIframeId = ( $( this.selector ).attr( 'id' ) )? $( this.selector ).attr( 'id' ) : Math.floor( 9999999 * Math.random() ); |
| 110 | + var iframePlayerId = originalIframeId + '_ifp' ; // here we use random to generate a unique id |
| 111 | + // Append the div element proxy after the iframe |
| 112 | + $j( this.selector ) |
| 113 | + .attr('id', iframePlayerId) |
| 114 | + .after( |
| 115 | + $('<div />') |
| 116 | + .attr( 'id', originalIframeId ) |
| 117 | + ); |
| 118 | + var playerProxy = $j( '#' + originalIframeId ).get(0); |
| 119 | + var iframe = $j('#' + iframePlayerId).get(0); |
| 120 | + if(!iframe){ |
| 121 | + mw.log("Error invalide iFramePlayer request"); |
| 122 | + return false; |
| 123 | + } |
| 124 | + if( !iframe['playerApi'] ){ |
| 125 | + iframe['playerApi'] = new mw.IFramePlayerApiClient( iframe, playerProxy, options ); |
| 126 | + } |
| 127 | + // Return the player proxy for chaining player events / attributes |
| 128 | + return $j( playerProxy ); |
| 129 | + }; |
| 130 | +} )( jQuery ); |
\ No newline at end of file |
Index: branches/MwEmbedStandAlone/modules/EmbedPlayer/skins/mw.PlayerControlBuilder.js |
— | — | @@ -796,8 +796,7 @@ |
797 | 797 | 'id' : 'ffwarn_' + embedPlayer.id, |
798 | 798 | 'type' : "checkbox", |
799 | 799 | 'name' : 'ffwarn_' + embedPlayer.id |
800 | | - }) |
801 | | - .click( function() { |
| 800 | + }).click( function() { |
802 | 801 | mw.log("WarningBindinng:: set " + preferenceId + ' to hidewarning ' ); |
803 | 802 | // Set up a cookie for 30 days: |
804 | 803 | $j.cookie( preferenceId, 'hidewarning', { expires: 30 } ); |
Index: branches/MwEmbedStandAlone/modules/EmbedPlayer/mw.EmbedPlayerNative.js |
— | — | @@ -1,37 +1,34 @@ |
2 | 2 | /** |
3 | 3 | * Native embed library: |
4 | | -* |
| 4 | +* |
5 | 5 | * Enables embedPlayer support for native html5 browser playback system |
6 | 6 | */ |
7 | 7 | mw.EmbedPlayerNative = { |
8 | 8 | |
9 | 9 | //Instance Name |
10 | 10 | instanceOf: 'Native', |
11 | | - |
12 | | - // Counts the number of times we tried to access the video element |
| 11 | + |
| 12 | + // Counts the number of times we tried to access the video element |
13 | 13 | grab_try_count:0, |
14 | | - |
15 | | - // Flag to only load the video ( not play it ) |
| 14 | + |
| 15 | + // Flag to only load the video ( not play it ) |
16 | 16 | onlyLoadFlag:false, |
17 | | - |
18 | | - //Callback fired once video is "loaded" |
| 17 | + |
| 18 | + //Callback fired once video is "loaded" |
19 | 19 | onLoadedCallback: null, |
20 | | - |
21 | | - //For retrying a player embed with a distinct url |
22 | | - // NOTE: this bug workaround may no longer be applicable |
23 | | - urlAppend:'', |
24 | | - |
25 | | - // The previous "currentTime" to sniff seek actions |
| 20 | + |
| 21 | + // The previous "currentTime" to sniff seek actions |
26 | 22 | // NOTE the bug where onSeeked does not seem fire consistently may no longer be applicable |
27 | 23 | prevCurrentTime: -1, |
28 | | - |
| 24 | + |
29 | 25 | // Store the progress event ( updated during monitor ) |
30 | 26 | progressEventData: null, |
31 | | - |
32 | | - // If the media loaded event has been fired |
| 27 | + |
| 28 | + // If the media loaded event has been fired |
33 | 29 | mediaLoadedFlag: null, |
34 | | - |
35 | | - // All the native events per: |
| 30 | + |
| 31 | + |
| 32 | + // All the native events per: |
36 | 33 | // http://www.w3.org/TR/html5/video.html#mediaevents |
37 | 34 | nativeEvents : [ |
38 | 35 | 'loadstart', |
— | — | @@ -68,49 +65,49 @@ |
69 | 66 | 'overlays' : true |
70 | 67 | }, |
71 | 68 | |
72 | | - /** |
73 | | - * updates the supported features given the "type of player" |
| 69 | + /** |
| 70 | + * Updates the supported features given the "type of player" |
74 | 71 | */ |
75 | 72 | updateFeatureSupport: function(){ |
76 | 73 | // iWhatever devices appear to have a broken |
77 | 74 | // dom overlay implementation of video atm. (hopefully iphone OS 4 fixes this ) |
78 | | - if( mw.isMobileHTML5() ) { |
| 75 | + if( mw.isHTML5FallForwardNative() ) { |
79 | 76 | this.supports.overlays = false; |
80 | 77 | } |
81 | 78 | }, |
82 | | - |
| 79 | + |
83 | 80 | /** |
84 | 81 | * Return the embed code |
85 | 82 | */ |
86 | 83 | doEmbedHTML : function () { |
87 | 84 | var _this = this; |
88 | | - |
89 | | - // Reset some play state flags: |
| 85 | + |
| 86 | + // Reset some play state flags: |
90 | 87 | _this.bufferStartFlag = false; |
91 | 88 | _this.bufferEndFlag = false; |
92 | | - |
| 89 | + |
93 | 90 | mw.log( "native play url:" + this.getSrc() + ' startOffset: ' + this.start_ntp + ' end: ' + this.end_ntp ); |
94 | | - |
| 91 | + |
95 | 92 | // Check if using native controls and already the "pid" is already in the DOM |
96 | 93 | if( this.useNativePlayerControls() && $j( '#' + this.pid ).length && |
97 | 94 | typeof $j( '#' + this.pid ).get(0).play != 'undefined' ) { |
98 | 95 | _this.postEmbedJS(); |
99 | 96 | return ; |
100 | 97 | } |
101 | | - |
| 98 | + |
102 | 99 | $j( this ).html( |
103 | 100 | _this.getNativePlayerHtml() |
104 | 101 | ); |
105 | | - |
106 | | - // Directly run postEmbedJS ( if playerElement is not available it will retry ) |
| 102 | + |
| 103 | + // Directly run postEmbedJS ( if playerElement is not available it will retry ) |
107 | 104 | _this.postEmbedJS(); |
108 | 105 | }, |
109 | | - |
| 106 | + |
110 | 107 | /** |
111 | 108 | * Get the native player embed code. |
112 | | - * |
| 109 | + * |
113 | 110 | * @param {object} playerAttribtues Attributes to be override in function call |
114 | | - * @return {object} cssSet css to apply to the player |
| 111 | + * @return {object} cssSet css to apply to the player |
115 | 112 | */ |
116 | 113 | getNativePlayerHtml: function( playerAttribtues, cssSet ){ |
117 | 114 | if( !playerAttribtues) { |
— | — | @@ -119,46 +116,46 @@ |
120 | 117 | // Update required attributes |
121 | 118 | if( !playerAttribtues[ 'id'] ) playerAttribtues['id'] = this.pid; |
122 | 119 | if( !playerAttribtues['src'] ) playerAttribtues['src'] = this.getSrc(); |
123 | | - |
| 120 | + |
124 | 121 | // If autoplay pass along to attribute ( needed for iPad / iPod no js autoplay support |
125 | 122 | if( this.autoplay ) { |
126 | 123 | playerAttribtues['autoplay'] = 'true'; |
127 | 124 | } |
128 | | - |
129 | | - |
| 125 | + |
| 126 | + |
130 | 127 | if( !cssSet ){ |
131 | 128 | cssSet = {}; |
132 | 129 | } |
133 | 130 | // Set default width height to 100% of parent container |
134 | 131 | if( !cssSet['width'] ) cssSet['width'] = '100%'; |
135 | 132 | if( !cssSet['height'] ) cssSet['height'] = '100%'; |
136 | | - |
| 133 | + |
137 | 134 | // Also need to set the loop param directly for iPad / iPod |
138 | 135 | if( this.loop ) { |
139 | 136 | playerAttribtues['loop'] = 'true'; |
140 | 137 | } |
141 | | - |
| 138 | + |
142 | 139 | var tagName = ( this.isAudio() ) ? 'audio' : 'video'; |
143 | | - |
| 140 | + |
144 | 141 | return $j( '<' + tagName + ' />' ) |
145 | | - // Add the special nativeEmbedPlayer to avoid any rewrites of of this video tag. |
| 142 | + // Add the special nativeEmbedPlayer to avoid any rewrites of of this video tag. |
146 | 143 | .addClass( 'nativeEmbedPlayerPid' ) |
147 | 144 | .attr( playerAttribtues ) |
148 | 145 | .css( cssSet ); |
149 | 146 | }, |
150 | | - |
| 147 | + |
151 | 148 | /** |
152 | | - * Post element javascript, binds event listeners and starts monitor |
| 149 | + * Post element javascript, binds event listeners and starts monitor |
153 | 150 | */ |
154 | 151 | postEmbedJS: function() { |
155 | 152 | var _this = this; |
156 | 153 | mw.log( "f:native:postEmbedJS:" ); |
157 | 154 | |
158 | | - // Setup local pointer: |
| 155 | + // Setup local pointer: |
159 | 156 | var vid = this.getPlayerElement(); |
160 | 157 | // Apply media element bindings: |
161 | 158 | this.applyMediaElementBindings(); |
162 | | - |
| 159 | + |
163 | 160 | // Check for load flag |
164 | 161 | if ( this.onlyLoadFlag ) { |
165 | 162 | vid.pause(); |
— | — | @@ -167,14 +164,14 @@ |
168 | 165 | // Issue play request |
169 | 166 | vid.play(); |
170 | 167 | } |
171 | | - |
| 168 | + |
172 | 169 | setTimeout( function() { |
173 | 170 | _this.monitor(); |
174 | 171 | }, 100 ); |
175 | 172 | }, |
176 | | - |
| 173 | + |
177 | 174 | /** |
178 | | - * Apply media element bindings |
| 175 | + * Apply media element bindings |
179 | 176 | */ |
180 | 177 | applyMediaElementBindings: function(){ |
181 | 178 | var _this = this; |
— | — | @@ -183,35 +180,36 @@ |
184 | 181 | mw.log( " Error: applyMediaElementBindings without player elemnet"); |
185 | 182 | return ; |
186 | 183 | } |
187 | | - |
188 | | - // Bind events to local js methods: |
189 | | - vid.addEventListener( 'canplaythrough', function() { $j( _this ).trigger('canplaythrough'); }, true); |
190 | | - vid.addEventListener( 'loadedmetadata', function() { _this.onloadedmetadata() }, true); |
191 | | - vid.addEventListener( 'progress', function( e ) { if( _this.onprogress ) { _this.onprogress( e ); } }, true); |
192 | | - vid.addEventListener( 'ended', function() { _this.onended() }, true); |
193 | | - vid.addEventListener( 'seeking', function() { _this.onSeeking() }, true); |
194 | | - vid.addEventListener( 'seeked', function() { _this.onSeeked() }, true); |
195 | | - |
196 | | - vid.addEventListener( 'pause', function() { if( _this.onPaused ) { _this.onPaused() } }, true ); |
197 | | - vid.addEventListener( 'play', function(){ _this.onPlay() }, true ); |
198 | | - vid.addEventListener( 'volumechange', function(){ _this.onVolumeChange() } , true ); |
| 184 | + $j.each( _this.nativeEvents, function( inx, eventName ){ |
| 185 | + $j( vid ).bind( eventName , function(){ |
| 186 | + if( _this._propagateEvents ){ |
| 187 | + // Check if there is local handler: |
| 188 | + if( _this['on' + eventName ] ){ |
| 189 | + _this['on' + eventName ].apply( _this, arguments ); |
| 190 | + } else { |
| 191 | + // no local handler directly propagate the event to the abstract object: |
| 192 | + $j( _this ).trigger( eventName, arguments ) |
| 193 | + } |
| 194 | + } |
| 195 | + }) |
| 196 | + }); |
199 | 197 | }, |
200 | | - |
| 198 | + |
201 | 199 | // basic monitor function to update buffer |
202 | 200 | monitor: function(){ |
203 | 201 | var _this = this; |
204 | 202 | var vid = _this.getPlayerElement(); |
205 | | - |
| 203 | + |
206 | 204 | // Update the bufferedPercent |
207 | 205 | if( vid && vid.buffered && vid.buffered.end && vid.duration ) { |
208 | 206 | this.bufferedPercent = (vid.buffered.end(0) / vid.duration); |
209 | 207 | } |
210 | 208 | _this.parent_monitor(); |
211 | 209 | }, |
212 | | - |
213 | | - |
| 210 | + |
| 211 | + |
214 | 212 | /** |
215 | | - * Issue a seeking request. |
| 213 | + * Issue a seeking request. |
216 | 214 | * |
217 | 215 | * @param {Float} percentage |
218 | 216 | */ |
— | — | @@ -220,10 +218,10 @@ |
221 | 219 | this.seeking = true; |
222 | 220 | // Run the seeking hook |
223 | 221 | $j( this.embedPlayer ).trigger( 'onSeek' ); |
224 | | - |
| 222 | + |
225 | 223 | // Run the onSeeking interface update |
226 | 224 | this.controlBuilder.onSeek(); |
227 | | - |
| 225 | + |
228 | 226 | // @@todo check if the clip is loaded here (if so we can do a local seek) |
229 | 227 | if ( this.supportsURLTimeEncoding() ) { |
230 | 228 | // Make sure we could not do a local seek instead: |
— | — | @@ -231,21 +229,22 @@ |
232 | 230 | mw.log( "do local seek " + percentage + ' is already buffered < ' + this.bufferedPercent ); |
233 | 231 | this.doNativeSeek( percentage ); |
234 | 232 | } else { |
235 | | - // We support URLTimeEncoding call parent seek: |
| 233 | + // We support URLTimeEncoding call parent seek: |
236 | 234 | this.parent_doSeek( percentage ); |
237 | 235 | } |
238 | 236 | } else if ( this.playerElement && this.playerElement.duration ) { |
239 | | - // (could also check bufferedPercent > percentage seek (and issue oggz_chop request or not) |
| 237 | + // (could also check bufferedPercent > percentage seek (and issue oggz_chop request or not) |
240 | 238 | this.doNativeSeek( percentage ); |
241 | 239 | } else { |
242 | | - // try to do a play then seek: |
| 240 | + // try to do a play then seek: |
243 | 241 | this.doPlayThenSeek( percentage ) |
244 | 242 | } |
245 | 243 | }, |
246 | | - |
| 244 | + |
247 | 245 | /** |
248 | 246 | * Do a native seek by updating the currentTime |
249 | | - * @param {float} percentage Percent to seek to of full time |
| 247 | + * @param {float} percentage |
| 248 | + * Percent to seek to of full time |
250 | 249 | */ |
251 | 250 | doNativeSeek: function( percentage ) { |
252 | 251 | var _this = this; |
— | — | @@ -257,11 +256,12 @@ |
258 | 257 | _this.monitor(); |
259 | 258 | }) |
260 | 259 | }, |
261 | | - |
| 260 | + |
262 | 261 | /** |
263 | 262 | * Seek in a existing stream |
264 | 263 | * |
265 | | - * @param {Float} percentage Percentage of the stream to seek to between 0 and 1 |
| 264 | + * @param {Float} percentage |
| 265 | + * Percentage of the stream to seek to between 0 and 1 |
266 | 266 | */ |
267 | 267 | doPlayThenSeek: function( percentage ) { |
268 | 268 | mw.log( 'native::doPlayThenSeek::' ); |
— | — | @@ -274,7 +274,7 @@ |
275 | 275 | if ( _this.playerElement && _this.playerElement.duration ) { |
276 | 276 | _this.doNativeSeek( percentage ); |
277 | 277 | } else { |
278 | | - // Try to get player for 40 seconds: |
| 278 | + // Try to get player for 40 seconds: |
279 | 279 | // (it would be nice if the onmetadata type callbacks where fired consistently) |
280 | 280 | if ( retryCount < 800 ) { |
281 | 281 | setTimeout( readyForSeek, 50 ); |
— | — | @@ -286,12 +286,14 @@ |
287 | 287 | } |
288 | 288 | readyForSeek(); |
289 | 289 | }, |
290 | | - |
| 290 | + |
291 | 291 | /** |
292 | 292 | * Set the current time with a callback |
293 | | - * |
294 | | - * @param {Float} position Seconds to set the time to |
295 | | - * @param {Function} callback Function called once time has been set. |
| 293 | + * |
| 294 | + * @param {Float} position |
| 295 | + * Seconds to set the time to |
| 296 | + * @param {Function} callback |
| 297 | + * Function called once time has been set. |
296 | 298 | */ |
297 | 299 | setCurrentTime: function( time , callback, callbackCount ) { |
298 | 300 | var _this = this; |
— | — | @@ -301,7 +303,7 @@ |
302 | 304 | if( _this.playerElement.readyState >= 1 ){ |
303 | 305 | if( _this.playerElement.currentTime == time ){ |
304 | 306 | callback(); |
305 | | - return; |
| 307 | + return; |
306 | 308 | } |
307 | 309 | var once = function( event ) { |
308 | 310 | if( callback ){ |
— | — | @@ -311,17 +313,11 @@ |
312 | 314 | }; |
313 | 315 | // Assume we will get to add the Listener before the seek is done |
314 | 316 | _this.playerElement.addEventListener( 'seeked', once, false ); |
315 | | - try { |
316 | | - _this.playerElement.currentTime = time; |
317 | | - } catch (e) { |
318 | | - mw.log("Could not seek to this point. Unbuffered point."); |
319 | | - callback(); |
320 | | - return; |
321 | | - } |
| 317 | + _this.playerElement.currentTime = time; |
322 | 318 | } else { |
323 | 319 | if( callbackCount >= 300 ){ |
324 | 320 | mw.log("Error with seek request, media never in ready state"); |
325 | | - return ; |
| 321 | + return ; |
326 | 322 | } |
327 | 323 | setTimeout( function(){ |
328 | 324 | _this.setCurrentTime( time, callback , callbackCount++); |
— | — | @@ -335,8 +331,8 @@ |
336 | 332 | getPlayerElementTime: function() { |
337 | 333 | var _this = this; |
338 | 334 | // Make sure we have .vid obj |
339 | | - this.getPlayerElement(); |
340 | | - |
| 335 | + this.getPlayerElement(); |
| 336 | + |
341 | 337 | if ( !this.playerElement ) { |
342 | 338 | mw.log( 'mwEmbedPlayer::getPlayerElementTime: ' + this.id + ' not in dom ( stop monitor)' ); |
343 | 339 | return false; |
— | — | @@ -344,19 +340,8 @@ |
345 | 341 | // Return the playerElement currentTime |
346 | 342 | return this.playerElement.currentTime; |
347 | 343 | }, |
348 | | - |
| 344 | + |
349 | 345 | /** |
350 | | - * Get video src URI |
351 | | - * appends this.urlAppend for unique urls for re-requesting src urls on broken playback |
352 | | - */ |
353 | | - getSrc: function() { |
354 | | - var src = this.parent_getSrc(); |
355 | | - if ( this.urlAppend != '' ) |
356 | | - return src + ( ( src.indexOf( '?' ) == -1 ) ? '?':'&' ) + this.urlAppend; |
357 | | - return src; |
358 | | - }, |
359 | | - |
360 | | - /** |
361 | 346 | * Pause the video playback |
362 | 347 | * calls parent_pause to update the interface |
363 | 348 | */ |
— | — | @@ -369,13 +354,13 @@ |
370 | 355 | } |
371 | 356 | } |
372 | 357 | }, |
373 | | - |
| 358 | + |
374 | 359 | /** |
375 | 360 | * Play back the video stream |
376 | | - * calls parent_play to update the interface |
| 361 | + * calls parent_play to update the interface |
377 | 362 | */ |
378 | 363 | play: function( ) { |
379 | | - |
| 364 | + |
380 | 365 | this.getPlayerElement(); |
381 | 366 | this.parent_play(); // update interface |
382 | 367 | if ( this.playerElement && this.playerElement.play ) { |
— | — | @@ -383,12 +368,12 @@ |
384 | 369 | if( this.playerElement.paused ){ |
385 | 370 | this.playerElement.play(); |
386 | 371 | } |
387 | | - // re-start the monitor: |
| 372 | + // re-start the monitor: |
388 | 373 | this.monitor(); |
389 | 374 | } |
390 | 375 | }, |
391 | 376 | /** |
392 | | - * Stop the player ( end all listeners ) |
| 377 | + * Stop the player ( end all listeners ) |
393 | 378 | */ |
394 | 379 | stop:function(){ |
395 | 380 | if( this.playerElement ){ |
— | — | @@ -396,10 +381,10 @@ |
397 | 382 | } |
398 | 383 | this.parent_stop(); |
399 | 384 | }, |
400 | | - |
| 385 | + |
401 | 386 | /** |
402 | 387 | * Toggle the Mute |
403 | | - * calls parent_toggleMute to update the interface |
| 388 | + * calls parent_toggleMute to update the interface |
404 | 389 | */ |
405 | 390 | toggleMute: function() { |
406 | 391 | this.parent_toggleMute(); |
— | — | @@ -407,7 +392,7 @@ |
408 | 393 | if ( this.playerElement ) |
409 | 394 | this.playerElement.muted = this.muted; |
410 | 395 | }, |
411 | | - |
| 396 | + |
412 | 397 | /** |
413 | 398 | * Update Volume |
414 | 399 | * |
— | — | @@ -422,37 +407,28 @@ |
423 | 408 | this.playerElement.volume = percentage; |
424 | 409 | } |
425 | 410 | }, |
426 | | - |
| 411 | + |
427 | 412 | /** |
428 | 413 | * get Volume |
429 | 414 | * |
430 | | - * @return {Float} |
| 415 | + * @return {Float} |
431 | 416 | * Audio volume between 0 and 1. |
432 | 417 | */ |
433 | | - getPlayerElementVolume: function() { |
| 418 | + getPlayerElementVolume: function() { |
434 | 419 | if ( this.getPlayerElement() ) { |
435 | 420 | return this.playerElement.volume; |
436 | 421 | } |
437 | 422 | }, |
438 | 423 | /** |
439 | 424 | * get the native muted state |
440 | | - */ |
| 425 | + */ |
441 | 426 | getPlayerElementMuted: function(){ |
442 | 427 | if ( this.getPlayerElement() ) { |
443 | 428 | return this.playerElement.muted; |
444 | 429 | } |
445 | 430 | }, |
446 | | - |
| 431 | + |
447 | 432 | /** |
448 | | - * Handle volume change are handled via "monitor" as to not do too many binding triggers per seconds. |
449 | | - */ |
450 | | - onVolumeChange: function(){ |
451 | | - //mw.log( "native::volumechange::trigger" ); |
452 | | - //this.volume = this.playerElement.volume; |
453 | | - $j( this ).trigger( 'volumechange' ); |
454 | | - }, |
455 | | - |
456 | | - /** |
457 | 433 | * Get the native media duration |
458 | 434 | */ |
459 | 435 | getNativeDuration: function() { |
— | — | @@ -460,7 +436,7 @@ |
461 | 437 | return this.playerElement.duration; |
462 | 438 | } |
463 | 439 | }, |
464 | | - |
| 440 | + |
465 | 441 | /** |
466 | 442 | * load the video stream with a callback fired once the video is "loaded" |
467 | 443 | * |
— | — | @@ -481,80 +457,80 @@ |
482 | 458 | callback(); |
483 | 459 | } |
484 | 460 | }, |
485 | | - |
| 461 | + |
486 | 462 | /** |
487 | | - * Get /update the playerElement value |
488 | | - */ |
| 463 | + * Get /update the playerElement value |
| 464 | + */ |
489 | 465 | getPlayerElement: function () { |
490 | 466 | this.playerElement = $j( '#' + this.pid ).get( 0 ); |
491 | 467 | return this.playerElement; |
492 | 468 | }, |
493 | | - |
| 469 | + |
494 | 470 | /** |
495 | | - * Bindings for the Video Element Events |
| 471 | + * Bindings for the Video Element Events |
496 | 472 | */ |
497 | | - |
| 473 | + |
498 | 474 | /** |
499 | 475 | * Local method for seeking event |
500 | | - * fired when "seeking" |
| 476 | + * fired when "seeking" |
501 | 477 | */ |
502 | | - onSeeking: function() { |
| 478 | + onseeking: function() { |
503 | 479 | mw.log( "native:onSeeking"); |
504 | | - // Trigger the html5 seeking event |
| 480 | + // Trigger the html5 seeking event |
505 | 481 | //( if not already set from interface ) |
506 | 482 | if( !this.seeking ) { |
507 | 483 | this.seeking = true; |
508 | 484 | // Run the seeking hook (somewhat redundant ) |
509 | 485 | $j( this ).trigger( 'onSeek' ); |
510 | | - |
| 486 | + |
511 | 487 | // Run the onSeeking interface update |
512 | 488 | this.controlBuilder.onSeek(); |
513 | | - |
| 489 | + |
514 | 490 | // Trigger the html5 "seeking" trigger |
515 | 491 | mw.log("native:seeking:trigger:: " + this.seeking); |
516 | 492 | $j( this ).trigger( 'seeking' ); |
517 | 493 | } |
518 | 494 | }, |
519 | | - |
| 495 | + |
520 | 496 | /** |
521 | 497 | * Local method for seeked event |
522 | | - * fired when done seeking |
| 498 | + * fired when done seeking |
523 | 499 | */ |
524 | | - onSeeked: function() { |
| 500 | + onseeked: function() { |
525 | 501 | mw.log("native:onSeeked"); |
526 | | - |
| 502 | + |
527 | 503 | mw.log("native:onSeeked:trigger"); |
528 | | - // Trigger the html5 action on the parent |
| 504 | + // Trigger the html5 action on the parent |
529 | 505 | if( this.seeking && this.useNativePlayerControls() ){ |
530 | 506 | this.seeking = false; |
531 | 507 | $j( this ).trigger( 'seeked' ); |
532 | 508 | } |
533 | 509 | this.seeking = false; |
534 | 510 | }, |
535 | | - |
| 511 | + |
536 | 512 | /** |
537 | 513 | * Handle the native paused event |
538 | | - */ |
539 | | - onPaused: function(){ |
| 514 | + */ |
| 515 | + onpause: function(){ |
540 | 516 | mw.log( "EmbedPlayer:native: OnPaused" ); |
541 | 517 | this.parent_pause(); |
542 | 518 | }, |
543 | | - |
| 519 | + |
544 | 520 | /** |
545 | | - * Handle the native play event |
| 521 | + * Handle the native play event |
546 | 522 | */ |
547 | | - onPlay: function(){ |
| 523 | + onplay: function(){ |
548 | 524 | mw.log("EmbedPlayer:native:: OnPlay"); |
549 | 525 | // Update the interface ( if paused ) |
550 | 526 | this.parent_play(); |
551 | 527 | }, |
552 | | - |
| 528 | + |
553 | 529 | /** |
554 | 530 | * Local method for metadata ready |
555 | | - * fired when metadata becomes available |
| 531 | + * fired when metadata becomes available |
556 | 532 | * |
557 | | - * Used to update the media duration to |
558 | | - * accurately reflect the src duration |
| 533 | + * Used to update the media duration to |
| 534 | + * accurately reflect the src duration |
559 | 535 | */ |
560 | 536 | onloadedmetadata: function() { |
561 | 537 | this.getPlayerElement(); |
— | — | @@ -562,42 +538,45 @@ |
563 | 539 | mw.log( 'f:onloadedmetadata metadata ready Update duration:' + this.playerElement.duration + ' old dur: ' + this.getDuration() ); |
564 | 540 | this.duration = this.playerElement.duration; |
565 | 541 | } |
566 | | - |
| 542 | + |
567 | 543 | //Fire "onLoaded" flags if set |
568 | 544 | if( typeof this.onLoadedCallback == 'function' ) { |
569 | 545 | this.onLoadedCallback(); |
570 | 546 | } |
571 | | - |
| 547 | + |
572 | 548 | // Tigger "media loaded" |
573 | 549 | if( ! this.mediaLoadedFlag ){ |
574 | 550 | $j( this ).trigger( 'mediaLoaded' ); |
575 | 551 | this.mediaLoadedFlag = true; |
576 | 552 | } |
577 | 553 | }, |
578 | | - |
| 554 | + |
579 | 555 | /** |
580 | 556 | * Local method for progress event |
581 | | - * fired as the video is downloaded / buffered |
| 557 | + * fired as the video is downloaded / buffered |
582 | 558 | * |
583 | | - * Used to update the bufferedPercent |
| 559 | + * Used to update the bufferedPercent |
| 560 | + * |
| 561 | + * Note: this way of updating buffer was only supported in firefox 3.x and |
| 562 | + * not supported in firefox 4.x |
584 | 563 | */ |
585 | 564 | onprogress: function( e ) { |
586 | 565 | if( e.loaded && e.total ) { |
587 | | - this.bufferedPercent = e.loaded / e.total; |
| 566 | + this.bufferedPercent = e.loaded / e.total; |
588 | 567 | this.progressEventData = e.loaded; |
589 | 568 | } |
590 | 569 | }, |
591 | | - |
| 570 | + |
592 | 571 | /** |
593 | 572 | * Local method for progress event |
594 | | - * fired as the video is downloaded / buffered |
| 573 | + * fired as the video is downloaded / buffered |
595 | 574 | * |
596 | | - * Used to update the bufferedPercent |
| 575 | + * Used to update the bufferedPercent |
597 | 576 | */ |
598 | 577 | onended: function() { |
599 | 578 | var _this = this; |
600 | 579 | mw.log( 'EmbedPlayer:native: onended:' + this.playerElement.currentTime + ' real dur:' + this.getDuration() ); |
601 | | - |
| 580 | + |
602 | 581 | this.onClipDone(); |
603 | 582 | } |
604 | 583 | }; |
Index: branches/MwEmbedStandAlone/libraries/jquery/plugins/jquery.postmessage.js |
— | — | @@ -0,0 +1,222 @@ |
| 2 | +/*! |
| 3 | + * jQuery postMessage - v0.5 - 9/11/2009 |
| 4 | + * http://benalman.com/projects/jquery-postmessage-plugin/ |
| 5 | + * |
| 6 | + * Copyright (c) 2009 "Cowboy" Ben Alman |
| 7 | + * Dual licensed under the MIT and GPL licenses. |
| 8 | + * http://benalman.com/about/license/ |
| 9 | + */ |
| 10 | + |
| 11 | +// Script: jQuery postMessage: Cross-domain scripting goodness |
| 12 | +// |
| 13 | +// *Version: 0.5, Last updated: 9/11/2009* |
| 14 | +// |
| 15 | +// Project Home - http://benalman.com/projects/jquery-postmessage-plugin/ |
| 16 | +// GitHub - http://github.com/cowboy/jquery-postmessage/ |
| 17 | +// Source - http://github.com/cowboy/jquery-postmessage/raw/master/jquery.ba-postmessage.js |
| 18 | +// (Minified) - http://github.com/cowboy/jquery-postmessage/raw/master/jquery.ba-postmessage.min.js (0.9kb) |
| 19 | +// |
| 20 | +// About: License |
| 21 | +// |
| 22 | +// Copyright (c) 2009 "Cowboy" Ben Alman, |
| 23 | +// Dual licensed under the MIT and GPL licenses. |
| 24 | +// http://benalman.com/about/license/ |
| 25 | +// |
| 26 | +// About: Examples |
| 27 | +// |
| 28 | +// This working example, complete with fully commented code, illustrates one |
| 29 | +// way in which this plugin can be used. |
| 30 | +// |
| 31 | +// Iframe resizing - http://benalman.com/code/projects/jquery-postmessage/examples/iframe/ |
| 32 | +// |
| 33 | +// About: Support and Testing |
| 34 | +// |
| 35 | +// Information about what version or versions of jQuery this plugin has been |
| 36 | +// tested with and what browsers it has been tested in. |
| 37 | +// |
| 38 | +// jQuery Versions - 1.3.2 |
| 39 | +// Browsers Tested - Internet Explorer 6-8, Firefox 3, Safari 3-4, Chrome, Opera 9. |
| 40 | +// |
| 41 | +// About: Release History |
| 42 | +// |
| 43 | +// 0.5 - (9/11/2009) Improved cache-busting |
| 44 | +// 0.4 - (8/25/2009) Initial release |
| 45 | + |
| 46 | +(function($){ |
| 47 | + '$:nomunge'; // Used by YUI compressor. |
| 48 | + |
| 49 | + // A few vars used in non-awesome browsers. |
| 50 | + var interval_id, |
| 51 | + last_hash, |
| 52 | + cache_bust = 1, |
| 53 | + |
| 54 | + // A var used in awesome browsers. |
| 55 | + rm_callback, |
| 56 | + |
| 57 | + // A few convenient shortcuts. |
| 58 | + window = this, |
| 59 | + FALSE = !1, |
| 60 | + |
| 61 | + // Reused internal strings. |
| 62 | + postMessage = 'postMessage', |
| 63 | + addEventListener = 'addEventListener', |
| 64 | + |
| 65 | + p_receiveMessage, |
| 66 | + |
| 67 | + // I couldn't get window.postMessage to actually work in Opera 9.64! |
| 68 | + has_postMessage = window[postMessage] && !$.browser.opera; |
| 69 | + |
| 70 | + // Method: jQuery.postMessage |
| 71 | + // |
| 72 | + // This method will call window.postMessage if available, setting the |
| 73 | + // targetOrigin parameter to the base of the target_url parameter for maximum |
| 74 | + // security in browsers that support it. If window.postMessage is not available, |
| 75 | + // the target window's location.hash will be used to pass the message. If an |
| 76 | + // object is passed as the message param, it will be serialized into a string |
| 77 | + // using the jQuery.param method. |
| 78 | + // |
| 79 | + // Usage: |
| 80 | + // |
| 81 | + // > jQuery.postMessage( message, target_url [, target ] ); |
| 82 | + // |
| 83 | + // Arguments: |
| 84 | + // |
| 85 | + // message - (String) A message to be passed to the other frame. |
| 86 | + // message - (Object) An object to be serialized into a params string, using |
| 87 | + // the jQuery.param method. |
| 88 | + // target_url - (String) The URL of the other frame this window is |
| 89 | + // attempting to communicate with. This must be the exact URL (including |
| 90 | + // any query string) of the other window for this script to work in |
| 91 | + // browsers that don't support window.postMessage. |
| 92 | + // target - (Object) A reference to the other frame this window is |
| 93 | + // attempting to communicate with. If omitted, defaults to `parent`. |
| 94 | + // |
| 95 | + // Returns: |
| 96 | + // |
| 97 | + // Nothing. |
| 98 | + |
| 99 | + $[postMessage] = function( message, target_url, target ) { |
| 100 | + if ( !target_url ) { return; } |
| 101 | + |
| 102 | + // Serialize the message if not a string. Note that this is the only real |
| 103 | + // jQuery dependency for this script. If removed, this script could be |
| 104 | + // written as very basic JavaScript. |
| 105 | + message = typeof message === 'string' ? message : $.param( message ); |
| 106 | + |
| 107 | + // Default to parent if unspecified. |
| 108 | + target = target || parent; |
| 109 | + |
| 110 | + if ( has_postMessage ) { |
| 111 | + // The browser supports window.postMessage, so call it with a targetOrigin |
| 112 | + // set appropriately, based on the target_url parameter. |
| 113 | + target[postMessage]( message, target_url.replace( /([^:]+:\/\/[^\/]+).*/, '$1' ) ); |
| 114 | + |
| 115 | + } else if ( target_url ) { |
| 116 | + // The browser does not support window.postMessage, so set the location |
| 117 | + // of the target to target_url#message. A bit ugly, but it works! A cache |
| 118 | + // bust parameter is added to ensure that repeat messages trigger the |
| 119 | + // callback. |
| 120 | + target.location = target_url.replace( /#.*$/, '' ) + '#' + (+new Date) + (cache_bust++) + '&' + message; |
| 121 | + } |
| 122 | + }; |
| 123 | + |
| 124 | + // Method: jQuery.receiveMessage |
| 125 | + // |
| 126 | + // Register a single callback for either a window.postMessage call, if |
| 127 | + // supported, or if unsupported, for any change in the current window |
| 128 | + // location.hash. If window.postMessage is supported and source_origin is |
| 129 | + // specified, the source window will be checked against this for maximum |
| 130 | + // security. If window.postMessage is unsupported, a polling loop will be |
| 131 | + // started to watch for changes to the location.hash. |
| 132 | + // |
| 133 | + // Note that for simplicity's sake, only a single callback can be registered |
| 134 | + // at one time. Passing no params will unbind this event (or stop the polling |
| 135 | + // loop), and calling this method a second time with another callback will |
| 136 | + // unbind the event (or stop the polling loop) first, before binding the new |
| 137 | + // callback. |
| 138 | + // |
| 139 | + // Also note that if window.postMessage is available, the optional |
| 140 | + // source_origin param will be used to test the event.origin property. From |
| 141 | + // the MDC window.postMessage docs: This string is the concatenation of the |
| 142 | + // protocol and "://", the host name if one exists, and ":" followed by a port |
| 143 | + // number if a port is present and differs from the default port for the given |
| 144 | + // protocol. Examples of typical origins are https://example.org (implying |
| 145 | + // port 443), http://example.net (implying port 80), and http://example.com:8080. |
| 146 | + // |
| 147 | + // Usage: |
| 148 | + // |
| 149 | + // > jQuery.receiveMessage( callback [, source_origin ] [, delay ] ); |
| 150 | + // |
| 151 | + // Arguments: |
| 152 | + // |
| 153 | + // callback - (Function) This callback will execute whenever a <jQuery.postMessage> |
| 154 | + // message is received, provided the source_origin matches. If callback is |
| 155 | + // omitted, any existing receiveMessage event bind or polling loop will be |
| 156 | + // canceled. |
| 157 | + // source_origin - (String) If window.postMessage is available and this value |
| 158 | + // is not equal to the event.origin property, the callback will not be |
| 159 | + // called. |
| 160 | + // source_origin - (Function) If window.postMessage is available and this |
| 161 | + // function returns false when passed the event.origin property, the |
| 162 | + // callback will not be called. |
| 163 | + // delay - (Number) An optional zero-or-greater delay in milliseconds at |
| 164 | + // which the polling loop will execute (for browser that don't support |
| 165 | + // window.postMessage). If omitted, defaults to 100. |
| 166 | + // |
| 167 | + // Returns: |
| 168 | + // |
| 169 | + // Nothing! |
| 170 | + |
| 171 | + $.receiveMessage = p_receiveMessage = function( callback, source_origin, delay ) { |
| 172 | + if ( has_postMessage ) { |
| 173 | + // Since the browser supports window.postMessage, the callback will be |
| 174 | + // bound to the actual event associated with window.postMessage. |
| 175 | + |
| 176 | + if ( callback ) { |
| 177 | + // Unbind an existing callback if it exists. |
| 178 | + rm_callback && p_receiveMessage(); |
| 179 | + |
| 180 | + // Bind the callback. A reference to the callback is stored for ease of |
| 181 | + // unbinding. |
| 182 | + rm_callback = function(e) { |
| 183 | + if ( ( typeof source_origin === 'string' && e.origin !== source_origin ) |
| 184 | + || ( $.isFunction( source_origin ) && source_origin( e.origin ) === FALSE ) ) { |
| 185 | + return FALSE; |
| 186 | + } |
| 187 | + callback( e ); |
| 188 | + }; |
| 189 | + } |
| 190 | + |
| 191 | + if ( window[addEventListener] ) { |
| 192 | + window[ callback ? addEventListener : 'removeEventListener' ]( 'message', rm_callback, FALSE ); |
| 193 | + } else { |
| 194 | + window[ callback ? 'attachEvent' : 'detachEvent' ]( 'onmessage', rm_callback ); |
| 195 | + } |
| 196 | + |
| 197 | + } else { |
| 198 | + // Since the browser sucks, a polling loop will be started, and the |
| 199 | + // callback will be called whenever the location.hash changes. |
| 200 | + |
| 201 | + interval_id && clearInterval( interval_id ); |
| 202 | + interval_id = null; |
| 203 | + |
| 204 | + if ( callback ) { |
| 205 | + delay = typeof source_origin === 'number' |
| 206 | + ? source_origin |
| 207 | + : typeof delay === 'number' |
| 208 | + ? delay |
| 209 | + : 100; |
| 210 | + |
| 211 | + interval_id = setInterval(function(){ |
| 212 | + var hash = document.location.hash, |
| 213 | + re = /^#?\d+&/; |
| 214 | + if ( hash !== last_hash && re.test( hash ) ) { |
| 215 | + last_hash = hash; |
| 216 | + callback({ data: hash.replace( re, '' ) }); |
| 217 | + } |
| 218 | + }, delay ); |
| 219 | + } |
| 220 | + } |
| 221 | + }; |
| 222 | + |
| 223 | +})(jQuery); |
\ No newline at end of file |