summaryrefslogtreecommitdiff
path: root/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.processEmbedPlayers.js
blob: 0748ccd0450159fe76a76fb4a822bba95d7e8e98 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/**
 * Selector based embedPlayer processing
 *
 * @param {Function=}
 *      callback Optional Function to be called once video interfaces
 *      are ready
 *
 */

( function( mw, $ ) { "use strict";

mw.processEmbedPlayers = function( playerSet, callback ) {
	mw.log( 'processEmbedPlayers:: playerSet: ', playerSet);
	// The player id list container
	var playerIdList = [];
	
	// Check if the selected player set is ready if ready issue the parent callback
	var areSelectedPlayersReady = function(){
		var playersLoaded = true;
		$.each( playerIdList, function(inx, playerId){
			if( ! $( '#' + playerId )[0].playerReadyFlag ){
				playersLoaded = false;
				return false;
			}
		})
		if( playersLoaded ){
			if( callback ){
				callback();
			}
		}
	}

	/**
	 * Adds a player element for the embedPlayer to rewrite
	 *
	 * uses embedPlayer interface on audio / video elements uses mvPlayList
	 * interface on playlist elements
	 *
	 * Once a player interface is established the following chain of functions
	 * are called;
	 *
	 * _this.checkPlayerSources()
	 * _this.setupSourcePlayer()
	 * _this.updatePlaybackInterface()
	 * _this.selectedPlayer.load()
	 * _this.showPlayer()
	 *
	 * @param {Element}
	 *      playerElement DOM element to be swapped
	 */
	var addPlayerElement = function( playerElement ) {
		var _this = this;
		mw.log('EmbedPlayer:: addElement:: ' + playerElement.id );

		// Be sure to "stop" the target ( Firefox 3x keeps playing
		// the video even though its been removed from the DOM )
		if( playerElement.pause ){
			playerElement.pause();
		}

		// Allow modules to override the wait for metadata flag:
		$( mw ).trigger( 'EmbedPlayerWaitForMetaCheck', playerElement );

		// DOM *could* load height, width and duration eventually, in some browsers
		// By default, don't bother waiting for this.
		var waitForMeta = false;

		// if a plugin has told us not to waitForMeta, don't
		if ( playerElement.waitForMeta !== false ) {
			// Check if we should wait for metadata, after all
			waitForMeta = waitForMetaCheck( playerElement );
		}

		var ranPlayerSwapFlag = false;

		// Local callback to runPlayer swap once playerElement has metadata
		var runPlayerSwap = function () {
			// Don't run player swap twice
			if( ranPlayerSwapFlag ){
				return ;
			}
			ranPlayerSwapFlag = true;
			mw.log( "processEmbedPlayers::runPlayerSwap::" + $( playerElement ).attr('id') );

			var playerInterface = new mw.EmbedPlayer( playerElement );
			var inDomPlayer = swapEmbedPlayerElement( playerElement, playerInterface );

			// IE/Edge with WebM components re-triggers autoplay after removal as well.
			if( playerElement.pause ){
				playerElement.pause();
			}

			// Trigger the EmbedPlayerNewPlayer for embedPlayer interface
			mw.log("processEmbedPlayers::trigger:: EmbedPlayerNewPlayer " + inDomPlayer.id );

			// Allow plugins to add bindings to the inDomPlayer
			$( mw ).trigger ( 'EmbedPlayerNewPlayer', inDomPlayer );

			// Add a player ready binding:
			$( inDomPlayer ).bind( 'playerReady.swap', function(event, id){
				$( inDomPlayer ).unbind( 'playerReady.swap' );
				areSelectedPlayersReady();
			});

			//
			// Allow modules to block player build out
			//
			// this is needed in cases where you need to do an asynchronous
			// player interface setup. like iframes asynchronous announcing its ready for
			// bindings that can affect player setup.
			mw.log("EmbedPlayer::addPlayerElement :trigger startPlayerBuildOut:" + inDomPlayer.id );
			$( '#' + inDomPlayer.id ).triggerQueueCallback( 'startPlayerBuildOut', function(){
				// Issue the checkPlayerSources call to the new player
				// interface: make sure to use the element that is in the DOM:
				inDomPlayer.checkPlayerSources();
			});
		};

		if( waitForMeta && mw.config.get('EmbedPlayer.WaitForMeta' ) ) {
			mw.log('processEmbedPlayers::WaitForMeta ( video missing height (' +
					$( playerElement ).attr('height') + '), width (' +
					$( playerElement ).attr('width') + ') or duration: ' +
					$( playerElement ).attr('duration')
			);
			$( playerElement ).bind( "loadedmetadata", runPlayerSwap );

			// Time-out of 5 seconds ( maybe still playable but no timely metadata )
			setTimeout( runPlayerSwap, 5000 );
			return ;
		} else {
			runPlayerSwap();
			return ;
		}
	};

	/**
	 * Check if we should wait for metadata.
	 *
	 * @return 	true if the size is "likely" to be updated by waiting for metadata
	 * 			false if the size has been set via an attribute or is already loaded
	 */
	var waitForMetaCheck = function( playerElement ){
		var waitForMeta = false;

		// Don't wait for metadata for non html5 media elements
		if( !playerElement ){
			return false;
		}
		if( !playerElement.tagName || ( playerElement.tagName.toLowerCase() != 'audio'  && playerElement.tagName.toLowerCase() != 'video' ) ){
			return false;
		}
		// If we don't have a native player don't wait for metadata
		if( !mw.EmbedTypes.getMediaPlayers().isSupportedPlayer( 'oggNative') &&
			!mw.EmbedTypes.getMediaPlayers().isSupportedPlayer( 'webmNative') &&
			!mw.EmbedTypes.getMediaPlayers().isSupportedPlayer( 'h264Native' ) &&
			!mw.EmbedTypes.getMediaPlayers().isSupportedPlayer( 'appleVdnPlayer' ) )
		{
			return false;
		}


		var width = $( playerElement ).css( 'width' );
		var height = $( playerElement ).css( 'height' );
		// Css video defaults ( firefox )
		if( $( playerElement ).css( 'width' ) == '300px' &&
				$( playerElement ).css( 'height' ) == '150px'
		){
			waitForMeta = true;
		} else {
			// Check if we should wait for duration:
			if( $( playerElement ).attr( 'duration') ||
				$( playerElement ).attr( 'durationHint') ||
				$( playerElement ).attr('data-durationhint')
			){
				// height, width and duration set; do not wait for meta data:
				return false;
			} else {
				waitForMeta = true;
			}
		}

		// Firefox ~ sometimes~ gives -1 for unloaded media
		if ( $(playerElement).attr( 'width' ) == -1 || $(playerElement).attr( 'height' ) == -1 ) {
			waitForMeta = true;
		}

		// Google Chrome / safari gives 0 width height for unloaded media
		if( $(playerElement).attr( 'width' ) === 0 ||
			$(playerElement).attr( 'height' ) === 0
		) {
			waitForMeta = true;
		}

		// Firefox default width height is ~sometimes~ 150 / 300
		if( playerElement.height == 150 && playerElement.width == 300 ){
			waitForMeta = true;
		}

		// Make sure we have a src attribute or source child
		// ( i.e not a video tag to be dynamically populated or looked up from
		// xml resource description )
		if( waitForMeta &&
			(
				$( playerElement ).attr('src') ||
				$( playerElement ).find("source[src]").length !== 0
			)
		) {
			// Detect src type ( if no type set )
			return true;
		} else {
			// playerElement is not likely to update its meta data ( no src )
			return false;
		}
	};

	/**
	 * swapEmbedPlayerElement
	 *
	 * Takes a video element as input and swaps it out with an embed player interface
	 *
	 * @param {Element}
	 *      targetElement Element to be swapped
	 * @param {Object}
	 *      playerInterface Interface to swap into the target element
	 */
	var swapEmbedPlayerElement =  function( targetElement, playerInterface ) {
		mw.log( 'processEmbedPlayers::swapEmbedPlayerElement: ' + targetElement.id );
		// Create a new element to swap the player interface into
		var swapPlayerElement = document.createElement('div');

		// Add a class that identifies all embedPlayers:
		$( swapPlayerElement ).addClass( 'mwEmbedPlayer' );

		// Get properties / methods from playerInterface:
		for ( var method in playerInterface ) {
			if ( method != 'readyState' ) { // readyState crashes IE ( don't include )
				swapPlayerElement[ method ] = playerInterface[ method ];
			}
		}
		// copy over css text:
		swapPlayerElement.style.cssText = targetElement.style.cssText;
		// player element must always be relative to host video and image layout
		swapPlayerElement.style.position = 'relative';

		// Copy any data attributes from the target player element over to the swapPlayerElement
		var dataAttributes = mw.config.get("EmbedPlayer.DataAttributes");
		if( dataAttributes ){
			$.each( dataAttributes, function( attrName, na ){
				if( $( targetElement ).data( attrName ) ){
					$( swapPlayerElement ).data( attrName, $( targetElement ).data( attrName ) );
				}
			});
		}
		// Check for Persistent native player ( should keep the video embed around )
		if(  playerInterface.isPersistentNativePlayer()
				||
			// Also check for native controls on a video or audio tag
			( playerInterface.useNativePlayerControls()
					&&
				( targetElement.nodeName == 'video' || targetElement.nodeName == 'audio' )
			)
		) {

			$( targetElement )
			.attr( 'id', playerInterface.pid )
			.addClass( 'nativeEmbedPlayerPid' )
			.show()
			.after(
				$( swapPlayerElement ).css( 'display', 'none' )
			);

		} else {
			$( targetElement ).replaceWith( swapPlayerElement );
		}

		// If we don't already have a loadSpiner add one:
		if( $('#loadingSpinner_' + playerInterface.id ).length == 0 && $.client.profile().name !== 'firefox' ){
			if( playerInterface.useNativePlayerControls() || playerInterface.isPersistentNativePlayer() ) {
				var $spinner = $( targetElement )
					.getAbsoluteOverlaySpinner();
			}else{
				var $spinner = $( swapPlayerElement ).getAbsoluteOverlaySpinner();
			}
			$spinner.attr('id', 'loadingSpinner_' + playerInterface.id );
		}
		return swapPlayerElement;
	};

	// Add a loader for <div /> embed player rewrites:
	$( playerSet ).each( function( index, playerElement) {

		// Make sure the playerElement has an id:
		if( !$( playerElement ).attr('id') ){
			$( playerElement ).attr( "id", 'mwe_vid' + ( index ) );
		}
		// Add the player Id to the playerIdList
		playerIdList.push( $( playerElement ).attr( "id") );

		// If we are dynamically embedding on a "div" check if we can
		// add a poster image behind the loader:
		if( playerElement.nodeName.toLowerCase() == 'div'
				&&
			$(playerElement).attr( 'poster' ) )
		{
			var posterSrc = $(playerElement).attr( 'poster' );

			// Set image size:
			var width = $( playerElement ).width();
			var height = $( playerElement ).height();
			if( !width ){
				var width = '100%';
			}
			if( !height ){
				var height = '100%';
			}

			mw.log('EmbedPlayer:: set loading background: ' + posterSrc);
			$( playerElement ).append(
				$( '<img />' )
				.attr( 'src', posterSrc)
				.css({
					'position' : 'absolute',
					'width' : width,
					'height' : height
				})
			);
		}
	});

	// Make sure we have user preference setup for setting preferences on video selection
	var addedPlayersFlag = false;
	mw.log("processEmbedPlayers:: Do: " + $( playerSet ).length + ' players ');
	// Add each selected element to the player manager:
	$( playerSet ).each( function( index, playerElement) {
		// Make sure the video tag was not generated by our library:
		if( $( playerElement ).hasClass( 'nativeEmbedPlayerPid' ) ){
			$( '#loadingSpinner_' + $( playerElement ).attr('id') ).remove();
			mw.log( 'processEmbedPlayers::$.embedPlayer skip embedPlayer gennerated video: ' + playerElement );
		} else {
			addedPlayersFlag = true;
			// Add the player
			addPlayerElement( playerElement );
		}
	});
	if( !addedPlayersFlag ){
		// Run the callback directly if no players were added
		if( callback ){
			callback();
		}
	}
};

})( mw, jQuery );