PageSourceSearch

https://www.arpae.it/++resource++wildcard-media/integration.js?version=2019-05-08%2009%3A10%3A06

js arpae.it collected 2026-10-01 10:32:48 UTC 169,986 bytes, 6,020 lines download raw bytes

1/*!
2 *
3 * MediaElement.js
4 * HTML5 <video> and <audio> shim and player
5 * http://mediaelementjs.com/
6 *
7 * Creates a JavaScript object that mimics HTML5 MediaElement API
8 * for browsers that don't understand HTML5 or can't play the provided codec
9 * Can play MP4 (H.264), Ogg, WebM, FLV, WMV, WMA, ACC, and MP3
10 *
11 * Copyright 2010-2014, John Dyer (http://j.hn)
12 * License: MIT
13 *
14 */
15// Namespace
16var mejs = mejs || {};
17
18// version number
19mejs.version = '2.18.2'; 
20
21
22// player number (for missing, same id attr)
23mejs.meIndex = 0;
24
25// media types accepted by plugins
26mejs.plugins = {
27	silverlight: [
28		{version: [3,0], types: ['video/mp4','video/m4v','video/mov','video/wmv','audio/wma','audio/m4a','audio/mp3','audio/wav','audio/mpeg']}
29	],
30	flash: [
31		{version: [9,0,124], types: ['video/mp4','video/m4v','video/mov','video/flv','video/rtmp','video/x-flv','audio/flv','audio/x-flv','audio/mp3','audio/m4a','audio/mpeg', 'video/youtube', 'video/x-youtube', 'video/dailymotion', 'video/x-dailymotion', 'application/x-mpegURL']}
32		//,{version: [12,0], types: ['video/webm']} // for future reference (hopefully!)
33	],
34	youtube: [
35		{version: null, types: ['video/youtube', 'video/x-youtube', 'audio/youtube', 'audio/x-youtube']}
36	],
37	vimeo: [
38		{version: null, types: ['video/vimeo', 'video/x-vimeo']}
39	]
40};
41
42/*
43Utility methods
44*/
45mejs.Utility = {
46	encodeUrl: function(url) {
47		return encodeURIComponent(url); //.replace(/\?/gi,'%3F').replace(/=/gi,'%3D').replace(/&/gi,'%26');
48	},
49	escapeHTML: function(s) {
50		return s.toString().split('&').join('&amp;').split('<').join('&lt;').split('"').join('&quot;');
51	},
52	absolutizeUrl: function(url) {
53		var el = document.createElement('div');
54		el.innerHTML = '<a href="' + this.escapeHTML(url) + '">x</a>';
55		return el.firstChild.href;
56	},
57	getScriptPath: function(scriptNames) {
58		var
59			i = 0,
60			j,
61			codePath = '',
62			testname = '',
63			slashPos,
64			filenamePos,
65			scriptUrl,
66			scriptPath,			
67			scriptFilename,
68			scripts = document.getElementsByTagName('script'),
69			il = scripts.length,
70			jl = scriptNames.length;
71			
72		// go through all <script> tags
73		for (; i < il; i++) {
74			scriptUrl = scripts[i].src;
75			slashPos = scriptUrl.lastIndexOf('/');
76			if (slashPos > -1) {
77				scriptFilename = scriptUrl.substring(slashPos + 1);
78				scriptPath = scriptUrl.substring(0, slashPos + 1);
79			} else {
80				scriptFilename = scriptUrl;
81				scriptPath = '';			
82			}
83			
84			// see if any <script> tags have a file name that matches the 
85			for (j = 0; j < jl; j++) {
86				testname = scriptNames[j];
87				filenamePos = scriptFilename.indexOf(testname);
88				if (filenamePos > -1) {
89					codePath = scriptPath;
90					break;
91				}
92			}
93			
94			// if we found a path, then break and return it
95			if (codePath !== '') {
96				break;
97			}
98		}
99		
100		// send the best path back
101		return codePath;
102	},
103	/*
104	 * Calculate the time format to use. We have a default format set in the
105	 * options but it can be imcomplete. We ajust it according to the media
106	 * duration.
107	 *
108	 * We support format like 'hh:mm:ss:ff'.
109	 */
110	calculateTimeFormat: function(time, options, fps) {
111		if (time < 0) {
112			time = 0;
113		}
114
115		if(typeof fps == 'undefined') {
116		    fps = 25;
117		}
118
119		var format = options.timeFormat,
120			firstChar = format[0],
121			firstTwoPlaces = (format[1] == format[0]),
122			separatorIndex = firstTwoPlaces? 2: 1,
123			separator = ':',
124			hours = Math.floor(time / 3600) % 24,
125			minutes = Math.floor(time / 60) % 60,
126			seconds = Math.floor(time % 60),
127			frames = Math.floor(((time % 1)*fps).toFixed(3)),
128			lis = [
129				[frames, 'f'],
130				[seconds, 's'],
131				[minutes, 'm'],
132				[hours, 'h']
133			];
134
135		// Try to get the separator from the format
136		if (format.length < separatorIndex) {
137			separator = format[separatorIndex];
138		}
139
140		var required = false;
141
142		for (var i=0, len=lis.length; i < len; i++) {
143			if (format.indexOf(lis[i][1]) !== -1) {
144				required=true;
145			}
146			else if (required) {
147				var hasNextValue = false;
148				for (var j=i; j < len; j++) {
149					if (lis[j][0] > 0) {
150						hasNextValue = true;
151						break;
152					}
153				}
154
155				if (! hasNextValue) {
156					break;
157				}
158
159				if (!firstTwoPlaces) {
160					format = firstChar + format;
161				}
162				format = lis[i][1] + separator + format;
163				if (firstTwoPlaces) {
164					format = lis[i][1] + format;
165				}
166				firstChar = lis[i][1];
167			}
168		}
169		options.currentTimeFormat = format;
170	},
171	/*
172	 * Prefix the given number by zero if it is lower than 10.
173	 */
174	twoDigitsString: function(n) {
175		if (n < 10) {
176			return '0' + n;
177		}
178		return String(n);
179	},
180	secondsToTimeCode: function(time, options) {
181		if (time < 0) {
182			time = 0;
183		}
184
185		// Maintain backward compatibility with method signature before v2.18.
186		if (typeof options !== 'object') {
187			var format = 'm:ss';
188			format = arguments[1] ? 'hh:mm:ss' : format; // forceHours
189			format = arguments[2] ? format + ':ff' : format; // showFrameCount
190
191			options = {
192				currentTimeFormat: format,
193				framesPerSecond: arguments[3] || 25
194			};
195		}
196
197		var fps = options.framesPerSecond;
198		if(typeof fps === 'undefined') {
199			fps = 25;
200		}
201
202		var format = options.currentTimeFormat,
203			hours = Math.floor(time / 3600) % 24,
204			minutes = Math.floor(time / 60) % 60,
205			seconds = Math.floor(time % 60),
206			frames = Math.floor(((time % 1)*fps).toFixed(3));
207			lis = [
208				[frames, 'f'],
209				[seconds, 's'],
210				[minutes, 'm'],
211				[hours, 'h']
212			];
213
214		var res = format;
215		for (i=0,len=lis.length; i < len; i++) {
216			res = res.replace(lis[i][1]+lis[i][1], this.twoDigitsString(lis[i][0]));
217			res = res.replace(lis[i][1], lis[i][0]);
218		}
219		return res;
220	},
221	
222	timeCodeToSeconds: function(hh_mm_ss_ff, forceHours, showFrameCount, fps){
223		if (typeof showFrameCount == 'undefined') {
224		    showFrameCount=false;
225		} else if(typeof fps == 'undefined') {
226		    fps = 25;
227		}
228	
229		var tc_array = hh_mm_ss_ff.split(":"),
230			tc_hh = parseInt(tc_array[0], 10),
231			tc_mm = parseInt(tc_array[1], 10),
232			tc_ss = parseInt(tc_array[2], 10),
233			tc_ff = 0,
234			tc_in_seconds = 0;
235		
236		if (showFrameCount) {
237		    tc_ff = parseInt(tc_array[3])/fps;
238		}
239		
240		tc_in_seconds = ( tc_hh * 3600 ) + ( tc_mm * 60 ) + tc_ss + tc_ff;
241		
242		return tc_in_seconds;
243	},
244	
245
246	convertSMPTEtoSeconds: function (SMPTE) {
247		if (typeof SMPTE != 'string') 
248			return false;
249
250		SMPTE = SMPTE.replace(',', '.');
251		
252		var secs = 0,
253			decimalLen = (SMPTE.indexOf('.') != -1) ? SMPTE.split('.')[1].length : 0,
254			multiplier = 1;
255		
256		SMPTE = SMPTE.split(':').reverse();
257		
258		for (var i = 0; i < SMPTE.length; i++) {
259			multiplier = 1;
260			if (i > 0) {
261				multiplier = Math.pow(60, i); 
262			}
263			secs += Number(SMPTE[i]) * multiplier;
264		}
265		return Number(secs.toFixed(decimalLen));
266	},	
267	
268	/* borrowed from SWFObject: http://code.google.com/p/swfobject/source/browse/trunk/swfobject/src/swfobject.js#474 */
269	removeSwf: function(id) {
270		var obj = document.getElementById(id);
271		if (obj && /object|embed/i.test(obj.nodeName)) {
272			if (mejs.MediaFeatures.isIE) {
273				obj.style.display = "none";
274				(function(){
275					if (obj.readyState == 4) {
276						mejs.Utility.removeObjectInIE(id);
277					} else {
278						setTimeout(arguments.callee, 10);
279					}
280				})();
281			} else {
282				obj.parentNode.removeChild(obj);
283			}
284		}
285	},
286	removeObjectInIE: function(id) {
287		var obj = document.getElementById(id);
288		if (obj) {
289			for (var i in obj) {
290				if (typeof obj[i] == "function") {
291					obj[i] = null;
292				}
293			}
294			obj.parentNode.removeChild(obj);
295		}		
296	}
297};
298
299
300// Core detector, plugins are added below
301mejs.PluginDetector = {
302
303	// main public function to test a plug version number PluginDetector.hasPluginVersion('flash',[9,0,125]);
304	hasPluginVersion: function(plugin, v) {
305		var pv = this.plugins[plugin];
306		v[1] = v[1] || 0;
307		v[2] = v[2] || 0;
308		return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
309	},
310
311	// cached values
312	nav: window.navigator,
313	ua: window.navigator.userAgent.toLowerCase(),
314
315	// stored version numbers
316	plugins: [],
317
318	// runs detectPlugin() and stores the version number
319	addPlugin: function(p, pluginName, mimeType, activeX, axDetect) {
320		this.plugins[p] = this.detectPlugin(pluginName, mimeType, activeX, axDetect);
321	},
322
323	// get the version number from the mimetype (all but IE) or ActiveX (IE)
324	detectPlugin: function(pluginName, mimeType, activeX, axDetect) {
325
326		var version = [0,0,0],
327			description,
328			i,
329			ax;
330
331		// Firefox, Webkit, Opera
332		if (typeof(this.nav.plugins) != 'undefined' && typeof this.nav.plugins[pluginName] == 'object') {
333			description = this.nav.plugins[pluginName].description;
334			if (description && !(typeof this.nav.mimeTypes != 'undefined' && this.nav.mimeTypes[mimeType] && !this.nav.mimeTypes[mimeType].enabledPlugin)) {
335				version = description.replace(pluginName, '').replace(/^\s+/,'').replace(/\sr/gi,'.').split('.');
336				for (i=0; i<version.length; i++) {
337					version[i] = parseInt(version[i].match(/\d+/), 10);
338				}
339			}
340		// Internet Explorer / ActiveX
341		} else if (typeof(window.ActiveXObject) != 'undefined') {
342			try {
343				ax = new ActiveXObject(activeX);
344				if (ax) {
345					version = axDetect(ax);
346				}
347			}
348			catch (e) { }
349		}
350		return version;
351	}
352};
353
354// Add Flash detection
355mejs.PluginDetector.addPlugin('flash','Shockwave Flash','application/x-shockwave-flash','ShockwaveFlash.ShockwaveFlash', function(ax) {
356	// adapted from SWFObject
357	var version = [],
358		d = ax.GetVariable("$version");
359	if (d) {
360		d = d.split(" ")[1].split(",");
361		version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
362	}
363	return version;
364});
365
366// Add Silverlight detection
367mejs.PluginDetector.addPlugin('silverlight','Silverlight Plug-In','application/x-silverlight-2','AgControl.AgControl', function (ax) {
368	// Silverlight cannot report its version number to IE
369	// but it does have a isVersionSupported function, so we have to loop through it to get a version number.
370	// adapted from http://www.silverlightversion.com/
371	var v = [0,0,0,0],
372		loopMatch = function(ax, v, i, n) {
373			while(ax.isVersionSupported(v[0]+ "."+ v[1] + "." + v[2] + "." + v[3])){
374				v[i]+=n;
375			}
376			v[i] -= n;
377		};
378	loopMatch(ax, v, 0, 1);
379	loopMatch(ax, v, 1, 1);
380	loopMatch(ax, v, 2, 10000); // the third place in the version number is usually 5 digits (4.0.xxxxx)
381	loopMatch(ax, v, 2, 1000);
382	loopMatch(ax, v, 2, 100);
383	loopMatch(ax, v, 2, 10);
384	loopMatch(ax, v, 2, 1);
385	loopMatch(ax, v, 3, 1);
386
387	return v;
388});
389// add adobe acrobat
390/*
391PluginDetector.addPlugin('acrobat','Adobe Acrobat','application/pdf','AcroPDF.PDF', function (ax) {
392	var version = [],
393		d = ax.GetVersions().split(',')[0].split('=')[1].split('.');
394
395	if (d) {
396		version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
397	}
398	return version;
399});
400*/
401// necessary detection (fixes for <IE9)
402mejs.MediaFeatures = {
403	init: function() {
404		var
405			t = this,
406			d = document,
407			nav = mejs.PluginDetector.nav,
408			ua = mejs.PluginDetector.ua.toLowerCase(),
409			i,
410			v,
411			html5Elements = ['source','track','audio','video'];
412
413		// detect browsers (only the ones that have some kind of quirk we need to work around)
414		t.isiPad = (ua.match(/ipad/i) !== null);
415		t.isiPhone = (ua.match(/iphone/i) !== null);
416		t.isiOS = t.isiPhone || t.isiPad;
417		t.isAndroid = (ua.match(/android/i) !== null);
418		t.isBustedAndroid = (ua.match(/android 2\.[12]/) !== null);
419		t.isBustedNativeHTTPS = (location.protocol === 'https:' && (ua.match(/android [12]\./) !== null || ua.match(/macintosh.* version.* safari/) !== null));
420		t.isIE = (nav.appName.toLowerCase().indexOf("microsoft") != -1 || nav.appName.toLowerCase().match(/trident/gi) !== null);
421		t.isChrome = (ua.match(/chrome/gi) !== null);
422		t.isChromium = (ua.match(/chromium/gi) !== null);
423		t.isFirefox = (ua.match(/firefox/gi) !== null);
424		t.isWebkit = (ua.match(/webkit/gi) !== null);
425		t.isGecko = (ua.match(/gecko/gi) !== null) && !t.isWebkit && !t.isIE;
426		t.isOpera = (ua.match(/opera/gi) !== null);
427		t.hasTouch = ('ontouchstart' in window); //  && window.ontouchstart != null); // this breaks iOS 7
428		
429		// borrowed from Modernizr
430		t.svg = !! document.createElementNS &&
431				!! document.createElementNS('http://www.w3.org/2000/svg','svg').createSVGRect;
432
433		// create HTML5 media elements for IE before 9, get a <video> element for fullscreen detection
434		for (i=0; i<html5Elements.length; i++) {
435			v = document.createElement(html5Elements[i]);
436		}
437		
438		t.supportsMediaTag = (typeof v.canPlayType !== 'undefined' || t.isBustedAndroid);
439
440		// Fix for IE9 on Windows 7N / Windows 7KN (Media Player not installer)
441		try{
442			v.canPlayType("video/mp4");
443		}catch(e){
444			t.supportsMediaTag = false;
445		}
446
447		// detect native JavaScript fullscreen (Safari/Firefox only, Chrome still fails)
448		
449		// iOS
450		t.hasSemiNativeFullScreen = (typeof v.webkitEnterFullscreen !== 'undefined');
451		
452		// W3C
453		t.hasNativeFullscreen = (typeof v.requestFullscreen !== 'undefined');
454		
455		// webkit/firefox/IE11+
456		t.hasWebkitNativeFullScreen = (typeof v.webkitRequestFullScreen !== 'undefined');
457		t.hasMozNativeFullScreen = (typeof v.mozRequestFullScreen !== 'undefined');
458		t.hasMsNativeFullScreen = (typeof v.msRequestFullscreen !== 'undefined');
459		
460		t.hasTrueNativeFullScreen = (t.hasWebkitNativeFullScreen || t.hasMozNativeFullScreen || t.hasMsNativeFullScreen);
461		t.nativeFullScreenEnabled = t.hasTrueNativeFullScreen;
462		
463		// Enabled?
464		if (t.hasMozNativeFullScreen) {
465			t.nativeFullScreenEnabled = document.mozFullScreenEnabled;
466		} else if (t.hasMsNativeFullScreen) {
467			t.nativeFullScreenEnabled = document.msFullscreenEnabled;		
468		}
469		
470		if (t.isChrome) {
471			t.hasSemiNativeFullScreen = false;
472		}
473		
474		if (t.hasTrueNativeFullScreen) {
475			
476			t.fullScreenEventName = '';
477			if (t.hasWebkitNativeFullScreen) { 
478				t.fullScreenEventName = 'webkitfullscreenchange';
479				
480			} else if (t.hasMozNativeFullScreen) {
481				t.fullScreenEventName = 'mozfullscreenchange';
482				
483			} else if (t.hasMsNativeFullScreen) {
484				t.fullScreenEventName = 'MSFullscreenChange';
485			}
486			
487			t.isFullScreen = function() {
488				if (t.hasMozNativeFullScreen) {
489					return d.mozFullScreen;
490				
491				} else if (t.hasWebkitNativeFullScreen) {
492					return d.webkitIsFullScreen;
493				
494				} else if (t.hasMsNativeFullScreen) {
495					return d.msFullscreenElement !== null;
496				}
497			}
498					
499			t.requestFullScreen = function(el) {
500		
501				if (t.hasWebkitNativeFullScreen) {
502					el.webkitRequestFullScreen();
503					
504				} else if (t.hasMozNativeFullScreen) {
505					el.mozRequestFullScreen();
506
507				} else if (t.hasMsNativeFullScreen) {
508					el.msRequestFullscreen();
509
510				}
511			}
512			
513			t.cancelFullScreen = function() {				
514				if (t.hasWebkitNativeFullScreen) {
515					document.webkitCancelFullScreen();
516					
517				} else if (t.hasMozNativeFullScreen) {
518					document.mozCancelFullScreen();
519					
520				} else if (t.hasMsNativeFullScreen) {
521					document.msExitFullscreen();
522					
523				}
524			}	
525			
526		}
527		
528		
529		// OS X 10.5 can't do this even if it says it can :(
530		if (t.hasSemiNativeFullScreen && ua.match(/mac os x 10_5/i)) {
531			t.hasNativeFullScreen = false;
532			t.hasSemiNativeFullScreen = false;
533		}
534		
535	}
536};
537mejs.MediaFeatures.init();
538
539/*
540extension methods to <video> or <audio> object to bring it into parity with PluginMediaElement (see below)
541*/
542mejs.HtmlMediaElement = {
543	pluginType: 'native',
544	isFullScreen: false,
545
546	setCurrentTime: function (time) {
547		this.currentTime = time;
548	},
549
550	setMuted: function (muted) {
551		this.muted = muted;
552	},
553
554	setVolume: function (volume) {
555		this.volume = volume;
556	},
557
558	// for parity with the plugin versions
559	stop: function () {
560		this.pause();
561	},
562
563	// This can be a url string
564	// or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
565	setSrc: function (url) {
566		
567		// Fix for IE9 which can't set .src when there are <source> elements. Awesome, right?
568		var 
569			existingSources = this.getElementsByTagName('source');
570		while (existingSources.length > 0){
571			this.removeChild(existingSources[0]);
572		}
573	
574		if (typeof url == 'string') {
575			this.src = url;
576		} else {
577			var i, media;
578
579			for (i=0; i<url.length; i++) {
580				media = url[i];
581				if (this.canPlayType(media.type)) {
582					this.src = media.src;
583					break;
584				}
585			}
586		}
587	},
588
589	setVideoSize: function (width, height) {
590		this.width = width;
591		this.height = height;
592	}
593};
594
595/*
596Mimics the <video/audio> element by calling Flash's External Interface or Silverlights [ScriptableMember]
597*/
598mejs.PluginMediaElement = function (pluginid, pluginType, mediaUrl) {
599	this.id = pluginid;
600	this.pluginType = pluginType;
601	this.src = mediaUrl;
602	this.events = {};
603	this.attributes = {};
604};
605
606// JavaScript values and ExternalInterface methods that match HTML5 video properties methods
607// http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/fl/video/FLVPlayback.html
608// http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
609mejs.PluginMediaElement.prototype = {
610
611	// special
612	pluginElement: null,
613	pluginType: '',
614	isFullScreen: false,
615
616	// not implemented :(
617	playbackRate: -1,
618	defaultPlaybackRate: -1,
619	seekable: [],
620	played: [],
621
622	// HTML5 read-only properties
623	paused: true,
624	ended: false,
625	seeking: false,
626	duration: 0,
627	error: null,
628	tagName: '',
629
630	// HTML5 get/set properties, but only set (updated by event handlers)
631	muted: false,
632	volume: 1,
633	currentTime: 0,
634
635	// HTML5 methods
636	play: function () {
637		if (this.pluginApi != null) {
638			if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
639				this.pluginApi.playVideo();
640			} else {
641				this.pluginApi.playMedia();
642			}
643			this.paused = false;
644		}
645	},
646	load: function () {
647		if (this.pluginApi != null) {
648			if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
649			} else {
650				this.pluginApi.loadMedia();
651			}
652			
653			this.paused = false;
654		}
655	},
656	pause: function () {
657		if (this.pluginApi != null) {
658			if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
659				this.pluginApi.pauseVideo();
660			} else {
661				this.pluginApi.pauseMedia();
662			}			
663			
664			
665			this.paused = true;
666		}
667	},
668	stop: function () {
669		if (this.pluginApi != null) {
670			if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
671				this.pluginApi.stopVideo();
672			} else {
673				this.pluginApi.stopMedia();
674			}	
675			this.paused = true;
676		}
677	},
678	canPlayType: function(type) {
679		var i,
680			j,
681			pluginInfo,
682			pluginVersions = mejs.plugins[this.pluginType];
683
684		for (i=0; i<pluginVersions.length; i++) {
685			pluginInfo = pluginVersions[i];
686
687			// test if user has the correct plugin version
688			if (mejs.PluginDetector.hasPluginVersion(this.pluginType, pluginInfo.version)) {
689
690				// test for plugin playback types
691				for (j=0; j<pluginInfo.types.length; j++) {
692					// find plugin that can play the type
693					if (type == pluginInfo.types[j]) {
694						return 'probably';
695					}
696				}
697			}
698		}
699
700		return '';
701	},
702	
703	positionFullscreenButton: function(x,y,visibleAndAbove) {
704		if (this.pluginApi != null && this.pluginApi.positionFullscreenButton) {
705			this.pluginApi.positionFullscreenButton(Math.floor(x),Math.floor(y),visibleAndAbove);
706		}
707	},
708	
709	hideFullscreenButton: function() {
710		if (this.pluginApi != null && this.pluginApi.hideFullscreenButton) {
711			this.pluginApi.hideFullscreenButton();
712		}		
713	},	
714	
715
716	// custom methods since not all JavaScript implementations supp
716ort get/set
717
718	// This can be a url string
719	// or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
720	setSrc: function (url) {
721		if (typeof url == 'string') {
722			this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(url));
723			this.src = mejs.Utility.absolutizeUrl(url);
724		} else {
725			var i, media;
726
727			for (i=0; i<url.length; i++) {
728				media = url[i];
729				if (this.canPlayType(media.type)) {
730					this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(media.src));
731					this.src = mejs.Utility.absolutizeUrl(media.src);
732					break;
733				}
734			}
735		}
736
737	},
738	setCurrentTime: function (time) {
739		if (this.pluginApi != null) {
740			if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
741				this.pluginApi.seekTo(time);
742			} else {
743				this.pluginApi.setCurrentTime(time);
744			}				
745			
746			
747			
748			this.currentTime = time;
749		}
750	},
751	setVolume: function (volume) {
752		if (this.pluginApi != null) {
753			// same on YouTube and MEjs
754			if (this.pluginType == 'youtube') {
755				this.pluginApi.setVolume(volume * 100);
756			} else {
757				this.pluginApi.setVolume(volume);
758			}
759			this.volume = volume;
760		}
761	},
762	setMuted: function (muted) {
763		if (this.pluginApi != null) {
764			if (this.pluginType == 'youtube') {
765				if (muted) {
766					this.pluginApi.mute();
767				} else {
768					this.pluginApi.unMute();
769				}
770				this.muted = muted;
771				this.dispatchEvent({type:'volumechange'});
772			} else {
773				this.pluginApi.setMuted(muted);
774			}
775			this.muted = muted;
776		}
777	},
778
779	// additional non-HTML5 methods
780	setVideoSize: function (width, height) {
781		
782		//if (this.pluginType == 'flash' || this.pluginType == 'silverlight') {
783			if (this.pluginElement && this.pluginElement.style) {
784				this.pluginElement.style.width = width + 'px';
785				this.pluginElement.style.height = height + 'px';
786			}
787			if (this.pluginApi != null && this.pluginApi.setVideoSize) {
788				this.pluginApi.setVideoSize(width, height);
789			}
790		//}
791	},
792
793	setFullscreen: function (fullscreen) {
794		if (this.pluginApi != null && this.pluginApi.setFullscreen) {
795			this.pluginApi.setFullscreen(fullscreen);
796		}
797	},
798	
799	enterFullScreen: function() {
800		if (this.pluginApi != null && this.pluginApi.setFullscreen) {
801			this.setFullscreen(true);
802		}		
803		
804	},
805	
806	exitFullScreen: function() {
807		if (this.pluginApi != null && this.pluginApi.setFullscreen) {
808			this.setFullscreen(false);
809		}
810	},	
811
812	// start: fake events
813	addEventListener: function (eventName, callback, bubble) {
814		this.events[eventName] = this.events[eventName] || [];
815		this.events[eventName].push(callback);
816	},
817	removeEventListener: function (eventName, callback) {
818		if (!eventName) { this.events = {}; return true; }
819		var callbacks = this.events[eventName];
820		if (!callbacks) return true;
821		if (!callback) { this.events[eventName] = []; return true; }
822		for (var i = 0; i < callbacks.length; i++) {
823			if (callbacks[i] === callback) {
824				this.events[eventName].splice(i, 1);
825				return true;
826			}
827		}
828		return false;
829	},	
830	dispatchEvent: function (event) {
831		var i,
832			args,
833			callbacks = this.events[event.type];
834
835		if (callbacks) {
836			for (i = 0; i < callbacks.length; i++) {
837				callbacks[i].apply(this, [event]);
838			}
839		}
840	},
841	// end: fake events
842	
843	// fake DOM attribute methods
844	hasAttribute: function(name){
845		return (name in this.attributes);  
846	},
847	removeAttribute: function(name){
848		delete this.attributes[name];
849	},
850	getAttribute: function(name){
851		if (this.hasAttribute(name)) {
852			return this.attributes[name];
853		}
854		return '';
855	},
856	setAttribute: function(name, value){
857		this.attributes[name] = value;
858	},
859
860	remove: function() {
861		mejs.Utility.removeSwf(this.pluginElement.id);
862		mejs.MediaPluginBridge.unregisterPluginElement(this.pluginElement.id);
863	}
864};
865
866// Handles calls from Flash/Silverlight and reports them as native <video/audio> events and properties
867mejs.MediaPluginBridge = {
868
869	pluginMediaElements:{},
870	htmlMediaElements:{},
871
872	registerPluginElement: function (id, pluginMediaElement, htmlMediaElement) {
873		this.pluginMediaElements[id] = pluginMediaElement;
874		this.htmlMediaElements[id] = htmlMediaElement;
875	},
876
877	unregisterPluginElement: function (id) {
878		delete this.pluginMediaElements[id];
879		delete this.htmlMediaElements[id];
880	},
881
882	// when Flash/Silverlight is ready, it calls out to this method
883	initPlugin: function (id) {
884
885		var pluginMediaElement = this.pluginMediaElements[id],
886			htmlMediaElement = this.htmlMediaElements[id];
887
888		if (pluginMediaElement) {
889			// find the javascript bridge
890			switch (pluginMediaElement.pluginType) {
891				case "flash":
892					pluginMediaElement.pluginElement = pluginMediaElement.pluginApi = document.getElementById(id);
893					break;
894				case "silverlight":
895					pluginMediaElement.pluginElement = document.getElementById(pluginMediaElement.id);
896					pluginMediaElement.pluginApi = pluginMediaElement.pluginElement.Content.MediaElementJS;
897					break;
898			}
899	
900			if (pluginMediaElement.pluginApi != null && pluginMediaElement.success) {
901				pluginMediaElement.success(pluginMediaElement, htmlMediaElement);
902			}
903		}
904	},
905
906	// receives events from Flash/Silverlight and sends them out as HTML5 media events
907	// http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
908	fireEvent: function (id, eventName, values) {
909
910		var
911			e,
912			i,
913			bufferedTime,
914			pluginMediaElement = this.pluginMediaElements[id];
915
916		if(!pluginMediaElement){
917            return;
918        }
919        
920		// fake event object to mimic real HTML media event.
921		e = {
922			type: eventName,
923			target: pluginMediaElement
924		};
925
926		// attach all values to element and event object
927		for (i in values) {
928			pluginMediaElement[i] = values[i];
929			e[i] = values[i];
930		}
931
932		// fake the newer W3C buffered TimeRange (loaded and total have been removed)
933		bufferedTime = values.bufferedTime || 0;
934
935		e.target.buffered = e.buffered = {
936			start: function(index) {
937				return 0;
938			},
939			end: function (index) {
940				return bufferedTime;
941			},
942			length: 1
943		};
944
945		pluginMediaElement.dispatchEvent(e);
946	}
947};
948
949/*
950Default options
951*/
952mejs.MediaElementDefaults = {
953	// allows testing on HTML5, flash, silverlight
954	// auto: attempts to detect what the browser can do
955	// auto_plugin: prefer plugins and then attempt native HTML5
956	// native: forces HTML5 playback
957	// shim: disallows HTML5, will attempt either Flash or Silverlight
958	// none: forces fallback view
959	mode: 'auto',
960	// remove or reorder to change plugin priority and availability
961	plugins: ['flash','silverlight','youtube','vimeo'],
962	// shows debug errors on screen
963	enablePluginDebug: false,
964	// use plugin for browsers that have trouble with Basic Authentication on HTTPS sites
965	httpsBasicAuthSite: false,
966	// overrides the type specified, useful for dynamic instantiation
967	type: '',
968	// path to Flash and Silverlight plugins
969	pluginPath: mejs.Utility.getScriptPath(['mediaelement.js','mediaelement.min.js','mediaelement-and-player.js','mediaelement-and-player.min.js']),
970	// name of flash file
971	flashName: 'flashmediaelement.swf',
972	// streamer for RTMP streaming
973	flashStreamer: '',
974	// set to 'always' for CDN version
975	flashScriptAccess: 'sameDomain',	
976	// turns on the smoothing filter in Flash
977	enablePluginSmoothing: false,
978	// enabled pseudo-streaming (seek) on .mp4 files
979	enablePseudoStreaming: false,
980	// start query parameter sent to server for pseudo-streaming
981	pseudoStreamingStartQueryParam: 'start',
982	// name of silverlight file
983	silverlightName: 'silverlightmediaelement.xap',
984	// default if the <video width> is not specified
985	defaultVideoWidth: 480,
986	// default if the <video height> is not specified
987	defaultVideoHeight: 270,
988	// overrides <video width>
989	pluginWidth: -1,
990	// overrides <video height>
991	pluginHeight: -1,
992	// additional plugin variables in 'key=value' form
993	pluginVars: [],	
994	// rate in milliseconds for Flash and Silverlight to fire the timeupdate event
995	// larger number is less accurate, but less strain on plugin->JavaScript bridge
996	timerRate: 250,
997	// initial volume for player
998	startVolume: 0.8,
999	success: function () { },
1000	error: function () { }
1001};
1002
1003/*
1004Determines if a browser supports the <video> or <audio> element
1005and returns either the native element or a Flash/Silverlight version that
1006mimics HTML5 MediaElement
1007*/
1008mejs.MediaElement = function (el, o) {
1009	return mejs.HtmlMediaElementShim.create(el,o);
1010};
1011
1012mejs.HtmlMediaElementShim = {
1013
1014	create: function(el, o) {
1015		var
1016			options = {},
1017			htmlMediaElement = (typeof(el) == 'string') ? document.getElementById(el) : el,
1018			tagName = htmlMediaElement.tagName.toLowerCase(),
1019			isMediaTag = (tagName === 'audio' || tagName === 'video'),
1020			src = (isMediaTag) ? htmlMediaElement.getAttribute('src') : htmlMediaElement.getAttribute('href'),
1021			poster = htmlMediaElement.getAttribute('poster'),
1022			autoplay =  htmlMediaElement.getAttribute('autoplay'),
1023			preload =  htmlMediaElement.getAttribute('preload'),
1024			controls =  htmlMediaElement.getAttribute('controls'),
1025			playback,
1026			prop;
1027
1028		// extend options
1029		for (prop in mejs.MediaElementDefaults) {
1030			options[prop] = mejs.MediaElementDefaults[prop];
1031		}
1032		for (prop in o) {
1033			options[prop] = o[prop];
1034		}		
1035		
1036
1037		// clean up attributes
1038		src = 		(typeof src == 'undefined' 	|| src === null || src == '') ? null : src;		
1039		poster =	(typeof poster == 'undefined' 	|| poster === null) ? '' : poster;
1040		preload = 	(typeof preload == 'undefined' 	|| preload === null || preload === 'false') ? 'none' : preload;
1041		autoplay = 	!(typeof autoplay == 'undefined' || autoplay === null || autoplay === 'false');
1042		controls = 	!(typeof controls == 'undefined' || controls === null || controls === 'false');
1043
1044		// test for HTML5 and plugin capabilities
1045		playback = this.determinePlayback(htmlMediaElement, options, mejs.MediaFeatures.supportsMediaTag, isMediaTag, src);
1046		playback.url = (playback.url !== null) ? mejs.Utility.absolutizeUrl(playback.url) : '';
1047
1048		if (playback.method == 'native') {
1049			// second fix for android
1050			if (mejs.MediaFeatures.isBustedAndroid) {
1051				htmlMediaElement.src = playback.url;
1052				htmlMediaElement.addEventListener('click', function() {
1053					htmlMediaElement.play();
1054				}, false);
1055			}
1056		
1057			// add methods to native HTMLMediaElement
1058			return this.updateNative(playback, options, autoplay, preload);
1059		} else if (playback.method !== '') {
1060			// create plugin to mimic HTMLMediaElement
1061			
1062			return this.createPlugin( playback,  options, poster, autoplay, preload, controls);
1063		} else {
1064			// boo, no HTML5, no Flash, no Silverlight.
1065			this.createErrorMessage( playback, options, poster );
1066			
1067			return this;
1068		}
1069	},
1070	
1071	determinePlayback: function(htmlMediaElement, options, supportsMediaTag, isMediaTag, src) {
1072		var
1073			mediaFiles = [],
1074			i,
1075			j,
1076			k,
1077			l,
1078			n,
1079			type,
1080			result = { method: '', url: '', htmlMediaElement: htmlMediaElement, isVideo: (htmlMediaElement.tagName.toLowerCase() != 'audio')},
1081			pluginName,
1082			pluginVersions,
1083			pluginInfo,
1084			dummy,
1085			media;
1086			
1087		// STEP 1: Get URL and type from <video src> or <source src>
1088
1089		// supplied type overrides <video type> and <source type>
1090		if (typeof options.type != 'undefined' && options.type !== '') {
1091			
1092			// accept either string or array of types
1093			if (typeof options.type == 'string') {
1094				mediaFiles.push({type:options.type, url:src});
1095			} else {
1096				
1097				for (i=0; i<options.type.length; i++) {
1098					mediaFiles.push({type:options.type[i], url:src});
1099				}
1100			}
1101
1102		// test for src attribute first
1103		} else if (src !== null) {
1104			type = this.formatType(src, htmlMediaElement.getAttribute('type'));
1105			mediaFiles.push({type:type, url:src});
1106
1107		// then test for <source> elements
1108		} else {
1109			// test <source> types to see if they are usable
1110			for (i = 0; i < htmlMediaElement.childNodes.length; i++) {
1111				n = htmlMediaElement.childNodes[i];
1112				if (n.nodeType == 1 && n.tagName.toLowerCase() == 'source') {
1113					src = n.getAttribute('src');
1114					type = this.formatType(src, n.getAttribute('type'));
1115					media = n.getAttribute('media');
1116
1117					if (!media || !window.matchMedia || (window.matchMedia && window.matchMedia(media).matches)) {
1118						mediaFiles.push({type:type, url:src});
1119					}
1120				}
1121			}
1122		}
1123		
1124		// in the case of dynamicly created players
1125		// check for audio types
1126		if (!isMediaTag && mediaFiles.length > 0 && mediaFiles[0].url !== null && this.getTypeFromFile(mediaFiles[0].url).indexOf('audio') > -1) {
1127			result.isVideo = false;
1128		}
1129		
1130
1131		// STEP 2: Test for playback method
1132		
1133		// special case for Android which sadly doesn't implement the canPlayType function (always returns '')
1134		if (mejs.MediaFeatures.isBustedAndroid) {
1135			htmlMediaElement.canPlayType = function(type) {
1136				return (type.match(/video\/(mp4|m4v)/gi) !== null) ? 'maybe' : '';
1137			};
1138		}		
1139		
1140		// special case for Chromium to specify natively supported video codecs (i.e. WebM and Theora) 
1141		if (mejs.MediaFeatures.isChromium) { 
1142			htmlMediaElement.canPlayType = function(type) { 
1143				return (type.match(/video\/(webm|ogv|ogg)/gi) !== null) ? 'maybe' : ''; 
1144			}; 
1145		}
1146
1147		// test for native playback first
1148		if (supportsMediaTag && (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'native')  && !(mejs.MediaFeatures.isBustedNativeHTTPS && options.httpsBasicAuthSite === true)) {
1149						
1150			if (!isMediaTag) {
1151
1152				// create a real HTML5 Media Element 
1153				dummy = document.createElement( result.isVideo ? 'video' : 'audio');			
1154				htmlMediaElement.parentNode.insertBefore(dummy, htmlMediaElement);
1155				htmlMediaElement.style.display = 'none';
1156				
1157				// use this one from now on
1158				result.htmlMediaElement = htmlMediaElement = dummy;
1159			}
1160				
1161			for (i=0; i<mediaFiles.length; i++) {
1162				// normal check
1163				if (mediaFiles[i].type == "video/m3u8" || htmlMediaElement.canPlayType(mediaFiles[i].type).replace(/no/, '') !== ''
1164					// special case for Mac/Safari 5.0.3 which answers '' to canPlayType('audio/mp3') but 'maybe' to canPlayType('audio/mpeg')
1165					|| htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/mp3/,'mpeg')).replace(/no/, '') !== ''
1166					// special case for m4a supported by detecting mp4 support
1167					|| htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/m4a/,'mp4')).replace(/no/, '') !== '') {
1168					result.method = 'native';
1169					result.url = mediaFiles[i].url;
1170					break;
1171				}
1172			}			
1173			
1174			if (result.method === 'native') {
1175				if (result.url !== null) {
1176					htmlMediaElement.src = result.url;
1177				}
1178			
1179				// if `auto_plugin` mode, then cache the native result but try plugins.
1180				if (options.mode !== 'auto_plugin') {
1181					return result;
1182				}
1183			}
1184		}
1185
1186		// if native playback didn't work, then test plugins
1187		if (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'shim') {
1188			for (i=0; i<mediaFiles.length; i++) {
1189				type = mediaFiles[i].type;
1190
1191				// test all plugins in order of preference [silverlight, flash]
1192				for (j=0; j<options.plugins.length; j++) {
1193
1194					pluginName = options.plugins[j];
1195			
1196					// test version of plugin (for future features)
1197					pluginVersions = mejs.plugins[pluginName];				
1198					
1199					for (k=0; k<pluginVersions.length; k++) {
1200						pluginInfo = pluginVersions[k];
1201					
1202						// test if user has the correct plugin version
1203						
1204						// for youtube/vimeo
1205						if (pluginInfo.version == null || 
1206							
1207							mejs.PluginDetector.hasPluginVersion(pluginName, pluginInfo.version)) {
1208
1209							// test for plugin playback types
1210							for (l=0; l<pluginInfo.types.length; l++) {
1211								// find plugin that can play the type
1212								if (type.toLowerCase() == pluginInfo.types[l].toLowerCase()) {
1213									result.method = pluginName;
1214									result.url = mediaFiles[i].url;
1215									return result;
1216								}
1217							}
1218						}
1219					}
1220				}
1221			}
1222		}
1223		
1224		// at this point, being in 'auto_plugin' mode implies that we tried plugins but failed.
1225		// if we have native support then return that.
1226		if (options.mode === 'auto_plugin' && result.method === 'native') {
1227			return result;
1228		}
1229
1230		// what if there's nothing to play? just grab the first available
1231		if (result.method === '' && mediaFiles.length > 0) {
1232			result.url = mediaFiles[0].url;
1233		}
1234
1235		return result;
1236	},
1237
1238	formatType: function(url, type) {
1239		// if no type is supplied, fake it with the extension
1240		if (url && !type) {		
1241			return this.getTypeFromFile(url);
1242		} else {
1243			// only return the mime part of the type in case the attribute contains the codec
1244			// see http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#the-source-element
1245			// `video/mp4; codecs="avc1.42E01E, mp4a.40.2"` becomes `video/mp4`
1246			
1247			if (type && ~type.indexOf(';')) {
1248				return type.substr(0, type.indexOf(';')); 
1249			} else {
1250				return type;
1251			}
1252		}
1253	},
1254	
1255	getTypeFromFile: function(url) {
1256		url = url.split('?')[0];
1257		var
1258			ext = url.substring(url.lastIndexOf('.') + 1).toLowerCase(),
1259			av = /(mp4|m4v|ogg|ogv|m3u8|webm|webmv|flv|wmv|mpeg|mov)/gi.test(ext) ? 'video/' : 'audio/';
1260		return this.getTypeFromExtension(ext, av);
1261	},
1262	
1263	getTypeFromExtension: function(ext, av) {
1264		av = av || '';
1265		
1266		switch (ext) {
1267			case 'mp4':
1268			case 'm4v':
1269			case 'm4a':
1270			case 'f4v':
1271			case 'f4a':
1272				return av + 'mp4';
1273			case 'flv':
1274				return av + 'x-flv';
1275			case 'webm':
1276			case 'webma':
1277			case 'webmv':	
1278				return av + 'webm';
1279			case 'ogg':
1280			case 'oga':
1281			case 'ogv':	
1282				return av + 'ogg';
1283			case 'm3u8':
1284				return 'application/x-mpegurl';
1285			case 'ts':
1286				return av + 'mp2t';
1287			default:
1288				return av + ext;
1289		}
1290	},
1291
1292	createErrorMessage: function(playback, options, poster) {
1293		var 
1294			htmlMediaElement = playback.htmlMediaElement,
1295			errorContainer = document.createElement('div'),
1296			errorContent = options.customError;
1297			
1298		errorContainer.className = 'me-cannotplay';
1299
1300		try {
1301			errorContainer.style.width = htmlMediaElement.width + 'px';
1302			errorContainer.style.height = htmlMediaElement.height + 'px';
1303		} catch (e) {}
1304
1305		if (!errorContent) {
1306			errorContent = '<a href="' + playback.url + '">';
1307
1308			if (poster !== '') {
1309				errorContent += '<img src="' + poster + '" width="100%" height="100%" alt="" />';
1310			}
1311
1312			errorContent += '<span>' + mejs.i18n.t('Download File') + '</span></a>';
1313		}
1314
1315		errorContainer.innerHTML = errorContent;
1316
1317		htmlMediaElement.parentNode.insertBefore(errorContainer, htmlMediaElement);
1318		htmlMediaElement.style.display = 'none';
1319
1320		options.error(htmlMediaElement);
1321	},
1322
1323	createPlugin:function(playback, options, poster, autoplay, preload, controls) {
1324		var 
1325			htmlMediaElement = playback.htmlMediaElement,
1326			width = 1,
1327			height = 1,
1328			pluginid = 'me_' + playback.method + '_' + (mejs.meIndex++),
1329			pluginMediaElement = new mejs.PluginMediaElement(pluginid, playback.method, playback.url),
1330			container = document.createElement('div'),
1331			specialIEContainer,
1332			node,
1333			initVars;
1334
1335		// copy tagName from html media element
1336		pluginMediaElement.tagName = htmlMediaElement.tagName
1337
1338		// copy attributes from html media element to plugin media element
1339		for (var i = 0; i < htmlMediaElement.attributes.length; i++) {
1340			var attribute = htmlMediaElement.attributes[i];
1341			if (attribute.specified) {
1342				pluginMediaElement.setAttribute(attribute.name, attribute.value);
1343			}
1344		}
1345
1346		// check for placement inside a <p> tag (sometimes WYSIWYG editors do this)
1347		node = htmlMediaElement.parentNode;
1348
1349		while (node !== null && node.tagName != null && node.tagName.toLowerCase() !== 'body' && 
1350				node.parentNode != null && node.parentNode.tagName != null && node.parentNode.constructor != null && node.parentNode.constructor.name === "ShadowRoot") {
1351			if (node.parentNode.tagName.toLowerCase() === 'p') {
1352				node.parentNode.parentNode.insertBefore(node, node.parentNode);
1353				break;
1354			}
1355			node = node.parentNode;
1356		}
1357
1358		if (playback.isVideo) {
1359			width = (options.pluginWidth > 0) ? options.pluginWidth : (options.videoWidth > 0) ? options.videoWidth : (htmlMediaElement.getAttribute('width') !== null) ? htmlMediaElement.getAttribute('width') : options.defaultVideoWidth;
1360			height = (options.pluginHeight > 0) ? options.pluginHeight : (options.videoHeight > 0) ? options.videoHeight : (htmlMediaElement.getAttribute('height') !== null) ? htmlMediaElement.getAttribute('height') : options.defaultVideoHeight;
1361		
1362			// in case of '%' make sure it's encoded
1363			width = mejs.Utility.encodeUrl(width);
1364			height = mejs.Utility.encodeUrl(height);
1365		
1366		} else {
1367			if (options.enablePluginDebug) {
1368				width = 320;
1369				height = 240;
1370			}
1371		}
1372
1373		// register plugin
1374		pluginMediaElement.success = options.success;
1375		mejs.MediaPluginBridge.registerPluginElement(pluginid, pluginMediaElement, htmlMediaElement);
1376
1377		// add container (must be added to DOM before inserting HTML for IE)
1378		container.className = 'me-plugin';
1379		container.id = pluginid + '_container';
1380		
1381		if (playback.isVideo) {
1382				htmlMediaElement.parentNode.insertBefore(container, htmlMediaElement);
1383		} else {
1384				document.body.insertBefore(container, document.body.childNodes[0]);
1385		}
1386
1387		// flash/silverlight vars
1388		initVars = [
1389			'id=' + pluginid,
1390			'jsinitfunction=' + "mejs.MediaPluginBridge.initPlugin",
1391			'jscallbackfunction=' + "mejs.MediaPluginBridge.fireEvent",
1392			'isvideo=' + ((playback.isVideo) ? "true" : "false"),
1393			'autoplay=' + ((autoplay) ? "true" : "false"),
1394			'preload=' + preload,
1395			'width=' + width,
1396			'startvolume=' + options.startVolume,
1397			'timerrate=' + options.timerRate,
1398			'flashstreamer=' + options.flashStreamer,
1399			'height=' + height,
1400			'pseudostreamstart=' + options.pseudoStreamingStartQueryParam];
1401
1402		if (playback.url !== null) {
1403			if (playback.method == 'flash') {
1404				initVars.push('file=' + mejs.Utility.encodeUrl(playback.url));
1405			} else {
1406				initVars.push('file=' + playback.url);
1407			}
1408		}
1409		if (options.enablePluginDebug) {
1410			initVars.push('debug=true');
1411		}
1412		if (options.enablePluginSmoothing) {
1413			initVars.push('smoothing=true');
1414		}
1415		if (options.enablePseudoStreaming) {
1416			initVars.push('pseudostreaming=true');
1417		}
1418		if (controls) {
1419			initVars.push('controls=true'); // shows controls in the plugin if desired
1420		}
1421		if (options.pluginVars) {
1422			initVars = initVars.concat(options.pluginVars);
1423		}		
1424
1425		switch (playback.method) {
1426			case 'silverlight':
1427				container.innerHTML =
1428'<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" id="' + pluginid + '" name="' + pluginid + '" width="' + width + '" height="' + height + '" class="mejs-shim">' +
1429'<param name="initParams" value="' + initVars.join(',') + '" />' +
1430'<param name="windowless" value="true" />' +
1431'<param name="background" value="black" />' +
1432'<param name="minRuntimeVersion" value="3.0.0.0" />' +
1433'<param name="autoUpgrade" value="true" />' +
1434'<param name="source" value="' + options.pluginPath + options.silverlightName + '" />' +
1435'</object>';
1436					break;
1437
1438			case 'flash':
1439
1440				if (mejs.MediaFeatures.isIE) {
1441					specialIEContainer = document.createElement('div');
1442					container.appendChild(specialIEContainer);
1443					specialIEContainer.outerHTML =
1444'<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' +
1445'id="' + pluginid + '" width="' + width + '" height="' + height + '" class="mejs-shim">' +
1446'<param name="movie" value="' + options.pluginPath + options.flashName + '?x=' + (new Date()) + '" />' +
1447'<param name="flashvars" value="' + initVars.join('&amp;') + '" />' +
1448'<param name="quality" value="high" />' +
1449'<param name="bgcolor" value="#000000" />' +
1450'<param name="wmode" value="transparent" />' +
1451'<param name="allowScriptAccess" value="' + options.flashScriptAccess + '" />' +
1452'<param name="allowFullScreen" value="true" />' +
1453'<param name="scale" value="default" />' + 
1454'</object>';
1455
1456				} else {
1457
1458					container.innerHTML =
1459'<embed id="' + pluginid + '" name="' + pluginid + '" ' +
1460'play="true" ' +
1461'loop="false" ' +
1462'quality="high" ' +
1463'bgcolor="#000000" ' +
1464'wmode="transparent" ' +
1465'allowScriptAccess="' + options.flashScriptAccess + '" ' +
1466'allowFullScreen="true" ' +
1467'type="application/x-shockwave-flash" pluginspage="//www.macromedia.com/go/getflashplayer" ' +
1468'src="' + options.pluginPath + options.flashName + '" ' +
1469'flashvars="' + initVars.join('&') + '" ' +
1470'width="' + width + '" ' +
1471'height="' + height + '" ' +
1472'scale="default"' + 
1473'class="mejs-shim"></embed>';
1474				}
1475				break;
1476			
1477			case 'youtube':
1478			
1479				
1480				var videoId;
1481				// youtu.be url from share button
1482				if (playback.url.lastIndexOf("youtu.be") != -1) {
1483					videoId = playback.url.substr(playback.url.lastIndexOf('/')+1);
1484					if (videoId.indexOf('?') != -1) {
1485						videoId = videoId.substr(0, videoId.indexOf('?'));
1486					}
1487				}
1488				else {
1489					videoId = playback.url.substr(playback.url.lastIndexOf('=')+1);
1490				}
1491				youtubeSettings = {
1492						container: container,
1493						containerId: container.id,
1494						pluginMediaElement: pluginMediaElement,
1495						pluginId: pluginid,
1496						videoId: videoId,
1497						height: height,
1498						width: width	
1499					};				
1500				
1501				if (mejs.PluginDetector.hasPluginVersion('flash', [10,0,0]) ) {
1502					mejs.YouTubeApi.createFlash(youtubeSettings, options);
1503				} else {
1504					mejs.YouTubeApi.enqueueIframe(youtubeSettings);		
1505				}
1506				
1507				break;
1508			
1509			// DEMO Code. Does NOT work.
1510			case 'vimeo':
1511				var player_id = pluginid + "_player";
1512				pluginMediaElement.vimeoid = playback.url.substr(playback.url.lastIndexOf('/')+1);
1513				
1514				container.innerHTML ='<iframe src="//player.vimeo.com/video/' + pluginMediaElement.vimeoid + '?api=1&portrait=0&byline=0&title=0&player_id=' + player_id + '" width="' + width +'" height="' + height +'" frameborder="0" class="mejs-shim" id="' + player_id + '" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>';
1515				if (typeof($f) == 'function') { // froogaloop available
1516					var player = $f(container.childNodes[0]);
1517					
1518					player.addEvent('ready', function() {
1519						
1520						player.playVideo = function() {
1521							player.api( 'play' );
1522						} 
1523						player.stopVideo = function() {
1524							player.api( 'unload' );
1525						} 
1526						player.pauseVideo = function() {
1527							player.api( 'pause' );
1528						} 
1529						player.seekTo = function( seconds ) {
1530							player.api( 'seekTo', seconds );
1531						}
1532						player.setVolume = function( volume ) {
1533							player.api( 'setVolume', volume );
1534						}
1535						player.setMuted = function( muted ) {
1536							if( muted ) {
1537								player.lastVolume = player.api( 'getVolume' );
1538								player.api( 'setVolume', 0 );
1539							} else {
1540								player.api( 'setVolume', player.lastVolume );
1541								delete player.lastVolume;
1542							}
1543						}						
1544
1545						function createEvent(player, pluginMediaElement, eventName, e) {
1546							var event = {
1547								type: eventName,
1548								target: pluginMediaElement
1549							};
1550							if (eventName == 'timeupdate') {
1551								pluginMediaElement.currentTime = event.currentTime = e.seconds;
1552								pluginMediaElement.duration = event.duration = e.duration;
1553							}
1554							pluginMediaElement.dispatchEvent(event);
1555						}
1556
1557						player.addEvent('play', function() {
1558							createEvent(player, pluginMediaElement, 'play');
1559							createEvent(player, pluginMediaElement, 'playing');
1560						});
1561
1562						player.addEvent('pause', function() {
1563							createEvent(player, pluginMediaElement, 'pause');
1564						});
1565
1566						player.addEvent('finish', function() {
1567							createEvent(player, pluginMediaElement, 'ended');
1568						});
1569
1570						player.addEvent('playProgress', function(e) {
1571							createEvent(player, pluginMediaElement, 'timeupdate', e);
1572						});
1573
1574						pluginMediaElement.pluginElement = container;
1575						pluginMediaElement.pluginApi = player;
1576
1577						// init mejs
1578						mejs.MediaPluginBridge.initPlugin(pluginid);
1579					});
1580				}
1581				else {
1582					console.warn("You need to include froogaloop for vimeo to work");
1583				}
1584				break;			
1585		}
1586		// hide original element
1587		htmlMediaElement.style.display = 'none';
1588		// prevent browser from autoplaying when using a plugin
1589		htmlMediaElement.removeAttribute('autoplay');
1590
1591		// FYI: options.success will be fired by the MediaPluginBridge
1592		
1593		return pluginMediaElement;
1594	},
1595
1596	updateNative: function(playback, options, autoplay, preload) {
1597		
1598		var htmlMediaElement = playback.htmlMediaElement,
1599			m;
1600		
1601		
1602		// add methods to video object to bring it into parity with Flash Object
1603		for (m in mejs.HtmlMediaElement) {
1604			htmlMediaElement[m] = mejs.HtmlMediaElement[m];
1605		}
1606
1607		/*
1608		Chrome now supports preload="none"
1609		if (mejs.MediaFeatures.isChrome) {
1610		
1611			// special case to enforce preload attribute (Chrome doesn't respect this)
1612			if (preload === 'none' && !autoplay) {
1613			
1614				// forces the browser to stop loading (note: fails in IE9)
1615				htmlMediaElement.src = '';
1616				htmlMediaElement.load();
1617				htmlMediaElement.canceledPreload = true;
1618
1619				htmlMediaElement.addEventListener('play',function() {
1620					if (htmlMediaElement.canceledPreload) {
1621						htmlMediaElement.src = playback.url;
1622						htmlMediaElement.load();
1623						htmlMediaElement.play();
1624						htmlMediaElement.canceledPreload = false;
1625					}
1626				}, false);
1627			// for some reason Chrome forgets how to autoplay sometimes.
1628			} else if (autoplay) {
1629				htmlMediaElement.load();
1630				htmlMediaElement.play();
1631			}
1632		}
1633		*/
1634
1635		// fire success code
1636		options.success(htmlMediaElement, htmlMediaElement);
1637		
1638		return htmlMediaElement;
1639	}
1640};
1641
1642/*
1643 - test on IE (object vs. embed)
1644 - determine when to use iframe (Firefox, Safari, Mobile) vs. Flash (Chrome, IE)
1645 - fullscreen?
1646*/
1647
1648// YouTube Flash and Iframe API
1649mejs.YouTubeApi = {
1650	isIframeStarted: false,
1651	isIframeLoaded: false,
1652	loadIframeApi: function() {
1653		if (!this.isIframeStarted) {
1654			var tag = document.createElement('script');
1655			tag.src = "//www.youtube.com/player_api";
1656			var firstScriptTag = document.getElementsByTagName('script')[0];
1657			firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
1658			this.isIframeStarted = true;
1659		}
1660	},
1661	iframeQueue: [],
1662	enqueueIframe: function(yt) {
1663		
1664		if (this.isLoaded) {
1665			this.createIframe(yt);
1666		} else {
1667			this.loadIframeApi();
1668			this.iframeQueue.push(yt);
1669		}
1670	},
1671	createIframe: function(settings) {
1672		
1673		var
1674		pluginMediaElement = settings.pluginMediaElement,	
1675		player = new YT.Player(settings.containerId, {
1676			height: settings.height,
1677			width: settings.width,
1678			videoId: settings.videoId,
1679			playerVars: {controls:0},
1680			events: {
1681				'onReady': function() {
1682					
1683					// hook up iframe object to MEjs
1684					settings.pluginMediaElement.pluginApi = player;
1685					
1686					// init mejs
1687					mejs.MediaPluginBridge.initPlugin(settings.pluginId);
1688					
1689					// create timer
1690					setInterval(function() {
1691						mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'timeupdate');
1692					}, 250);					
1693				},
1694				'onStateChange': function(e) {
1695					
1696					mejs.YouTubeApi.handleStateChange(e.data, player, pluginMediaElement);
1697					
1698				}
1699			}
1700		});
1701	},
1702	
1703	createEvent: function (player, pluginMediaElement, eventName) {
1704		var event = {
1705			type: eventName,
1706			target: pluginMediaElement
1707		};
1708
1709		if (player && player.getDuration) {
1710			
1711			// time 
1712			pluginMediaElement.currentTime = event.currentTime = player.getCurrentTime();
1713			pluginMediaElement.duration = event.duration = player.getDuration();
1714			
1715			// state
1716			event.paused = pluginMediaElement.paused;
1717			event.ended = pluginMediaElement.ended;			
1718			
1719			// sound
1720			event.muted = player.isMuted();
1721			event.volume = player.getVolume() / 100;
1722			
1723			// progress
1724			event.bytesTotal = player.getVideoBytesTotal();
1725			event.bufferedBytes = player.getVideoBytesLoaded();
1726			
1727			// fake the W3C buffered TimeRange
1728			var bufferedTime = event.bufferedBytes / event.bytesTotal * event.duration;
1729			
1730			event.target.buffered = event.buffered = {
1731				start: function(index) {
1732					return 0;
1733				},
1734				end: function (index) {
1735					return bufferedTime;
1736				},
1737				length: 1
1738			};
1739
1740		}
1741		
1742		// send event up the chain
1743		pluginMediaElement.dispatchEvent(event);
1744	},	
1745	
1746	iFrameReady: function() {
1747		
1748		this.isLoaded = true;
1749		this.isIframeLoaded = true;
1750		
1751		while (this.iframeQueue.length > 0) {
1752			var settings = this.iframeQueue.pop();
1753			this.createIframe(settings);
1754		}	
1755	},
1756	
1757	// FLASH!
1758	flashPlayers: {},
1759	createFlash: function(settings) {
1760		
1761		this.flashPlayers[settings.pluginId] = settings;
1762		
1763		/*
1764		settings.container.innerHTML =
1765			'<object type="application/x-shockwave-flash" id="' + settings.pluginId + '" data="//www.youtube.com/apiplayer?enablejsapi=1&amp;playerapiid=' + settings.pluginId  + '&amp;version=3&amp;autoplay=0&amp;controls=0&amp;modestbranding=1&loop=0" ' +
1766				'width="' + settings.width + '" height="' + settings.height + '" style="visibility: visible; " class="mejs-shim">' +
1767				'<param name="allowScriptAccess" value="sameDomain">' +
1768				'<param name="wmode" value="transparent">' +
1769			'</object>';
1770		*/
1771
1772		var specialIEContainer,
1773			youtubeUrl = '//www.youtube.com/apiplayer?enablejsapi=1&amp;playerapiid=' + settings.pluginId  + '&amp;version=3&amp;autoplay=0&amp;controls=0&amp;modestbranding=1&loop=0';
1774			
1775		if (mejs.MediaFeatures.isIE) {
1776			
1777			specialIEContainer = document.createElement('div');
1778			settings.container.appendChild(specialIEContainer);
1779			specialIEContainer.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="//download.macromedia.com/pub/s
1779hockwave/cabs/flash/swflash.cab" ' +
1780'id="' + settings.pluginId + '" width="' + settings.width + '" height="' + settings.height + '" class="mejs-shim">' +
1781	'<param name="movie" value="' + youtubeUrl + '" />' +
1782	'<param name="wmode" value="transparent" />' +
1783	'<param name="allowScriptAccess" value="' + options.flashScriptAccess + '" />' +
1784	'<param name="allowFullScreen" value="true" />' +
1785'</object>';
1786		} else {
1787		settings.container.innerHTML =
1788			'<object type="application/x-shockwave-flash" id="' + settings.pluginId + '" data="' + youtubeUrl + '" ' +
1789				'width="' + settings.width + '" height="' + settings.height + '" style="visibility: visible; " class="mejs-shim">' +
1790				'<param name="allowScriptAccess" value="' + options.flashScriptAccess + '">' +
1791				'<param name="wmode" value="transparent">' +
1792			'</object>';
1793		}		
1794		
1795	},
1796	
1797	flashReady: function(id) {
1798		var
1799			settings = this.flashPlayers[id],
1800			player = document.getElementById(id),
1801			pluginMediaElement = settings.pluginMediaElement;
1802		
1803		// hook up and return to MediaELementPlayer.success	
1804		pluginMediaElement.pluginApi = 
1805		pluginMediaElement.pluginElement = player;
1806		mejs.MediaPluginBridge.initPlugin(id);
1807		
1808		// load the youtube video
1809		player.cueVideoById(settings.videoId);
1810		
1811		var callbackName = settings.containerId + '_callback';
1812		
1813		window[callbackName] = function(e) {
1814			mejs.YouTubeApi.handleStateChange(e, player, pluginMediaElement);
1815		}
1816		
1817		player.addEventListener('onStateChange', callbackName);
1818		
1819		setInterval(function() {
1820			mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'timeupdate');
1821		}, 250);
1822		
1823		mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'canplay');
1824	},
1825	
1826	handleStateChange: function(youTubeState, player, pluginMediaElement) {
1827		switch (youTubeState) {
1828			case -1: // not started
1829				pluginMediaElement.paused = true;
1830				pluginMediaElement.ended = true;
1831				mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'loadedmetadata');
1832				//createYouTubeEvent(player, pluginMediaElement, 'loadeddata');
1833				break;
1834			case 0:
1835				pluginMediaElement.paused = false;
1836				pluginMediaElement.ended = true;
1837				mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'ended');
1838				break;
1839			case 1:
1840				pluginMediaElement.paused = false;
1841				pluginMediaElement.ended = false;				
1842				mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'play');
1843				mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'playing');
1844				break;
1845			case 2:
1846				pluginMediaElement.paused = true;
1847				pluginMediaElement.ended = false;				
1848				mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'pause');
1849				break;
1850			case 3: // buffering
1851				mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'progress');
1852				break;
1853			case 5:
1854				// cued?
1855				break;						
1856			
1857		}			
1858		
1859	}
1860}
1861// IFRAME
1862window.onYouTubePlayerAPIReady = function() {
1863	mejs.YouTubeApi.iFrameReady();
1864};
1865// FLASH
1866window.onYouTubePlayerReady = function(id) {
1867	mejs.YouTubeApi.flashReady(id);
1868};
1869
1870window.mejs = mejs;
1871window.MediaElement = mejs.MediaElement;
1872
1873/*
1874 * Adds Internationalization and localization to mediaelement.
1875 *
1876 * This file does not contain translations, you have to add them manually.
1877 * The schema is always the same: me-i18n-locale-[IETF-language-tag].js
1878 *
1879 * Examples are provided both for german and chinese translation.
1880 *
1881 *
1882 * What is the concept beyond i18n?
1883 *   http://en.wikipedia.org/wiki/Internationalization_and_localization
1884 *
1885 * What langcode should i use?
1886 *   http://en.wikipedia.org/wiki/IETF_language_tag
1887 *   https://tools.ietf.org/html/rfc5646
1888 *
1889 *
1890 * License?
1891 *
1892 *   The i18n file uses methods from the Drupal project (drupal.js):
1893 *     - i18n.methods.t() (modified)
1894 *     - i18n.methods.checkPlain() (full copy)
1895 *
1896 *   The Drupal project is (like mediaelementjs) licensed under GPLv2.
1897 *    - http://drupal.org/licensing/faq/#q1
1898 *    - https://github.com/johndyer/mediaelement
1899 *    - http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
1900 *
1901 *
1902 * @author
1903 *   Tim Latz ([email protected])
1904 *
1905 *
1906 * @params
1907 *  - context - document, iframe ..
1908 *  - exports - CommonJS, window ..
1909 *
1910 */
1911;(function(context, exports, undefined) {
1912    "use strict";
1913
1914    var i18n = {
1915        "locale": {
1916            // Ensure previous values aren't overwritten.
1917            "language" : (exports.i18n && exports.i18n.locale.language) || '',
1918            "strings" : (exports.i18n && exports.i18n.locale.strings) || {}
1919        },
1920        "ietf_lang_regex" : /^(x\-)?[a-z]{2,}(\-\w{2,})?(\-\w{2,})?$/,
1921        "methods" : {}
1922    };
1923// start i18n
1924
1925
1926    /**
1927     * Get language, fallback to browser's language if empty
1928     *
1929     * IETF: RFC 5646, https://tools.ietf.org/html/rfc5646
1930     * Examples: en, zh-CN, cmn-Hans-CN, sr-Latn-RS, es-419, x-private
1931     */
1932    i18n.getLanguage = function () {
1933        var language = i18n.locale.language || window.navigator.userLanguage || window.navigator.language;
1934        return i18n.ietf_lang_regex.exec(language) ? language : null;
1935
1936        //(WAS: convert to iso 639-1 (2-letters, lower case))
1937        //return language.substr(0, 2).toLowerCase();
1938    };
1939
1940    // i18n fixes for compatibility with WordPress
1941    if ( typeof mejsL10n != 'undefined' ) {
1942        i18n.locale.language = mejsL10n.language;
1943    }
1944
1945
1946
1947    /**
1948     * Encode special characters in a plain-text string for display as HTML.
1949     */
1950    i18n.methods.checkPlain = function (str) {
1951        var character, regex,
1952        replace = {
1953            '&': '&amp;',
1954            '"': '&quot;',
1955            '<': '&lt;',
1956            '>': '&gt;'
1957        };
1958        str = String(str);
1959        for (character in replace) {
1960            if (replace.hasOwnProperty(character)) {
1961                regex = new RegExp(character, 'g');
1962                str = str.replace(regex, replace[character]);
1963            }
1964        }
1965        return str;
1966    };
1967
1968    /**
1969     * Translate strings to the page language or a given language.
1970     *
1971     *
1972     * @param str
1973     *   A string containing the English string to translate.
1974     *
1975     * @param options
1976     *   - 'context' (defaults to the default context): The context the source string
1977     *     belongs to.
1978     *
1979     * @return
1980     *   The translated string, escaped via i18n.methods.checkPlain()
1981     */
1982    i18n.methods.t = function (str, options) {
1983
1984        // Fetch the localized version of the string.
1985        if (i18n.locale.strings && i18n.locale.strings[options.context] && i18n.locale.strings[options.context][str]) {
1986            str = i18n.locale.strings[options.context][str];
1987        }
1988
1989        return i18n.methods.checkPlain(str);
1990    };
1991
1992
1993    /**
1994     * Wrapper for i18n.methods.t()
1995     *
1996     * @see i18n.methods.t()
1997     * @throws InvalidArgumentException
1998     */
1999    i18n.t = function(str, options) {
2000
2001        if (typeof str === 'string' && str.length > 0) {
2002
2003            // check every time due language can change for
2004            // different reasons (translation, lang switcher ..)
2005            var language = i18n.getLanguage();
2006
2007            options = options || {
2008                "context" : language
2009            };
2010
2011            return i18n.methods.t(str, options);
2012        }
2013        else {
2014            throw {
2015                "name" : 'InvalidArgumentException',
2016                "message" : 'First argument is either not a string or empty.'
2017            };
2018        }
2019    };
2020
2021// end i18n
2022    exports.i18n = i18n;
2023}(document, mejs));
2024
2025// i18n fixes for compatibility with WordPress
2026;(function(exports, undefined) {
2027
2028    "use strict";
2029
2030    if ( typeof mejsL10n != 'undefined' ) {
2031        exports[mejsL10n.language] = mejsL10n.strings;
2032    }
2033
2034}(mejs.i18n.locale.strings));
2035
2036/*!
2037 *
2038 * MediaElementPlayer
2039 * http://mediaelementjs.com/
2040 *
2041 * Creates a controller bar for HTML5 <video> add <audio> tags
2042 * using jQuery and MediaElement.js (HTML5 Flash/Silverlight wrapper)
2043 *
2044 * Copyright 2010-2013, John Dyer (http://j.hn/)
2045 * License: MIT
2046 *
2047 */
2048if (typeof jQuery != 'undefined') {
2049	mejs.$ = jQuery;
2050} else if (typeof Zepto != 'undefined') {
2051	mejs.$ = Zepto;
2052
2053	// define `outerWidth` method which has not been realized in Zepto
2054	Zepto.fn.outerWidth = function(includeMargin) {
2055		var width = $(this).width();
2056		if (includeMargin) {
2057			width += parseInt($(this).css('margin-right'), 10);
2058			width += parseInt($(this).css('margin-left'), 10);
2059		}
2060		return width
2061	}
2062
2063} else if (typeof ender != 'undefined') {
2064	mejs.$ = ender;
2065}
2066(function ($) {
2067
2068	// default player values
2069	mejs.MepDefaults = {
2070		// url to poster (to fix iOS 3.x)
2071		poster: '',
2072		// When the video is ended, we can show the poster.
2073		showPosterWhenEnded: false,
2074		// default if the <video width> is not specified
2075		defaultVideoWidth: 480,
2076		// default if the <video height> is not specified
2077		defaultVideoHeight: 270,
2078		// if set, overrides <video width>
2079		videoWidth: -1,
2080		// if set, overrides <video height>
2081		videoHeight: -1,
2082		// default if the user doesn't specify
2083		defaultAudioWidth: 400,
2084		// default if the user doesn't specify
2085		defaultAudioHeight: 30,
2086
2087		// default amount to move back when back key is pressed
2088		defaultSeekBackwardInterval: function(media) {
2089			return (media.duration * 0.05);
2090		},
2091		// default amount to move forward when forward key is pressed
2092		defaultSeekForwardInterval: function(media) {
2093			return (media.duration * 0.05);
2094		},
2095
2096		// set dimensions via JS instead of CSS
2097		setDimensions: true,
2098
2099		// width of audio player
2100		audioWidth: -1,
2101		// height of audio player
2102		audioHeight: -1,
2103		// initial volume when the player starts (overrided by user cookie)
2104		startVolume: 0.8,
2105		// useful for <audio> player loops
2106		loop: false,
2107		// rewind to beginning when media ends
2108                autoRewind: true,
2109		// resize to media dimensions
2110		enableAutosize: true,
2111
2112		/*
2113		 * Time format to use. Default: 'mm:ss'
2114		 * Supported units:
2115		 *   h: hour
2116		 *   m: minute
2117		 *   s: second
2118		 *   f: frame count
2119		 * When using 'hh', 'mm', 'ss' or 'ff' we always display 2 digits.
2120		 * If you use 'h', 'm', 's' or 'f' we display 1 digit if possible.
2121		 *
2122		 * Example to display 75 seconds:
2123		 * Format 'mm:ss': 01:15
2124		 * Format 'm:ss': 1:15
2125		 * Format 'm:s': 1:15
2126		 */
2127		timeFormat: '',
2128		// forces the hour marker (##:00:00)
2129		alwaysShowHours: false,
2130		// show framecount in timecode (##:00:00:00)
2131		showTimecodeFrameCount: false,
2132		// used when showTimecodeFrameCount is set to true
2133		framesPerSecond: 25,
2134
2135		// automatically calculate the width of the progress bar based on the sizes of other elements
2136		autosizeProgress : true,
2137		// Hide controls when playing and mouse is not over the video
2138		alwaysShowControls: false,
2139		// Display the video control
2140		hideVideoControlsOnLoad: false,
2141		// Enable click video element to toggle play/pause
2142		clickToPlayPause: true,
2143		// force iPad's native controls
2144		iPadUseNativeControls: false,
2145		// force iPhone's native controls
2146		iPhoneUseNativeControls: false,
2147		// force Android's native controls
2148		AndroidUseNativeControls: false,
2149		// features to show
2150		features: ['playpause','current','progress','duration','tracks','volume','fullscreen'],
2151		// only for dynamic
2152		isVideo: true,
2153
2154		// turns keyboard support on and off for this instance
2155		enableKeyboard: true,
2156
2157		// whenthis player starts, it will pause other players
2158		pauseOtherPlayers: true,
2159
2160		// array of keyboard actions such as play pause
2161		keyActions: [
2162				{
2163						keys: [
2164								32, // SPACE
2165								179 // GOOGLE play/pause button
2166							  ],
2167						action: function(player, media) {
2168								if (media.paused || media.ended) {
2169										media.play();
2170								} else {
2171										media.pause();
2172								}
2173						}
2174				},
2175				{
2176						keys: [38], // UP
2177						action: function(player, media) {
2178								player.container.find('.mejs-volume-slider').css('display','block');
2179								if (player.isVideo) {
2180										player.showControls();
2181										player.startControlsTimer();
2182								}
2183
2184								var newVolume = Math.min(media.volume + 0.1, 1);
2185								media.setVolume(newVolume);
2186						}
2187				},
2188				{
2189						keys: [40], // DOWN
2190						action: function(player, media) {
2191								player.container.find('.mejs-volume-slider').css('display','block');
2192								if (player.isVideo) {
2193										player.showControls();
2194										player.startControlsTimer();
2195								}
2196
2197								var newVolume = Math.max(media.volume - 0.1, 0);
2198								media.setVolume(newVolume);
2199						}
2200				},
2201				{
2202						keys: [
2203								37, // LEFT
2204								227 // Google TV rewind
2205						],
2206						action: function(player, media) {
2207								if (!isNaN(media.duration) && media.duration > 0) {
2208										if (player.isVideo) {
2209												player.showControls();
2210												player.startControlsTimer();
2211										}
2212
2213										// 5%
2214										var newTime = Math.max(media.currentTime - player.options.defaultSeekBackwardInterval(media), 0);
2215										media.setCurrentTime(newTime);
2216								}
2217						}
2218				},
2219				{
2220						keys: [
2221								39, // RIGHT
2222								228 // Google TV forward
2223						],
2224						action: function(player, media) {
2225								if (!isNaN(media.duration) && media.duration > 0) {
2226										if (player.isVideo) {
2227												player.showControls();
2228												player.startControlsTimer();
2229										}
2230
2231										// 5%
2232										var newTime = Math.min(media.currentTime + player.options.defaultSeekForwardInterval(media), media.duration);
2233										media.setCurrentTime(newTime);
2234								}
2235						}
2236				},
2237				{
2238						keys: [70], // F
2239						action: function(player, media) {
2240								if (typeof player.enterFullScreen != 'undefined') {
2241										if (player.isFullScreen) {
2242												player.exitFullScreen();
2243										} else {
2244												player.enterFullScreen();
2245										}
2246								}
2247						}
2248				},
2249				{
2250						keys: [77], // M
2251						action: function(player, media) {
2252								player.container.find('.mejs-volume-slider').css('display','block');
2253								if (player.isVideo) {
2254										player.showControls();
2255										player.startControlsTimer();
2256								}
2257								if (player.media.muted) {
2258										player.setMuted(false);
2259								} else {
2260										player.setMuted(true);
2261								}
2262						}
2263				}
2264		]
2265	};
2266
2267	mejs.mepIndex = 0;
2268
2269	mejs.players = {};
2270
2271	// wraps a MediaElement object in player controls
2272	mejs.MediaElementPlayer = function(node, o) {
2273		// enforce object, even without "new" (via John Resig)
2274		if ( !(this instanceof mejs.MediaElementPlayer) ) {
2275			return new mejs.MediaElementPlayer(node, o);
2276		}
2277
2278		var t = this;
2279
2280		// these will be reset after the MediaElement.success fires
2281		t.$media = t.$node = $(node);
2282		t.node = t.media = t.$media[0];
2283
2284		if(!t.node) {
2285			return
2286		}
2287
2288		// check for existing player
2289		if (typeof t.node.player != 'undefined') {
2290			return t.node.player;
2291		}
2292
2293
2294		// try to get options from data-mejsoptions
2295		if (typeof o == 'undefined') {
2296			o = t.$node.data('mejsoptions');
2297		}
2298
2299		// extend default options
2300		t.options = $.extend({},mejs.MepDefaults,o);
2301
2302		if (!t.options.timeFormat) {
2303			// Generate the time format according to options
2304			t.options.timeFormat = 'mm:ss';
2305			if (t.options.alwaysShowHours) {
2306				t.options.timeFormat = 'hh:mm:ss';
2307			}
2308			if (t.options.showTimecodeFrameCount) {
2309				t.options.timeFormat += ':ff';
2310			}
2311		}
2312
2313		mejs.Utility.calculateTimeFormat(0, t.options, t.options.framesPerSecond || 25);
2314
2315		// unique ID
2316		t.id = 'mep_' + mejs.mepIndex++;
2317
2318		// add to player array (for focus events)
2319		mejs.players[t.id] = t;
2320
2321		// start up
2322		t.init();
2323
2324		return t;
2325	};
2326
2327	// actual player
2328	mejs.MediaElementPlayer.prototype = {
2329
2330		hasFocus: false,
2331
2332		controlsAreVisible: true,
2333
2334		init: function() {
2335
2336			var
2337				t = this,
2338				mf = mejs.MediaFeatures,
2339				// options for MediaElement (shim)
2340				meOptions = $.extend(true, {}, t.options, {
2341					success: function(media, domNode) { t.meReady(media, domNode); },
2342					error: function(e) { t.handleError(e);}
2343				}),
2344				tagName = t.media.tagName.toLowerCase();
2345
2346			t.isDynamic = (tagName !== 'audio' && tagName !== 'video');
2347
2348			if (t.isDynamic) {
2349				// get video from src or href?
2350				t.isVideo = t.options.isVideo;
2351			} else {
2352				t.isVideo = (tagName !== 'audio' && t.options.isVideo);
2353			}
2354
2355			// use native controls in iPad, iPhone, and Android
2356			if ((mf.isiPad && t.options.iPadUseNativeControls) || (mf.isiPhone && t.options.iPhoneUseNativeControls)) {
2357
2358				// add controls and stop
2359				t.$media.attr('controls', 'controls');
2360
2361				// attempt to fix iOS 3 bug
2362				//t.$media.removeAttr('poster');
2363                                // no Issue found on iOS3 -ttroxell
2364
2365				// override Apple's autoplay override for iPads
2366				if (mf.isiPad && t.media.getAttribute('autoplay') !== null) {
2367					t.play();
2368				}
2369
2370			} else if (mf.isAndroid && t.options.AndroidUseNativeControls) {
2371
2372				// leave default player
2373
2374			} else {
2375
2376				// DESKTOP: use MediaElementPlayer controls
2377
2378				// remove native controls
2379				t.$media.removeAttr('controls');
2380				var videoPlayerTitle = t.isVideo ?
2381					mejs.i18n.t('Video Player') : mejs.i18n.t('Audio Player');
2382				// insert description for screen readers
2383				$('<span class="mejs-offscreen">' + videoPlayerTitle + '</span>').insertBefore(t.$media);
2384				// build container
2385				t.container =
2386					$('<div id="' + t.id + '" class="mejs-container ' + (mejs.MediaFeatures.svg ? 'svg' : 'no-svg') +
2387                      '" tabindex="0" role="application" aria-label="' + videoPlayerTitle + '">'+
2388						'<div class="mejs-inner">'+
2389							'<div class="mejs-mediaelement"></div>'+
2390							'<div class="mejs-layers"></div>'+
2391							'<div class="mejs-controls"></div>'+
2392							'<div class="mejs-clear"></div>'+
2393						'</div>' +
2394					'</div>')
2395					.addClass(t.$media[0].className)
2396					.insertBefore(t.$media)
2397					.focus(function ( e ) {
2398						if( !t.controlsAreVisible ) {
2399							t.showControls(true);
2400							var playButton = t.container.find('.mejs-playpause-button > button');
2401							playButton.focus();
2402						}
2403					});
2404
2405				// add classes for user and content
2406				t.container.addClass(
2407					(mf.isAndroid ? 'mejs-android ' : '') +
2408					(mf.isiOS ? 'mejs-ios ' : '') +
2409					(mf.isiPad ? 'mejs-ipad ' : '') +
2410					(mf.isiPhone ? 'mejs-iphone ' : '') +
2411					(t.isVideo ? 'mejs-video ' : 'mejs-audio ')
2412				);
2413
2414
2415				// move the <video/video> tag into the right spot
2416				if (mf.isiOS) {
2417
2418					// sadly, you can't move nodes in iOS, so we have to destroy and recreate it!
2419					var $newMedia = t.$media.clone();
2420
2421					t.container.find('.mejs-mediaelement').append($newMedia);
2422
2423					t.$media.remove();
2424					t.$node = t.$media = $newMedia;
2425					t.node = t.media = $newMedia[0];
2426
2427				} else {
2428
2429					// normal way of moving it into place (doesn't work on iOS)
2430					t.container.find('.mejs-mediaelement').append(t.$media);
2431				}
2432
2433				// needs to be assigned here, after iOS remap
2434				t.node.player = t;
2435
2436				// find parts
2437				t.controls = t.container.find('.mejs-controls');
2438				t.layers = t.container.find('.mejs-layers');
2439
2440				// determine the size
2441
2442				/* size priority:
2443					(1) videoWidth (forced),
2444					(2) style="width;height;"
2445					(3) width attribute,
2446					(4) defaultVideoWidth (for unspecified cases)
2447				*/
2448
2449				var tagType = (t.isVideo ? 'video' : 'audio'),
2450					capsTagName = tagType.substring(0,1).toUpperCase() + tagType.substring(1);
2451
2452
2453
2454				if (t.options[tagType + 'Width'] > 0 || t.options[tagType + 'Width'].toString().indexOf('%') > -1) {
2455					t.width = t.options[tagType + 'Width'];
2456				} else if (t.media.style.width !== '' && t.media.style.width !== null) {
2457					t.width = t.media.style.width;
2458				} else if (t.media.getAttribute('width') !== null) {
2459					t.width = t.$media.attr('width');
2460				} else {
2461					t.width = t.options['default' + capsTagName + 'Width'];
2462				}
2463
2464				if (t.options[tagType + 'Height'] > 0 || t.options[tagType + 'Height'].toString().indexOf('%') > -1) {
2465					t.height = t.options[tagType + 'Height'];
2466				} else if (t.media.style.height !== '' && t.media.style.height !== null) {
2467					t.height = t.media.style.height;
2468				} else if (t.$media[0].getAttribute('height') !== null) {
2469					t.height = t.$media.attr('height');
2470				} else {
2471					t.height = t.options['default' + capsTagName + 'Height'];
2472				}
2473
2474				// set the size, while we wait for the plugins to load below
2475				t.setPlayerSize(t.width, t.height);
2476
2477				// create MediaElementShim
2478				meOptions.pluginWidth = t.width;
2479				meOptions.pluginHeight = t.height;
2480			}
2481
2482			// create MediaElement shim
2483			mejs.MediaElement(t.$media[0], meOptions);
2484
2485			if (typeof(t.container) != 'undefined' && t.controlsAreVisible){
2486				// controls are shown when loaded
2487				t.container.trigger('controlsshown');
2488			}
2489		},
2490
2491		showControls: function(doAnimation) {
2492			var t = this;
2493
2494			doAnimation = typeof doAnimation == 'undefined' || doAnimation;
2495
2496			if (t.controlsAreVisible)
2497				return;
2498
2499			if (doAnimation) {
2500				t.controls
2501					.css('visibility','visible')
2502					.stop(true, true).fadeIn(200, function() {
2503						t.controlsAreVisible = true;
2504						t.container.trigger('controlsshown');
2505					});
2506
2507				// any additional controls people might add and want to hide
2508				t.container.find('.mejs-control')
2509					.css('visibility','visible')
2510					.stop(true, true).fadeIn(200, function() {t.controlsAreVisible = true;});
2511
2512			} else {
2513				t.controls
2514					.css('visibility','visible')
2515					.css('display','block');
2516
2517				// any additional controls people might add and want to hide
2518				t.container.find('.mejs-control')
2519					.css('visibility','visible')
2520					.css('display','block');
2521
2522				t.controlsAreVisible = true;
2523				t.container.trigger('controlsshown');
2524			}
2525
2526			t.setControlsSize();
2527
2528		},
2529
2530		hideControls: function(doAnimation) {
2531			var t = this;
2532
2533			doAnimation = typeof doAnimation == 'undefined' || doAnimation;
2534
2535			if (!t.controlsAreVisible || t.options.alwaysShowControls || t.keyboardAction)
2536				return;
2537
2538			if (doAnimation) {
2539				// fade out main controls
2540				t.controls.stop(true, true).fadeOut(200, function() {
2541					$(this)
2542						.css('visibility','hidden')
2543						.css('display','block');
2544
2545					t.controlsAreVisible = false;
2546					t.container.trigger('controlshidden');
2547				});
2548
2549				// any additional controls people might add and want to hide
2550				t.container.find('.mejs-control').stop(true, true).fadeOut(200, function() {
2551					$(this)
2552						.css('visibility','hidden')
2553						.css('display','block');
2554				});
2555			} else {
2556
2557				// hide main controls
2558				t.controls
2559					.css('visibility','hidden')
2560					.css('display','block');
2561
2562				// hide others
2563				t.container.find('.mejs-control')
2564					.css('visibility','hidden')
2565					.css('display','block');
2566
2567				t.controlsAreVisible = false;
2568				t.container.trigger('controlshidden');
2569			}
2570		},
2571
2572		controlsTimer: null,
2573
2574		startControlsTimer: function(timeout) {
2575
2576			var t = this;
2577
2578			timeout = typeof timeout != 'undefined' ? timeout : 1500;
2579
2580			t.killControlsTimer('start');
2581
2582			t.controlsTimer = setTimeout(function() {
2583				//
2584				t.hideControls();
2585				t.killControlsTimer('hide');
2586			}, timeout);
2587		},
2588
2589		killControlsTimer: function(src) {
2590
2591			var t = this;
2592
2593			if (t.controlsTimer !== null) {
2594				clearTimeout(t.controlsTimer);
2595				delete t.controlsTimer;
2596				t.controlsTimer = null;
2597			}
2598		},
2599
2600		controlsEnabled: true,
2601
2602		disableControls: function() {
2603			var t= this;
2604
2605			t.killControlsTimer();
2606			t.hideControls(false);
2607			this.controlsEnabled = false;
2608		},
2609
2610		enableControls: function() {
2611			var t= this;
2612
2613			t.showControls(false);
2614
2615			t.controlsEnabled = true;
2616		},
2617
2618
2619		// Sets up all controls and events
2620		meReady: function(media, domNode) {
2621
2622
2623			var t = this,
2624				mf = mejs.MediaFeatures,
2625				autoplayAttr = domNode.getAttribute('autoplay'),
2626				autoplay = !(typeof autoplayAttr == 'undefined' || autoplayAttr === null || autoplayAttr === 'false'),
2627				featureIndex,
2628				feature;
2629
2630			// make sure it can't create itself again if a plugin reloads
2631			if (t.created) {
2632				return;
2633			} else {
2634				t.created = true;
2635			}
2636
2637			t.media = media;
2638			t.domNode = domNode;
2639
2640			if (!(mf.isAndroid && t.options.AndroidUseNativeControls) && !(mf.isiPad && t.options.iPadUseNativeControls) && !(mf.isiPhone && t.options.iPhoneUseNativeControls)) {
2641
2642				// two built in features
2643				t.buildposter(t, t.controls, t.layers, t.media);
2644				t.buildkeyboard(t, t.controls, t.layers, t.media);
2645				t.buildoverlays(t, t.controls, t.layers, t.media);
2646
2647				// grab for use by features
2648				t.findTracks();
2649
2650				// add user-defined features/controls
2651				for (featureIndex in t.options.features) {
2652					feature = t.options.features[featureIndex];
2653					if (t['build' + feature]) {
2654						try {
2655							t['build' + feature](t, t.controls, t.layers, t.media);
2656						} catch (e) {
2657							// TODO: report control error
2658							//throw e;
2659							
2660							
2661						}
2662					}
2663				}
2664
2665				t.container.trigger('controlsready');
2666
2667				// reset all layers and controls
2668				t.setPlayerSize(t.width, t.height);
2669				t.setControlsSize();
2670
2671
2672				// controls fade
2673				if (t.isVideo) {
2674
2675					if (mejs.MediaFeatures.hasTouch) {
2676
2677						// for touch devices (iOS, Android)
2678						// show/hide without animation on touch
2679
2680						t.$media.bind('touchstart', function() {
2681
2682
2683							// toggle controls
2684							if (t.controlsAreVisible) {
2685								t.hideControls(false);
2686							} else {
2687								if (t.controlsEnabled) {
2688									t.showControls(false);
2689								}
2690							}
2691						});
2692
2693					} else {
2694
2695						// create callback here since it needs access to current
2696						// MediaElement object
2697						t.clickToPlayPauseCallback = function() {
2698							//
2699
2700							if (t.options.clickToPlayPause) {
2701								if (t.media.paused) {
2702									t.play();
2703								} else {
2704									t.pause();
2705								}
2706							}
2707						};
2708
2709						// click to play/pause
2710						t.media.addEventListener('click', t.clickToPlayPauseCallback, false);
2711
2712						// show/hide controls
2713						t.container
2714							.bind('mouseenter', function () {
2715								if (t.controlsEnabled) {
2716									if (!t.options.alwaysShowControls ) {
2717										t.killControlsTimer('enter');
2718										t.showControls();
2719										t.startControlsTimer(2500);
2720									}
2721								}
2722							})
2723							.bind('mousemove', function() {
2724								if (t.controlsEnabled) {
2725									if (!t.controlsAreVisible) {
2726										t.showControls();
2727									}
2728									if (!t.options.alwaysShowControls) {
2729										t.startControlsTimer(2500);
2730									}
2731								}
2732							})
2733							.bind('mouseleave', function () {
2734								if (t.controlsEnabled) {
2735									if (!t.media.paused && !t.options.alwaysShowControls) {
2736										t.startControlsTimer(1000);
2737									}
2738								}
2739							});
2740					}
2741
2742					if(t.options.hideVideoControlsOnLoad) {
2743						t.hideControls(false);
2744					}
2745
2746					// check for autoplay
2747					if (autoplay && !t.options.alwaysShowControls) {
2748						t.hideControls();
2749					}
2750
2751					// resizer
2752					if (t.options.enableAutosize) {
2753						t.media.addEventListener('loadedmetadata', function(e) {
2754							// if the <video height> was not set and the options.videoHeight was not set
2755							// then resize to the real dimensions
2756							if (t.options.videoHeight <= 0 && t.domNode.getAttribute('height') === null && !isNaN(e.target.videoHeight)) {
2757								t.setPlayerSize(e.target.videoWidth, e.target.videoHeight);
2758								t.setControlsSize();
2759								t.media.setVideoSize(e.target.videoWidth, e.target.videoHeight);
2760							}
2761						}, false);
2762					}
2763				}
2764
2765				// EVENTS
2766
2767				// FOCUS: when a video starts playing, it takes focus from other players (possibily pausing them)
2768				media.addEventListener('play', function() {
2769					var playerIndex;
2770
2771					// go through all other players
2772					for (playerIndex in mejs.players) {
2773						var p = mejs.players[playerIndex];
2774						if (p.id != t.id && t.options.pauseOtherPlayers && !p.paused && !p.ended) {
2775							p.pause();
2776						}
2777						p.hasFocus = false;
2778					}
2779
2780					t.hasFocus = true;
2781				},false);
2782
2783
2784				// ended for all
2785				t.media.addEventListener('ended', function (e) {
2786					if(t.options.autoRewind) {
2787						try{
2788							t.media.setCurrentTime(0);
2789                            // Fixing an Android stock browser bug, where "seeked" isn't fired correctly after ending the video and jumping to the beginning
2790                            window.setTimeout(function(){
2791                                $(t.container).find('.mejs-overlay-loading').parent().hide();
2792                            }, 20);
2793						} catch (exp) {
2794
2795						}
2796					}
2797					t.media.pause();
2798
2799					if (t.setProgressRail) {
2800						t.setProgressRail();
2801					}
2802					if (t.setCurrentRail) {
2803						t.setCurrentRail();
2804					}
2805
2806					if (t.options.loop) {
2807						t.play();
2808					} else if (!t.options.alwaysShowControls && t.controlsEnabled) {
2809						t.showControls();
2810					}
2811				}, false);
2812
2813				// resize on the first play
2814				t.media.addEventListener('loadedmetadata', function(e) {
2815					if (t.updateDuration) {
2816						t.updateDuration();
2817					}
2818					if (t.updateCurrent) {
2819						t.updateCurrent();
2820					}
2821
2822					if (!t.isFullScreen) {
2823						t.setPlayerSize(t.width, t.height);
2824						t.setControlsSize();
2825					}
2826				}, false);
2827
2828				// Only change the time format when necessary
2829				var duration = null;
2830				t.media.addEventListener('timeupdate',function() {
2831					if (duration !== this.duration) {
2832						duration = this.duration;
2833						mejs.Utility.calculateTimeFormat(duration, t.options, t.options.framesPerSecond || 25);
2834					}
2835				}, false);
2836
2837				t.container.focusout(function (e) {
2838					if( e.relatedTarget ) { //FF is working on supporting focusout https://bugzilla.mozilla.org/show_bug.cgi?id=687787
2839						var $target = $(e.relatedTarget);
2840						if (t.keyboardAction && $target.parents('.mejs-container').length === 0) {
2841							t.keyboardAction = false;
2842							t.hideControls(true);
2843						}
2844					}
2845				});
2846
2847				// webkit has trouble doing this without a delay
2848				setTimeout(function () {
2849					t.setPlayerSize(t.width, t.height);
2850					t.setControlsSize();
2851				}, 50);
2852
2853				// adjust controls whenever window sizes (used to be in fullscreen only)
2854				t.globalBind('resize', function() {
2855
2856					// don't resize for fullscreen mode
2857					if ( !(t.isFullScreen || (mejs.MediaFeatures.hasTrueNativeFullScreen && document.webkitIsFullScreen)) ) {
2858						t.setPlayerSize(t.width, t.height);
2859					}
2860
2861					// always adjust controls
2862					t.setControlsSize();
2863				});
2864
2865				// This is a work-around for a bug in the YouTube iFrame player, which means
2866				//  we can't use the play() API for the initial playback on iOS or Android;
2867				//  user has to start playback directly by tapping on the iFrame.
2868				if (t.media.pluginType == 'youtube' && ( mf.isiOS || mf.isAndroid ) ) {
2869					t.container.find('.mejs-overlay-play').hide();
2870				}
2871			}
2872
2873			// force autoplay for HTML5
2874			if (autoplay && media.pluginType == 'native') {
2875				t.play();
2876			}
2877
2878
2879			if (t.options.success) {
2880
2881				if (typeof t.options.success == 'string') {
2882					window[t.options.success](t.media, t.domNode, t);
2883				} else {
2884					t.options.success(t.media, t.domNode, t);
2885				}
2886			}
2887		},
2888
2889		handleError: function(e) {
2890			var t = this;
2891
2892			t.controls.hide();
2893
2894			// Tell user that the file cannot be played
2895			if (t.options.error) {
2896				t.options.error(e);
2897			}
2898		},
2899
2900		setPlayerSize: function(width,height) {
2901			var t = this;
2902
2903			if( !t.options.setDimensions ) {
2904				return false;
2905			}
2906
2907			if (typeof width != 'undefined') {
2908				t.width = width;
2909			}
2910
2911			if (typeof height != 'undefined') {
2912				t.height = height;
2913			}
2914
2915			// detect 100% mode - use currentStyle for IE since css() doesn't return percentages
2916			if (t.height.toString().indexOf('%') > 0 || (t.$node.css('max-width') !== 'none' && t.$node.css('max-width') !== 't.width') || (t.$node[0].currentStyle && t.$node[0].currentStyle.maxWidth === '100%')) {
2917
2918				// do we have the native dimensions yet?
2919				var nativeWidth = (function() {
2920					if (t.isVideo) {
2921						if (t.media.videoWidth && t.media.videoWidth > 0) {
2922							return t.media.videoWidth;
2923						} else if (t.media.getAttribute('width') !== null) {
2924							return t.media.getAttribute('width');
2925						} else {
2926							return t.options.defaultVideoWidth;
2927						}
2928					} else {
2929						return t.options.defaultAudioWidth;
2930					}
2931				})();
2932
2933				var nativeHeight = (function() {
2934					if (t.isVideo) {
2935						if (t.media.videoHeight && t.media.videoHeight > 0) {
2936							return t.media.videoHeight;
2937						} else if (t.media.getAttribute('height') !== null) {
2938							return t.media.getAttribute('height');
2939						} else {
2940							return t.options.defaultVideoHeight;
2941						}
2942					} else {
2943						return t.options.defaultAudioHeight;
2944					}
2945				})();
2946
2947				var
2948					parentWidth = t.container.parent().closest(':visible').width(),
2949					parentHeight = t.container.parent().closest(':visible').height(),
2950					newHeight = t.isVideo || !t.options.autosizeProgress ? parseInt(parentWidth * nativeHeight/nativeWidth, 10) : nativeHeight;
2951
2952				// When we use percent, the newHeight can't be calculated so we get the container height
2953				if (isNaN(newHeight)) {
2954					newHeight = parentHeight;
2955				}
2956
2957				if (t.container.parent().length > 0 && t.container.parent()[0].tagName.toLowerCase() === 'body') { // && t.container.siblings().count == 0) {
2958					parentWidth = $(window).width();
2959					newHeight = $(window).height();
2960				}
2961
2962				if ( newHeight && parentWidth ) {
2963
2964					// set outer container size
2965					t.container
2966						.width(parentWidth)
2967						.height(newHeight);
2968
2969					// set native <video> or <audio> and shims
2970					t.$media.add(t.container.find('.mejs-shim'))
2971						.width('100%')
2972						.height('100%');
2973
2974					// if shim is ready, send the size to the embeded plugin
2975					if (t.isVideo) {
2976						if (t.media.setVideoSize) {
2977							t.media.setVideoSize(parentWidth, newHeight);
2978						}
2979					}
2980
2981					// set the layers
2982					t.layers.children('.mejs-layer')
2983						.width('100%')
2984						.height('100%');
2985				}
2986
2987
2988			} else {
2989
2990				t.container
2991					.width(t.width)
2992					.height(t.height);
2993
2994				t.layers.children('.mejs-layer')
2995					.width(t.width)
2996					.height(t.height);
2997
2998			}
2999
3000		},
3001
3002		setControlsSize: function() {
3003			var t = this,
3004				usedWidth = 0,
3005				railWidth = 0,
3006				rail = t.controls.find('.mejs-time-rail'),
3007				total = t.controls.find('.mejs-time-total'),
3008				others = rail.siblings(),
3009				lastControl = others.last(),
3010				lastControlPosition = null;
3011
3012			// skip calculation if hidden
3013			if (!t.container.is(':visible') || !rail.length || !rail.is(':visible')) {
3014				return;
3015			}
3016
3017
3018			// allow the size to come from custom CSS
3019			if (t.options && !t.options.autosizeProgress) {
3020				// Also, frontends devs can be more flexible
3021				// due the opportunity of absolute positioning.
3022				railWidth = parseInt(rail.css('width'), 10);
3023			}
3024
3025			// attempt to autosize
3026			if (railWidth === 0 || !railWidth) {
3027
3028				// find the size of all the other controls besides the rail
3029				others.each(function() {
3030					var $this = $(this);
3031					if ($this.css('position') != 'absolute' && $this.is(':visible')) {
3032						usedWidth += $(this).outerWidth(true);
3033					}
3034				});
3035
3036				// fit the rail into the remaining space
3037				railWidth = t.controls.width() - usedWidth - (rail.outerWidth(true) - rail.width());
3038			}
3039
3040			// resize the rail,
3041			// but then check if the last control (say, the fullscreen button) got pushed down
3042			// this often happens when zoomed
3043			do {
3044				// outer area
3045				rail.width(railWidth);
3046				// dark space
3047				total.width(railWidth - (total.outerWidth(true) - total.width()));
3048
3049				if (lastControl.css('position') != 'absolute') {
3050					lastControlPosition = lastControl.length ? lastControl.position() : null;
3051					railWidth--;
3052				}
3053			} while (lastControlPosition !== null && lastControlPosition.top > 0 && railWidth > 0);
3054
3055			t.container.trigger('controlsresize');
3056		},
3057
3058
3059		buildposter: function(player, controls, layers, media) {
3060			var t = this,
3061				poster =
3062				$('<div class="mejs-poster mejs-layer">' +
3063				'</div>')
3064					.appendTo(layers),
3065				posterUrl = player.$media.attr('poster');
3066
3067			// prioriy goes to option (this is useful if you need to support iOS 3.x (iOS completely fails with poster)
3068			if (player.options.poster !== '') {
3069				posterUrl = player.options.poster;
3070			}
3071
3072			// second, try the real poster
3073			if ( posterUrl ) {
3074				t.setPoster(posterUrl);
3075			} else {
3076				poster.hide();
3077			}
3078
3079			media.addEventListener('play',function() {
3080				poster.hide();
3081			}, false);
3082
3083			if(player.options.showPosterWhenEnded && player.options.autoRewind){
3084				media.addEventListener('ended',function() {
3085					poster.show();
3086				}, false);
3087			}
3088		},
3089
3090		setPoster: function(url) {
3091			var t = this,
3092				posterDiv = t.container.find('.mejs-poster'),
3093				posterImg = posterDiv.find('img');
3094
3095			if (posterImg.length === 0) {
3096				posterImg = $('<img width="100%" height="100%" alt="" />').appendTo(posterDiv);
3097			}
3098
3099			posterImg.attr('src', url);
3100			posterDiv.css({'background-image' : 'url(' + url + ')'});
3101		},
3102
3103		buildoverlays: function(player, controls, layers, media) {
3104            var t = this;
3105			if (!player.isVideo)
3106				return;
3107
3108			var
3109			loading =
3110				$('<div class="mejs-overlay mejs-layer">'+
3111					'<div class="mejs-overlay-loading"><span></span></div>'+
3112				'</div>')
3113				.hide() // start out hidden
3114				.appendTo(layers),
3115			error =
3116				$('<div class="mejs-overlay mejs-layer">'+
3117					'<div class="mejs-overlay-error"></div>'+
3118				'</div>')
3119				.hide() // start out hidden
3120				.appendTo(layers),
3121			// this needs to come last so it's on top
3122			bigPlay =
3123				$('<div class="mejs-overlay mejs-layer mejs-overlay-play">'+
3124					'<div class="mejs-overlay-button"></div>'+
3125				'</div>')
3126				.appendTo(layers)
3127				.bind('click', function() {  // Removed 'touchstart' due issues on Samsung Android devices where a tap on bigPlay started and immediately stopped the video
3128					if (t.options.clickToPlayPause) {
3129						if (media.paused) {
3130							media.play();
3131						}
3132					}
3133				});
3134
3135			/*
3136			if (mejs.MediaFeatures.isiOS || mejs.MediaFeatures.isAndroid) {
3137				bigPlay.remove();
3138				loading.remove();
3139			}
3140			*/
3141
3142
3143			// show/hide big play button
3144			media.addEventListener('play',function() {
3145				bigPlay.hide();
3146				loading.hide();
3147				controls.find('.mejs-time-buffering').hide();
3148				error.hide();
3149			}, false);
3150
3151			media.addEventListener('playing', function() {
3152				bigPlay.hide();
3153				loading.hide();
3154				controls.find('.mejs-time-buffering').hide();
3155				error.hide();
3156			}, false);
3157
3158			media.addEventListener('seeking', function() {
3159				loading.show();
3160				controls.find('.mejs-time-buffering').show();
3161			}, false);
3162
3163			media.addEventListener('seeked', function() {
3164				loading.hide();
3165				controls.find('.mejs-time-buffering').hide();
3166			}, false);
3167
3168			media.addEventListener('pause',function() {
3169				if (!mejs.MediaFeatures.isiPhone) {
3170					bigPlay.show();
3171				}
3172			}, false);
3173
3174			media.addEventListener('waiting', function() {
3175				loading.show();
3176				controls.find('.mejs-time-buffering').show();
3177			}, false);
3178
3179
3180			// show/hide loading
3181			media.addEventListener('loadeddata',function() {
3182				// for some reason Chrome is firing this event
3183				//if (mejs.MediaFeatures.isChrome && media.getAttribute && media.getAttribute('preload') === 'none')
3184				//	return;
3185
3186				loading.show();
3187				controls.find('.mejs-time-buffering').show();
3188                // Firing the 'canplay' event after a timeout which isn't getting fired on some Android 4.1 devices (https://github.com/johndyer/mediaelement/issues/1305)
3189                if (mejs.MediaFeatures.isAndroid) {
3190                    media.canplayTimeout = window.setTimeout(
3191                        function() {
3192                            if (document.createEvent) {
3193                                var evt = document.createEvent('HTMLEvents');
3194                                evt.initEvent('canplay', true, true);
3195                                return media.dispatchEvent(evt);
3196                            }
3197                        }, 300
3198                    );
3199                }
3200			}, false);
3201			media.addEventListener('canplay',function() {
3202				loading.hide();
3203				controls.find('.mejs-time-buffering').hide();
3204                clearTimeout(media.canplayTimeout); // Clear timeout inside 'loadeddata' to prevent 'canplay' to fire twice
3205			}, false);
3206
3207			// error handling
3208			media.addEventListener('error',function(e) {
3209				t.handleError(e);
3210				loading.hide();
3211				bigPlay.hide();
3212				error.show();
3213				error.find('.mejs-overlay-error').html("Error loading this resource");
3214			}, false);
3215
3216			media.addEventListener('keydown', function(e) {
3217				t.onkeydown(player, media, e);
3218			}, false);
3219		},
3220
3221		buildkeyboard: function(player, controls, layers, media) {
3222
3223				var t = this;
3224
3225				t.container.keydown(function () {
3226					t.keyboardAction = true;
3227				});
3228
3229				// listen for key presses
3230				t.globalBind('keydown', function(event) {
3231					player.hasFocus = $(event.target).closest('.mejs-container').length !== 0;
3232					return t.onkeydown(player, media, event);
3233				});
3234
3235
3236				// check if someone clicked outside a player region, then kill its focus
3237				t.globalBind('click', function(event) {
3238					player.hasFocus = $(event.target).closest('.mejs-container').length !== 0;
3239				});
3240
3241		},
3242		onkeydown: function(player, media, e) {
3243			if (player.hasFocus && player.options.enableKeyboard) {
3244				// find a matching key
3245				for (var i = 0, il = player.options.keyActions.length; i < il; i++) {
3246					var keyAction = player.options.keyActions[i];
3247
3248					for (var j = 0, jl = keyAction.keys.length; j < jl; j++) {
3249						if (e.keyCode == keyAction.keys[j]) {
3250							if (typeof(e.preventDefault) == "function") e.preventDefault();
3251							keyAction.action(player, media, e.keyCode);
3252							return false;
3253						}
3254					}
3255				}
3256			}
3257
3258			return true;
3259		},
3260
3261		findTracks: function() {
3262			var t = this,
3263				tracktags = t.$media.find('track');
3264
3265			// store for use by plugins
3266			t.tracks = [];
3267			tracktags.each(function(index, track) {
3268
3269				track = $(track);
3270
3271				t.tracks.push({
3272					srclang: (track.attr('srclang')) ? track.attr('srclang').toLowerCase() : '',
3273					src: track.attr('src'),
3274					kind: track.attr('kind'),
3275					label: track.attr('label') || '',
3276					entries: [],
3277					isLoaded: false
3278				});
3279			});
3280		},
3281		changeSkin: function(className) {
3282			this.container[0].className = 'mejs-container ' + className;
3283			this.setPlayerSize(this.width, this.height);
3284			this.setControlsSize();
3285		},
3286		play: function() {
3287			this.load();
3288			this.media.play();
3289		},
3290		pause: function() {
3291			try {
3292				this.media.pause();
3293			} catch (e) {}
3294		},
3295		load: function() {
3296			if (!this.isLoaded) {
3297				this.media.load();
3298			}
3299
3300			this.isLoaded = true;
3301		},
3302		setMuted: function(muted) {
3303			this.media.setMuted(muted);
3304		},
3305		setCurrentTime: function(time) {
3306			this.media.setCurrentTime(time);
3307		},
3308		getCurrentTime: function() {
3309			return this.media.currentTime;
3310		},
3311		setVolume: function(volume) {
3312			this.media.setVolume(volume);
3313		},
3314		getVolume: function() {
3315			return this.media.volume;
3316		},
3317		setSrc: function(src) {
3318			this.media.setSrc(src);
3319		},
3320		remove: function() {
3321			var t = this, featureIndex, feature;
3322
3323			t.container.prev('.mejs-offscreen').remove();
3324
3325			// invoke features cleanup
3326			for (featureIndex in t.options.features) {
3327				feature = t.options.features[featureIndex];
3328				if (t['clean' + feature]) {
3329					try {
3330						t['clean' + feature](t);
3331					} catch (e) {
3332						// TODO: report control error
3333						//throw e;
3334						//
3335						//
3336					}
3337				}
3338			}
3339
3340			// grab video and put it back in place
3341			if (!t.isDynamic) {
3342				t.$media.prop('controls', true);
3343				// detach events from the video
3344				// TODO: detach event listeners better than this;
3345				//       also detach ONLY the events attached by this plugin!
3346				t.$node.clone().insertBefore(t.container).show();
3347				t.$node.remove();
3348			} else {
3349				t.$node.insertBefore(t.container);
3350			}
3351
3352			if (t.media.pluginType !== 'native') {
3353				t.media.remove();
3354			}
3355
3356			// Remove the player from the mejs.players object so that pauseOtherPlayers doesn't blow up when trying to pause a non existance flash api.
3357			delete mejs.players[t.id];
3358
3359			if (typeof t.container == 'object') {
3360				t.container.remove();
3361			}
3362			t.globalUnbind();
3363			delete t.node.player;
3364		},
3365		rebuildtracks: function(){
3366			var t = this;
3367			t.findTracks();
3368			t.buildtracks(t, t.controls, t.layers, t.media);
3369		},
3370		resetSize: function(){
3371			var t = this;
3372			// webkit has trouble doing this without a delay
3373			setTimeout(function () {
3374				//
3375				t.setPlayerSize(t.width, t.height);
3376				t.setControlsSize();
3377			}, 50);
3378		}
3379	};
3380
3381	(function(){
3382		var rwindow = /^((after|before)print|(before)?unload|hashchange|message|o(ff|n)line|page(hide|show)|popstate|resize|storage)\b/;
3383
3384		function splitEvents(events, id) {
3385			// add player ID as an event namespace so it's easier to unbind them all later
3386			var ret = {d: [], w: []};
3387			$.each((events || '').split(' '), function(k, v){
3388				var eventname = v + '.' + id;
3389				if (eventname.indexOf('.') === 0) {
3390					ret.d.push(eventname);
3391					ret.w.push(eventname);
3392				}
3393				else {
3394					ret[rwindow.test(v) ? 'w' : 'd'].push(eventname);
3395				}
3396			});
3397			ret.d = ret.d.join(' ');
3398			ret.w = ret.w.join(' ');
3399			return ret;
3400		}
3401
3402		mejs.MediaElementPlayer.prototype.globalBind = function(events, data, callback) {
3403    	var t = this;
3404      var doc = t.node ? t.node.ownerDocument : document;
3405
3406			events = splitEvents(events, t.id);
3407			if (events.d) $(doc).bind(events.d, data, callback);
3408			if (events.w) $(window).bind(events.w, data, callback);
3409		};
3410
3411		mejs.MediaElementPlayer.prototype.globalUnbind = function(events, callback) {
3412			var t = this;
3413      var doc = t.node ? t.node.ownerDocument : document;
3414
3415			events = splitEvents(events, t.id);
3416			if (events.d) $(doc).unbind(events.d, callback);
3417			if (events.w) $(window).unbind(events.w, callback);
3418		};
3419	})();
3420
3421	// turn into jQuery plugin
3422	if (typeof $ != 'undefined') {
3423		$.fn.mediaelementplayer = function (options) {
3424			if (options === false) {
3425				this.each(function () {
3426					var player = $(this).data('mediaelementplayer');
3427					if (player) {
3428						player.remove();
3429					}
3430					$(this).removeData('mediaelementplayer');
3431				});
3432			}
3433			else {
3434				this.each(function () {
3435					$(this).data('mediaelementplayer', new mejs.MediaElementPlayer(this, options));
3436				});
3437			}
3438			return this;
3439		};
3440
3441
3442		$(document).ready(function() {
3443			// auto enable using JSON attribute
3444			$('.mejs-player').mediaelementplayer();
3445		});
3446	}
3447
3448	// push out to window
3449	window.MediaElementPlayer = mejs.MediaElementPlayer;
3450
3451})(mejs.$);
3452
3453(function($) {
3454
3455	$.extend(mejs.MepDefaults, {
3456		playText: mejs.i18n.t('Play'),
3457		pauseText: mejs.i18n.t('Pause')
3458	});
3459
3460	// PLAY/pause BUTTON
3461	$.extend(MediaElementPlayer.prototype, {
3462		buildplaypause: function(player, controls, layers, media) {
3463			var 
3464				t = this,
3465				op = t.options,
3466				play = 
3467				$('<div class="mejs-button mejs-playpause-button mejs-play" >' +
3468					'<button type="button" aria-controls="' + t.id + '" title="' + op.playText + '" aria-label="' + op.playText + '"></button>' +
3469				'</div>')
3470				.appendTo(controls)
3471				.click(function(e) {
3472					e.preventDefault();
3473				
3474					if (media.paused) {
3475						media.play();
3476					} else {
3477						media.pause();
3478					}
3479					
3480					return false;
3481				}),
3482				play_btn = play.find('button');
3483
3484
3485			function togglePlayPause(which) {
3486				if ('play' === which) {
3487					play.removeClass('mejs-play').addClass('mejs-pause');
3488					play_btn.attr({
3489						'title': op.pauseText,
3490						'aria-label': op.pauseText
3491					});
3492				} else {
3493					play.removeClass('mejs-pause').addClass('mejs-play');
3494					play_btn.attr({
3495						'title': op.playText,
3496						'aria-label': op.playText
3497					});
3498				}
3499			};
3500			togglePlayPause('pse');
3501
3502
3503			media.addEventListener('play',function() {
3504				togglePlayPause('play');
3505			}, false);
3506			media.addEventListener('playing',function() {
3507				togglePlayPause('play');
3508			}, false);
3509
3510
3511			media.addEventListener('pause',function() {
3512				togglePlayPause('pse');
3513			}, false);
3514			media.addEventListener('paused',function() {
3515				togglePlayPause('pse');
3516			}, false);
3517		}
3518	});
3519	
3520})(mejs.$);
3521
3522(function($) {
3523
3524	$.extend(mejs.MepDefaults, {
3525		stopText: 'Stop'
3526	});
3527
3528	// STOP BUTTON
3529	$.extend(MediaElementPlayer.prototype, {
3530		buildstop: function(player, controls, layers, media) {
3531			var t = this;
3532
3533			$('<div class="mejs-button mejs-stop-button mejs-stop">' +
3534					'<button type="button" aria-controls="' + t.id + '" title="' + t.options.stopText + '" aria-label="' + t.options.stopText + '"></button>' +
3535				'</div>')
3536				.appendTo(controls)
3537				.click(function() {
3538					if (!media.paused) {
3539						media.pause();
3540					}
3541					if (media.currentTime > 0) {
3542						media.setCurrentTime(0);
3543                        media.pause();
3544						controls.find('.mejs-time-current').width('0px');
3545						controls.find('.mejs-time-handle').css('left', '0px');
3546						controls.find('.mejs-time-float-current').html( mejs.Utility.secondsToTimeCode(0, player.options));
3547						controls.find('.mejs-currenttime').html( mejs.Utility.secondsToTimeCode(0, player.options));
3548						layers.find('.mejs-poster').show();
3549					}
3550				});
3551		}
3552	});
3553	
3554})(mejs.$);
3555
3556(function($) {
3557
3558	$.extend(mejs.MepDefaults, {
3559		progessHelpText: mejs.i18n.t(
3560		'Use Left/Right Arrow keys to advance one second, Up/Down arrows to advance ten seconds.')
3561	});
3562
3563	// progress/loaded bar
3564	$.extend(MediaElementPlayer.prototype, {
3565		buildprogress: function(player, controls, layers, media) {
3566
3567			$('<div class="mejs-time-rail">' +
3568				'<span  class="mejs-time-total mejs-time-slider">' +
3569				//'<span class="mejs-offscreen">' + this.options.progessHelpText + '</span>' +
3570					'<span class="mejs-time-buffering"></span>' +
3571					'<span class="mejs-time-loaded"></span>' +
3572					'<span class="mejs-time-current"></span>' +
3573					'<span class="mejs-time-handle"></span>' +
3574					'<span class="mejs-time-float">' +
3575						'<span class="mejs-time-float-current">00:00</span>' +
3576						'<span class="mejs-time-float-corner"></span>' +
3577					'</span>' +
3578				'</span>' +
3579			'</div>')
3580				.appendTo(controls);
3581			controls.find('.mejs-time-buffering').hide();
3582
3583			var 
3584				t = this,
3585				total = controls.find('.mejs-time-total'),
3586				loaded  = controls.find('.mejs-time-loaded'),
3587				current  = controls.find('.mejs-time-current'),
3588				handle  = controls.find('.mejs-time-handle'),
3589				timefloat  = controls.find('.mejs-time-float'),
3590				timefloatcurrent  = controls.find('.mejs-time-float-current'),
3591                slider = controls.find('.mejs-time-slider'),
3592				handleMouseMove = function (e) {
3593					
3594                    var offset = total.offset(),
3595						width = total.width(),
3596						percentage = 0,
3597						newTime = 0,
3598						pos = 0,
3599                        x;
3600                    
3601                    // mouse or touch position relative to the object
3602					if (e.originalEvent && e.originalEvent.changedTouches) {
3603						x = e.originalEvent.changedTouches[0].pageX;
3604					} else if (e.changedTouches) { // for Zepto
3605						x = e.changedTouches[0].pageX;
3606					} else {
3607						x = e.pageX;
3608					}
3609
3610					if (media.duration) {
3611						if (x < offset.left) {
3612							x = offset.left;
3613						} else if (x > width + offset.left) {
3614							x = width + offset.left;
3615						}
3616						
3617						pos = x - offset.left;
3618						percentage = (pos / width);
3619						newTime = (percentage <= 0.02) ? 0 : percentage * media.duration;
3620
3621						// seek to where the mouse is
3622						if (mouseIsDown && newTime !== media.currentTime) {
3623							media.setCurrentTime(newTime);
3624						}
3625
3626						// position floating time box
3627						if (!mejs.MediaFeatures.hasTouch) {
3628								timefloat.css('left', pos);
3629								timefloatcurrent.html( mejs.Utility.secondsToTimeCode(newTime, player.options) );
3630								timefloat.show();
3631						}
3632					}
3633				},
3634				mouseIsDown = false,
3635				mouseIsOver = false,
3636				lastKeyPressTime = 0,
3637				startedPaused = false,
3638				autoRewindInitial = player.options.autoRewind;
3639            // Accessibility for slider
3640            var updateSlider = function (e) {
3641
3642				var seconds = media.currentTime,
3643					timeSliderText = mejs.i18n.t('Time Slider'),
3644					time = mejs.Utility.secondsToTimeCode(seconds, player.options),
3645					duration = media.duration;
3646
3647				slider.attr({
3648					'aria-label': timeSliderText,
3649					'aria-valuemin': 0,
3650					'aria-valuemax': duration,
3651					'aria-valuenow': seconds,
3652					'aria-valuetext': time,
3653					'role': 'slider',
3654					'tabindex': 0
3655				});
3656
3657			};
3658            
3659            var restartPlayer = function () {
3660				var now = new Date();
3661				if (now - lastKeyPressTime >= 1000) {
3662					media.play();
3663				}
3664			};
3665
3666			slider.bind('focus', function (e) {
3667				player.options.autoRewind = false;
3668			});
3669
3670			slider.bind('blur', function (e) {
3671				player.options.autoRewind = autoRewindInitial;
3672			});
3673
3674			slider.bind('keydown', function (e) {
3675
3676				if ((new Date() - lastKeyPressTime) >= 1000) {
3677					startedPaused = media.paused;
3678				}
3679
3680				var keyCode = e.keyCode,
3681					duration = media.duration,
3682					seekTime = media.currentTime;
3683
3684				switch (keyCode) {
3685				case 37: // left
3686					seekTime -= 1;
3687					break;
3688				case 39: // Right
3689					seekTime += 1;
3690					break;
3691				case 38: // Up
3692					seekTime += Math.floor(duration * 0.1);
3693					break;
3694				case 40: // Down
3695					seekTime -= Math.floor(duration * 0.1);
3696					break;
3697				case 36: // Home
3698					seekTime = 0;
3699					break;
3700				case 35: // end
3701					seekTime = duration;
3702					break;
3703				case 10: // enter
3704					media.paused ? media.play() : media.pause();
3705					return;
3706				case 13: // space
3707					media.paused ? media.play() : media.pause();
3708					return;
3709				default:
3710					return;
3711				}
3712
3713				seekTime = seekTime < 0 ? 0 : (seekTime >= duration ? duration : Math.floor(seekTime));
3714				lastKeyPressTime = new Date();
3715				if (!startedPaused) {
3716					media.pause();
3717				}
3718
3719				if (seekTime < media.duration && !startedPaused) {
3720					setTimeout(restartPlayer, 1100);
3721				}
3722
3723				media.setCurrentTime(seekTime);
3724
3725				e.preventDefault();
3726				e.stopPropagation();
3727				return false;
3728			});
3729
3730
3731			// handle clicks
3732			//controls.find('.mejs-time-rail').delegate('span', 'click', handleMouseMove);
3733			total
3734				.bind('mousedown touchstart', function (e) {
3735					// only handle left clicks or touch
3736					if (e.which === 1 || e.which === 0) {
3737						mouseIsDown = true;
3738						handleMouseMove(e);
3739						t.globalBind('mousemove.dur touchmove.dur', function(e) {
3740							handleMouseMove(e);
3741						});
3742						t.globalBind('mouseup.dur touchend.dur', function (e) {
3743							mouseIsDown = false;
3744							timefloat.hide();
3745							t.globalUnbind('.dur');
3746						});
3747					}
3748				})
3749				.bind('mouseenter', function(e) {
3750					mouseIsOver = true;
3751					t.globalBind('mousemove.dur', function(e) {
3752						handleMouseMove(e);
3753					});
3754					if (!mejs.MediaFeatures.hasTouch) {
3755						timefloat.show();
3756					}
3757				})
3758				.bind('mouseleave',function(e) {
3759					mouseIsOver = false;
3760					if (!mouseIsDown) {
3761						t.globalUnbind('.dur');
3762						timefloat.hide();
3763					}
3764				});
3765
3766			// loading
3767			media.addEventListener('progress', function (e) {
3768				player.setProgressRail(e);
3769				player.setCurrentRail(e);
3770			}, false);
3771
3772			// current time
3773			media.addEventListener('timeupdate', function(e) {
3774				player.setProgressRail(e);
3775				player.setCurrentRail(e);
3776				updateSlider(e);
3777			}, false);
3778			
3779			t.container.on('controlsresize', function() {
3780				player.setProgressRail();
3781				player.setCurrentRail();
3782			});
3783			
3784			// store for later use
3785			t.loaded = loaded;
3786			t.total = total;
3787			t.current = current;
3788			t.handle = handle;
3789		},
3790		setProgressRail: function(e) {
3791
3792			var
3793				t = this,
3794				target = (e !== undefined) ? e.target : t.media,
3795				percent = null;
3796
3797			// newest HTML5 spec has buffered array (FF4, Webkit)
3798			if (target && target.buffered && target.buffered.length > 0 && target.buffered.end && target.duration) {
3799				// account for a real array with multiple values - always read the end of the last buffer
3800				percent = target.buffered.end(target.buffered.length - 1) / target.duration;
3801			} 
3802			// Some browsers (e.g., FF3.6 and Safari 5) cannot calculate target.bufferered.end()
3803			// to be anything other than 0. If the byte count is available we use this instead.
3804			// Browsers that support the else if do not seem to have the bufferedBytes value and
3805			// should skip to there. Tested in Safari 5, Webkit head, FF3.6, Chrome 6, IE 7/8.
3806			else if (target && target.bytesTotal !== undefined && target.bytesTotal > 0 && target.bufferedBytes !== undefined) {
3807				percent = target.bufferedBytes / target.bytesTotal;
3808			}
3809			// Firefox 3 with an Ogg file seems to go this way
3810			else if (e && e.lengthComputable && e.total !== 0) {
3811				percent = e.loaded / e.total;
3812			}
3813
3814			// finally update the progress bar
3815			if (percent !== null) {
3816				percent = Math.min(1, Math.max(0, percent));
3817				// update loaded bar
3818				if (t.loaded && t.total) {
3819					t.loaded.width(t.total.width() * percent);
3820				}
3821			}
3822		},
3823		setCurrentRail: function() {
3824
3825			var t = this;
3826		
3827			if (t.media.currentTime !== undefined && t.media.duration) {
3828
3829				// update bar and handle
3830				if (t.total && t.handle) {
3831					var 
3832						newWidth = Math.round(t.total.width() * t.media.currentTime / t.media.duration),
3833						handlePos = newWidth - Math.round(t.handle.outerWidth(true) / 2);
3834
3835					t.current.width(newWidth);
3836					t.handle.css('left', handlePos);
3837				}
3838			}
3839
3840		}
3841	});
3842})(mejs.$);
3843
3844(function($) {
3845	
3846	// options
3847	$.extend(mejs.MepDefaults, {
3848		duration: -1,
3849		timeAndDurationSeparator: '<span> | </span>'
3850	});
3851
3852
3853	// current and duration 00:00 / 00:00
3854	$.extend(MediaElementPlayer.prototype, {
3855		buildcurrent: function(player, controls, layers, media) {
3856			var t = this;
3857			
3858			$('<div class="mejs-time" role="timer" aria-live="off">' +
3859					'<span class="mejs-currenttime">' + 
3860						mejs.Utility.secondsToTimeCode(0, player.options) +
3861                    '</span>'+
3862				'</div>')
3863			.appendTo(controls);
3864			
3865			t.currenttime = t.controls.find('.mejs-currenttime');
3866
3867			media.addEventListener('timeupdate',function() {
3868				player.updateCurrent();
3869			}, false);
3870		},
3871
3872
3873		buildduration: function(player, controls, layers, media) {
3874			var t = this;
3875			
3876			if (controls.children().last().find('.mejs-currenttime').length > 0) {
3877				$(t.options.timeAndDurationSeparator +
3878					'<span class="mejs-duration">' + 
3879						mejs.Utility.secondsToTimeCode(t.options.duration, t.options) +
3880					'</span>')
3881					.appendTo(controls.find('.mejs-time'));
3882			} else {
3883
3884				// add class to current time
3885				controls.find('.mejs-currenttime').parent().addClass('mejs-currenttime-container');
3886				
3887				$('<div class="mejs-time mejs-duration-container">'+
3888					'<span class="mejs-duration">' + 
3889						mejs.Utility.secondsToTimeCode(t.options.duration, t.options) +
3890					'</span>' +
3891				'</div>')
3892				.appendTo(controls);
3893			}
3894			
3895			t.durationD = t.controls.find('.mejs-duration');
3896
3897			media.addEventListener('timeupdate',function() {
3898				player.updateDuration();
3899			}, false);
3900		},
3901		
3902		updateCurrent:  function() {
3903			var t = this;
3904
3905			if (t.currenttime) {
3906				t.currenttime.html(mejs.Utility.secondsToTimeCode(t.media.currentTime, t.options));
3907			}
3908		},
3909		
3910		updateDuration: function() {
3911			var t = this;
3912
3913			//Toggle the long video class if the video is longer than an hour.
3914			t.container.toggleClass("mejs-long-video", t.media.duration > 3600);
3915			
3916			if (t.durationD && (t.options.duration > 0 || t.media.duration)) {
3917				t.durationD.html(mejs.Utility.secondsToTimeCode(t.options.duration > 0 ? t.options.duration : t.media.duration, t.options));
3918			}		
3919		}
3920	});
3921
3922})(mejs.$);
3923
3924(function($) {
3925
3926	$.extend(mejs.MepDefaults, {
3927		muteText: mejs.i18n.t('Mute Toggle'),
3928        allyVolumeControlText: mejs.i18n.t('Use Up/Down Arrow keys to increase or decrease volume.'),
3929		hideVolumeOnTouchDevices: true,
3930		
3931		audioVolume: 'horizontal',
3932		videoVolume: 'vertical'
3933	});
3934
3935	$.extend(MediaElementPlayer.prototype, {
3936		buildvolume: function(player, controls, layers, media) {
3937				
3938			// Android and iOS don't support volume controls
3939			if ((mejs.MediaFeatures.isAndroid || mejs.MediaFeatures.isiOS) && this.options.hideVolumeOnTouchDevices)
3940				return;
3941			
3942			var t = this,
3943				mode = (t.isVideo) ? t.options.videoVolume : t.options.audioVolume,
3944				mute = (mode == 'horizontal') ?
3945				
3946				// horizontal version
3947				$('<div class="mejs-button mejs-volume-button mejs-mute">' +
3948					'<button type="button" aria-controls="' + t.id + 
3949						'" title="' + t.options.muteText + 
3950						'" aria-label="' + t.options.muteText +
3951					'"></button>'+
3952				'</div>' +
3953                  '<a href="javascript:void(0);" class="mejs-horizontal-volume-slider">' + // outer background
3954					'<span class="mejs-offscreen">' + t.options.allyVolumeControlText + '</span>' +
3955					'<div class="mejs-horizontal-volume-total"></div>'+ // line background
3956					'<div class="mejs-horizontal-volume-current"></div>'+ // current volume
3957					'<div class="mejs-horizontal-volume-handle"></div>'+ // handle
3958				'</a>'
3959				)
3960					.appendTo(controls) :
3961				
3962				// vertical version
3963				$('<div class="mejs-button mejs-volume-button mejs-mute">'+
3964					'<button type="button" aria-controls="' + t.id + 
3965						'" title="' + t.options.muteText + 
3966						'" aria-label="' + t.options.muteText + 
3967					'"></button>'+
3968					'<a href="javascript:void(0);" class="mejs-volume-slider">'+ // outer background
3969						'<span class="mejs-offscreen">' + t.options.allyVolumeControlText + '</span>' +                  
3970						'<div class="mejs-volume-total"></div>'+ // line background
3971						'<div class="mejs-volume-current"></div>'+ // current volume
3972						'<div class="mejs-volume-handle"></div>'+ // handle
3973					'</a>'+
3974				'</div>')
3975					.appendTo(controls),
3976			volumeSlider = t.container.find('.mejs-volume-slider, .mejs-horizontal-volume-slider'),
3977			volumeTotal = t.container.find('.mejs-volume-total, .mejs-horizontal-volume-total'),
3978			volumeCurrent = t.container.find('.mejs-volume-current, .mejs-horizontal-volume-current'),
3979			volumeHandle = t.container.find('.mejs-volume-handle, .mejs-horizontal-volume-handle'),
3980
3981			positionVolumeHandle = function(volume, secondTry) {
3982
3983				if (!volumeSlider.is(':visible') && typeof secondTry == 'undefined') {
3984					volumeSlider.show();
3985					positionVolumeHandle(volume, true);
3986					volumeSlider.hide();
3987					return;
3988				}
3989
3990				// correct to 0-1
3991				volume = Math.max(0,volume);
3992				volume = Math.min(volume,1);
3993
3994				// ajust mute button style
3995				if (volume === 0) {
3996					mute.removeClass('mejs-mute').addClass('mejs-unmute');
3997					mute.children('button').attr('title', mejs.i18n.t('Unmute')).attr('aria-label', mejs.i18n.t('Unmute'));
3998				} else {
3999					mute.removeClass('mejs-unmute').addClass('mejs-mute');
4000					mute.children('button').attr('title', mejs.i18n.t('Mute')).attr('aria-label', mejs.i18n.t('Mute'));
4001				}
4002
4003                // top/left of full size volume slider background
4004                var totalPosition = volumeTotal.position();
4005				// position slider 
4006				if (mode == 'vertical') {
4007					var
4008                    // height of the full size volume slider background
4009						totalHeight = volumeTotal.height(),
4010
4011                        // the new top position based on the current volume
4012						// 70% volume on 100px height == top:30px
4013						newTop = totalHeight - (totalHeight * volume);
4014	
4015					// handle
4016					volumeHandle.css('top', Math.round(totalPosition.top + newTop - (volumeHandle.height() / 2)));
4017	
4018					// show the current visibility
4019					volumeCurrent.height(totalHeight - newTop );
4020					volumeCurrent.css('top', totalPosition.top + newTop);
4021				} else {
4022                    var
4023						// height of the full size volume slider background
4024						totalWidth = volumeTotal.width(),
4025						
4026						// the new left position based on the current volume
4027						newLeft = totalWidth * volume;
4028	
4029					// handle
4030					volumeHandle.css('left', Math.round(totalPosition.left + newLeft - (volumeHandle.width() / 2)));
4031	
4032					// rezize the current part of the volume bar
4033					volumeCurrent.width( Math.round(newLeft) );
4034				}
4035			},
4036			handleVolumeMove = function(e) {
4037				
4038				var volume = null,
4039					totalOffset = volumeTotal.offset();
4040				
4041				// calculate the new volume based on the moust position
4042				if (mode === 'vertical') {
4043				
4044					var
4045						railHeight = volumeTotal.height(),
4046						newY = e.pageY - totalOffset.top;
4047						
4048					volume = (railHeight - newY) / railHeight;
4049						
4050					// the controls just hide themselves (usually when mouse moves too far up)
4051					if (totalOffset.top === 0 || totalOffset.left === 0) {
4052						return;
4053                    }
4054					
4055				} else {
4056					var
4057						railWidth = volumeTotal.width(),
4058						newX = e.pageX - totalOffset.left;
4059						
4060					volume = newX / railWidth;
4061				}
4062				
4063				// ensure the volume isn't outside 0-1
4064				volume = Math.max(0,volume);
4065				volume = Math.min(volume,1);
4066				
4067				// position the slider and handle
4068				positionVolumeHandle(volume);
4069				
4070				// set the media object (this will trigger the volumechanged event)
4071				if (volume === 0) {
4072					media.setMuted(true);
4073				} else {
4074					media.setMuted(false);
4075				}
4076				media.setVolume(volume);
4077			},
4078			mouseIsDown = false,
4079			mouseIsOver = false;
4080
4081			// SLIDER
4082			
4083			mute
4084				.hover(function() {
4085					volumeSlider.show();
4086					mouseIsOver = true;
4087				}, function() {
4088					mouseIsOver = false;
4089						
4090					if (!mouseIsDown && mode == 'vertical')	{
4091						volumeSlider.hide();
4092					}
4093				});
4094            
4095            var updateVolumeSlider = function (e) {
4096
4097                var volume = Math.floor(media.volume*100);
4098
4099				volumeSlider.attr({
4100					'aria-label': mejs.i18n.t('volumeSlider'),
4101					'aria-valuemin': 0,
4102					'aria-valuemax': 100,
4103					'aria-valuenow': volume,
4104					'aria-valuetext': volume+'%',
4105					'role': 'slider',
4106					'tabindex': 0
4107				});
4108
4109			};
4110			
4111			volumeSlider
4112				.bind('mouseover', function() {
4113					mouseIsOver = true;	
4114				})
4115				.bind('mousedown', function (e) {
4116					handleVolumeMove(e);
4117					t.globalBind('mousemove.vol', function(e) {
4118						handleVolumeMove(e);
4119					});
4120					t.globalBind('mouseup.vol', function () {
4121						mouseIsDown = false;
4122						t.globalUnbind('.vol');
4123
4124						if (!mouseIsOver && mode == 'vertical') {
4125							volumeSlider.hide();
4126						}
4127					});
4128					mouseIsDown = true;
4129						
4130					return false;
4131				})
4132				.bind('keydown', function (e) {
4133					var keyCode = e.keyCode;
4134					var volume = media.volume;
4135					switch (keyCode) {
4136                        case 38: // Up
4137                            volume += 0.1;
4138                            break;
4139                        case 40: // Down
4140                            volume = volume - 0.1;
4141                            break;
4142                        default:
4143                            return true;
4144                    }
4145
4146					mouseIsDown = false;
4147					positionVolumeHandle(volume);
4148					media.setVolume(volume);
4149					return false;
4150				});
4151
4152			// MUTE button
4153			mute.find('button').click(function() {
4154				media.setMuted( !media.muted );
4155			});
4156            
4157            //Keyboard input
4158            mute.find('button').bind('focus', function () {
4159				volumeSlider.show();
4160			});
4161
4162			// listen for volume change events from other sources
4163			media.addEventListener('volumechange', function(e) {
4164				if (!mouseIsDown) {
4165					if (media.muted) {
4166						positionVolumeHandle(0);
4167						mute.removeClass('mejs-mute').addClass('mejs-unmute');
4168					} else {
4169						positionVolumeHandle(media.volume);
4170						mute.removeClass('mejs-unmute').addClass('mejs-mute');
4171					}
4172				}
4173				updateVolumeSlider(e);
4174			}, false);
4175			
4176			// mutes the media and sets the volume icon muted if the initial volume is set to 0
4177			if (player.options.startVolume === 0) {
4178				media.setMuted(true);
4179			}
4180			
4181			// shim gets the startvolume as a parameter, but we have to set it on the native <video> and <audio> elements
4182			if (media.pluginType === 'native') {
4183				media.setVolume(player.options.startVolume);
4184			}
4185			
4186			t.container.on('controlsresize', function() {
4187				positionVolumeHandle(media.volume);
4188			});
4189		}
4190	});
4191	
4192})(mejs.$);
4193
4194(function($) {
4195
4196	$.extend(mejs.MepDefaults, {
4197		usePluginFullScreen: true,
4198		newWindowCallback: function() { return '';},
4199		fullscreenText: mejs.i18n.t('Fullscreen')
4200	});
4201
4202	$.extend(MediaElementPlayer.prototype, {
4203
4204		isFullScreen: false,
4205
4206		isNativeFullScreen: false,
4207
4208		isInIframe: false,
4209
4210		buildfullscreen: function(player, controls, layers, media) {
4211
4212			if (!player.isVideo)
4213				return;
4214
4215			player.isInIframe = (window.location != window.parent.location);
4216
4217			// native events
4218			if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
4219
4220				// chrome doesn't alays fire this in an iframe
4221				var func = function(e) {
4222					if (player.isFullScreen) {
4223						if (mejs.MediaFeatures.isFullScreen()) {
4224							player.isNativeFullScreen = true;
4225							// reset the controls once we are fully in full screen
4226							player.setControlsSize();
4227						} else {
4228							player.isNativeFullScreen = false;
4229							// when a user presses ESC
4230							// make sure to put the player back into place
4231							player.exitFullScreen();
4232						}
4233					}
4234				};
4235
4236				player.globalBind(mejs.MediaFeatures.fullScreenEventName, func);
4237			}
4238
4239			var t = this,
4240				fullscreenBtn =
4241					$('<div class="mejs-button mejs-fullscreen-button">' +
4242						'<button type="button" aria-controls="' + t.id + '" title="' + t.options.fullscreenText + '" aria-label="' + t.options.fullscreenText + '"></button>' +
4243					'</div>')
4244					.appendTo(controls);
4245
4246				if (t.media.pluginType === 'native' || (!t.options.usePluginFullScreen && !mejs.MediaFeatures.isFirefox)) {
4247
4248					fullscreenBtn.click(function() {
4249						var isFullScreen = (mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || player.isFullScreen;
4250
4251						if (isFullScreen) {
4252							player.exitFullScreen();
4253						} else {
4254							player.enterFullScreen();
4255						}
4256					});
4257
4258				} else {
4259
4260					var hideTimeout = null,
4261						supportsPointerEvents = (function() {
4262							// TAKEN FROM MODERNIZR
4263							var element = document.createElement('x'),
4264								documentElement = document.documentElement,
4265								getComputedStyle = window.getComputedStyle,
4266								supports;
4267							if(!('pointerEvents' in element.style)){
4268								return false;
4269							}
4270							element.style.pointerEvents = 'auto';
4271							element.style.pointerEvents = 'x';
4272							documentElement.appendChild(element);
4273							supports = getComputedStyle &&
4274								getComputedStyle(element, '').pointerEvents === 'auto';
4275							documentElement.removeChild(element);
4276							return !!supports;
4277						})();
4278
4279					//
4280
4281					if (supportsPointerEvents && !mejs.MediaFeatures.isOpera) { // opera doesn't allow this :(
4282
4283						// allows clicking through the fullscreen button and controls down directly to Flash
4284
4285						/*
4286						 When a user puts his mouse over the fullscreen button, the controls are disabled
4287						 So we put a div over the video and another one on iether side of the fullscreen button
4288						 that caputre mouse movement
4289						 and restore the controls once the mouse moves outside of the fullscreen button
4290						*/
4291
4292						var fullscreenIsDisabled = false,
4293							restoreControls = function() {
4294								if (fullscreenIsDisabled) {
4295									// hide the hovers
4296									for (var i in hoverDivs) {
4297										hoverDivs[i].hide();
4298									}
4299
4300									// restore the control bar
4301									fullscreenBtn.css('pointer-events', '');
4302									t.controls.css('pointer-events', '');
4303
4304									// prevent clicks from pausing video
4305									t.media.removeEventListener('click', t.clickToPlayPauseCallback);
4306
4307									// store for later
4308									fullscreenIsDisabled = false;
4309								}
4310							},
4311							hoverDivs = {},
4312							hoverDivNames = ['top', 'left', 'right', 'bottom'],
4313							i, len,
4314							positionHoverDivs = function() {
4315								var fullScreenBtnOffsetLeft = fullscreenBtn.offset().left - t.container.offset().left,
4316									fullScreenBtnOffsetTop = fullscreenBtn.offset().top - t.container.offset().top,
4317									fullScreenBtnWidth = fullscreenBtn.outerWidth(true),
4318									fullScreenBtnHeight = fullscreenBtn.outerHeight(true),
4319									containerWidth = t.container.width(),
4320									containerHeight = t.container.height();
4321
4322								for (i in hoverDivs) {
4323									hoverDivs[i].css({position: 'absolute', top: 0, left: 0}); //, backgroundColor: '#f00'});
4324								}
4325
4326								// over video, but not controls
4327								hoverDivs['top']
4328									.width( containerWidth )
4329									.height( fullScreenBtnOffsetTop );
4330
4331								// over controls, but not the fullscreen button
4332								hoverDivs['left']
4333									.width( fullScreenBtnOffsetLeft )
4334									.height( fullScreenBtnHeight )
4335									.css({top: fullScreenBtnOffsetTop});
4336
4337								// after the fullscreen button
4338								hoverDivs['right']
4339									.width( containerWidth - fullScreenBtnOffsetLeft - fullScreenBtnWidth )
4340									.height( fullScreenBtnHeight )
4341									.css({top: fullScreenBtnOffsetTop,
4342										 left: fullScreenBtnOffsetLeft + fullScreenBtnWidth});
4343
4344								// under the fullscreen button
4345								hoverDivs['bottom']
4346									.width( containerWidth )
4347									.height( containerHeight - fullScreenBtnHeight - fullScreenBtnOffsetTop )
4348									.css({top: fullScreenBtnOffsetTop + fullScreenBtnHeight});
4349							};
4350
4351						t.globalBind('resize', function() {
4352							positionHoverDivs();
4353						});
4354
4355						for (i = 0, len = hoverDivNames.length; i < len; i++) {
4356							hoverDivs[hoverDivNames[i]] = $('<div class="mejs-fullscreen-hover" />').appendTo(t.container).mouseover(restoreControls).hide();
4357						}
4358
4359						// on hover, kill the fullscreen button's HTML handling, allowing clicks down to Flash
4360						fullscreenBtn.on('mouseover',function() {
4361
4362							if (!t.isFullScreen) {
4363
4364								var buttonPos = fullscreenBtn.offset(),
4365									containerPos = player.container.offset();
4366
4367								// move the button in Flash into place
4368								media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, false);
4369
4370								// allows click through
4371								fullscreenBtn.css('pointer-events', 'none');
4372								t.controls.css('pointer-events', 'none');
4373
4374								// restore click-to-play
4375								t.media.addEventListener('click', t.clickToPlayPauseCallback);
4376
4377								// show the divs that will restore things
4378								for (i in hoverDivs) {
4379									hoverDivs[i].show();
4380								}
4381
4382								positionHoverDivs();
4383
4384								fullscreenIsDisabled = true;
4385							}
4386
4387						});
4388
4389						// restore controls anytime the user enters or leaves fullscreen
4390						media.addEventListener('fullscreenchange', function(e) {
4391							t.isFullScreen = !t.isFullScreen;
4392							// don't allow plugin click to pause video - messes with
4393							// plugin's controls
4394							if (t.isFullScreen) {
4395								t.media.removeEventListener('click', t.clickToPlayPauseCallback);
4396							} else {
4397								t.media.addEventListener('click', t.clickToPlayPauseCallback);
4398							}
4399							restoreControls();
4400						});
4401
4402
4403						// the mouseout event doesn't work on the fullscren button, because we already killed the pointer-events
4404						// so we use the document.mousemove event to restore controls when the mouse moves outside the fullscreen button
4405
4406						t.globalBind('mousemove', function(e) {
4407
4408							// if the mouse is anywhere but the fullsceen button, then restore it all
4409							if (fullscreenIsDisabled) {
4410
4411								var fullscreenBtnPos = fullscreenBtn.offset();
4412
4413
4414								if (e.pageY < fullscreenBtnPos.top || e.pageY > fullscreenBtnPos.top + fullscreenBtn.outerHeight(true) ||
4415									e.pageX < fullscreenBtnPos.left || e.pageX > fullscreenBtnPos.left + fullscreenBtn.outerWidth(true)
4416									) {
4417
4418									fullscreenBtn.css('pointer-events', '');
4419									t.controls.css('pointer-events', '');
4420
4421									fullscreenIsDisabled = false;
4422								}
4423							}
4424						});
4425
4426
4427
4428					} else {
4429
4430						// the hover state will show the fullscreen button in Flash to hover up and click
4431
4432						fullscreenBtn
4433							.on('mouseover', function() {
4434
4435								if (hideTimeout !== null) {
4436									clearTimeout(hideTimeout);
4437									delete hideTimeout;
4438								}
4439
4440								var buttonPos = fullscreenBtn.offset(),
4441									containerPos = player.container.offset();
4442
4443								media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, true);
4444
4445							})
4446							.on('mouseout', function() {
4447
4448								if (hideTimeout !== null) {
4449									clearTimeout(hideTimeout);
4450									delete hideTimeout;
4451								}
4452
4453								hideTimeout = setTimeout(function() {
4454									media.hideFullscreenButton();
4455								}, 1500);
4456
4457
4458							});
4459					}
4460				}
4461
4462			player.fullscreenBtn = fullscreenBtn;
4463
4464			t.globalBind('keydown',function (e) {
4465				if (((mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || t.isFullScreen) && e.keyCode == 27) {
4466					player.exitFullScreen();
4467				}
4468			});
4469			
4470			t.normalHeight = 0;
4471			t.normalWidth = 0;
4472
4473		},
4474
4475		cleanfullscreen: function(player) {
4476			player.exitFullScreen();
4477		},
4478
4479        containerSizeTimeout: null,
4480
4481		enterFullScreen: function() {
4482
4483			var t = this;
4484
4485			// firefox+flash can't adjust plugin sizes without resetting :(
4486			if (t.media.pluginType !== 'native' && (mejs.MediaFeatures.isFirefox || t.options.usePluginFullScreen)) {
4487				//t.media.setFullscreen(true);
4488				//player.isFullScreen = true;
4489				return;
4490			}
4491
4492			// set it to not show scroll bars so 100% will work
4493            $(document.documentElement).addClass('mejs-fullscreen');
4494
4495			// store sizing
4496			t.normalHeight = t.container.height();
4497			t.normalWidth = t.container.width();
4498
4499			// attempt to do true fullscreen (Safari 5.1 and Firefox Nightly only for now)
4500			if (t.media.pluginType === 'native') {
4501				if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
4502
4503					mejs.MediaFeatures.requestFullScreen(t.container[0]);
4504					//return;
4505
4506					if (t.isInIframe) {
4507						// sometimes exiting from fullscreen doesn't work
4508						// notably in Chrome <iframe>. Fixed in version 17
4509						setTimeout(function checkFullscreen() {
4510
4511							if (t.isNativeFullScreen) {
4512								var zoomMultiplier = window["devicePixelRatio"] || 1,
4513								// Use a percent error margin since devicePixelRatio is a float and not exact.
4514									percentErrorMargin = 0.002, // 0.2%
4515									windowWidth = zoomMultiplier * $(window).width(),
4516									screenWidth = screen.width,
4517									// ** 13twelve
4518									// Screen width is sort of useless: http://www.quirksmode.org/blog/archives/2013/11/screenwidth_is.html
4519									// My rMBP ignores devicePixelRatio when returning the values, so fullscreen would always fail the "suddenly not fullscreen" test
4520									// Theory: the gap between reported values should give us an indication of browser behavior with screen.width and devicePixelRatio
4521									zoomedWindowWidth = zoomMultiplier * windowWidth;
4522									
4523								if (Math.abs(screenWidth-windowWidth) > Math.abs(screenWidth-zoomedWindowWidth)) {
4524									// screen.width is likely true pixels, not CSS pixels, so we need to use the zoomed window width for comparison
4525									windowWidth = zoomedWindowWidth;
4526								}
4527								// ** / 13twelve
4528
4529								var absDiff = Math.abs(screenWidth - windowWidth),
4530									marginError = screenWidth * percentErrorMargin;
4531
4532								// check if the video is suddenly not really fullscreen
4533								if (absDiff > marginError) {
4534									// manually exit
4535									t.exitFullScreen();
4536								} else {
4537									// test again
4538									setTimeout(checkFullscreen, 500);
4539								}
4540							}
4541							
4542						}, 1000);
4543					}
4544
4545				} else if (mejs.MediaFeatures.hasSemiNativeFullScreen) {
4546					t.media.webkitEnterFullscreen();
4547					return;
4548				}
4549			}
4550
4551			// check for iframe launch
4552			if (t.isInIframe) {
4553				var url = t.options.newWindowCallback(this);
4554
4555
4556				if (url !== '') {
4557
4558					// launch immediately
4559					if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
4560						t.pause();
4561						window.open(url, t.id, 'top=0,left=0,width=' + screen.availWi
4561dth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
4562						return;
4563					} else {
4564						setTimeout(function() {
4565							if (!t.isNativeFullScreen) {
4566								t.pause();
4567								window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
4568							}
4569						}, 250);
4570					}
4571				}
4572
4573			}
4574
4575			// full window code
4576
4577
4578
4579			// make full size
4580			t.container
4581				.addClass('mejs-container-fullscreen')
4582				.width('100%')
4583				.height('100%');
4584				//.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});
4585
4586			// Only needed for safari 5.1 native full screen, can cause display issues elsewhere
4587			// Actually, it seems to be needed for IE8, too
4588			//if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
4589				t.containerSizeTimeout = setTimeout(function() {
4590					t.container.css({width: '100%', height: '100%'});
4591					t.setControlsSize();
4592				}, 500);
4593			//}
4594
4595			if (t.media.pluginType === 'native') {
4596				t.$media
4597					.width('100%')
4598					.height('100%');
4599			} else {
4600				t.container.find('.mejs-shim')
4601					.width('100%')
4602					.height('100%');
4603
4604				//if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
4605					t.media.setVideoSize($(window).width(),$(window).height());
4606				//}
4607			}
4608
4609			t.layers.children('div')
4610				.width('100%')
4611				.height('100%');
4612
4613			if (t.fullscreenBtn) {
4614				t.fullscreenBtn
4615					.removeClass('mejs-fullscreen')
4616					.addClass('mejs-unfullscreen');
4617			}
4618
4619			t.setControlsSize();
4620			t.isFullScreen = true;
4621
4622			t.container.find('.mejs-captions-text').css('font-size', screen.width / t.width * 1.00 * 100 + '%');
4623			t.container.find('.mejs-captions-position').css('bottom', '45px');
4624
4625			t.container.trigger('enteredfullscreen');
4626		},
4627
4628		exitFullScreen: function() {
4629
4630			var t = this;
4631
4632            // Prevent container from attempting to stretch a second time
4633            clearTimeout(t.containerSizeTimeout);
4634
4635			// firefox can't adjust plugins
4636			if (t.media.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) {
4637				t.media.setFullscreen(false);
4638				//player.isFullScreen = false;
4639				return;
4640			}
4641
4642			// come outo of native fullscreen
4643			if (mejs.MediaFeatures.hasTrueNativeFullScreen && (mejs.MediaFeatures.isFullScreen() || t.isFullScreen)) {
4644				mejs.MediaFeatures.cancelFullScreen();
4645			}
4646
4647			// restore scroll bars to document
4648            $(document.documentElement).removeClass('mejs-fullscreen');
4649
4650			t.container
4651				.removeClass('mejs-container-fullscreen')
4652				.width(t.normalWidth)
4653				.height(t.normalHeight);
4654
4655			if (t.media.pluginType === 'native') {
4656				t.$media
4657					.width(t.normalWidth)
4658					.height(t.normalHeight);
4659			} else {
4660				t.container.find('.mejs-shim')
4661					.width(t.normalWidth)
4662					.height(t.normalHeight);
4663
4664				t.media.setVideoSize(t.normalWidth, t.normalHeight);
4665			}
4666
4667			t.layers.children('div')
4668				.width(t.normalWidth)
4669				.height(t.normalHeight);
4670
4671			t.fullscreenBtn
4672				.removeClass('mejs-unfullscreen')
4673				.addClass('mejs-fullscreen');
4674
4675			t.setControlsSize();
4676			t.isFullScreen = false;
4677
4678			t.container.find('.mejs-captions-text').css('font-size','');
4679			t.container.find('.mejs-captions-position').css('bottom', '');
4680
4681			t.container.trigger('exitedfullscreen');
4682		}
4683	});
4684
4685})(mejs.$);
4686
4687(function($) {
4688
4689	// Speed
4690	$.extend(mejs.MepDefaults, {
4691
4692		// We also support to pass object like this:
4693		// [{name: 'Slow', value: '0.75'}, {name: 'Normal', value: '1.00'}, ...]
4694		speeds: ['2.00', '1.50', '1.25', '1.00', '0.75'],
4695
4696		defaultSpeed: '1.00',
4697		
4698		speedChar: 'x'
4699
4700	});
4701
4702	$.extend(MediaElementPlayer.prototype, {
4703
4704		buildspeed: function(player, controls, layers, media) {
4705			var t = this;
4706
4707			if (t.media.pluginType == 'native') {
4708				var 
4709					speedButton = null,
4710					speedSelector = null,
4711					playbackSpeed = null,
4712					inputId = null;
4713
4714				var speeds = [];
4715				var defaultInArray = false;
4716				for (var i=0, len=t.options.speeds.length; i < len; i++) {
4717					var s = t.options.speeds[i];
4718					if (typeof(s) === 'string'){
4719						speeds.push({
4720							name: s + t.options.speedChar,
4721							value: s
4722						});
4723						if(s === t.options.defaultSpeed) {
4724							defaultInArray = true;
4725						}
4726					}
4727					else {
4728						speeds.push(s);
4729						if(s.value === t.options.defaultSpeed) {
4730							defaultInArray = true;
4731						}
4732					}
4733				}
4734
4735				if (!defaultInArray) {
4736					speeds.push({
4737						name: t.options.defaultSpeed + t.options.speedChar,
4738						value: t.options.defaultSpeed
4739					});
4740				}
4741
4742				speeds.sort(function(a, b) {
4743					return parseFloat(b.value) - parseFloat(a.value);
4744				});
4745
4746				var getSpeedNameFromValue = function(value) {
4747					for(i=0,len=speeds.length; i <len; i++) {
4748						if (speeds[i].value === value) {
4749							return speeds[i].name;
4750						}
4751					}
4752				};
4753
4754				var html = '<div class="mejs-button mejs-speed-button">' +
4755							'<button type="button">' + getSpeedNameFromValue(t.options.defaultSpeed) + '</button>' +
4756							'<div class="mejs-speed-selector">' +
4757							'<ul>';
4758
4759				for (i = 0, il = speeds.length; i<il; i++) {
4760					inputId = t.id + '-speed-' + speeds[i].value;
4761					html += '<li>' + 
4762								'<input type="radio" name="speed" ' + 
4763											'value="' + speeds[i].value + '" ' +
4764											'id="' + inputId + '" ' +
4765											(speeds[i].value === t.options.defaultSpeed ? ' checked' : '') +
4766											' />' +
4767								'<label for="' + inputId + '" ' +
4768											(speeds[i].value === t.options.defaultSpeed ? ' class="mejs-speed-selected"' : '') +
4769											'>' + speeds[i].name + '</label>' +
4770							'</li>';
4771				}
4772				html += '</ul></div></div>';
4773
4774				speedButton = $(html).appendTo(controls);
4775				speedSelector = speedButton.find('.mejs-speed-selector');
4776
4777				playbackSpeed = t.options.defaultSpeed;
4778
4779				media.addEventListener('loadedmetadata', function(e) {
4780					if (playbackSpeed) {
4781						media.playbackRate = parseFloat(playbackSpeed);
4782					}
4783				}, true);
4784
4785				speedSelector
4786					.on('click', 'input[type="radio"]', function() {
4787						var newSpeed = $(this).attr('value');
4788						playbackSpeed = newSpeed;
4789						media.playbackRate = parseFloat(newSpeed);
4790						speedButton.find('button').html(getSpeedNameFromValue(newSpeed));
4791						speedButton.find('.mejs-speed-selected').removeClass('mejs-speed-selected');
4792						speedButton.find('input[type="radio"]:checked').next().addClass('mejs-speed-selected');
4793					});
4794				speedButton
4795					.one( 'mouseenter focusin', function() {
4796						speedSelector
4797							.height(
4798								speedButton.find('.mejs-speed-selector ul').outerHeight(true) +
4799								speedButton.find('.mejs-speed-translations').outerHeight(true))
4800							.css('top', (-1 * speedSelector.height()) + 'px');
4801					});
4802			}
4803		}
4804	});
4805
4806})(mejs.$);
4807
4808(function($) {
4809
4810	// add extra default options
4811	$.extend(mejs.MepDefaults, {
4812		// this will automatically turn on a <track>
4813		startLanguage: '',
4814
4815		tracksText: mejs.i18n.t('Captions/Subtitles'),
4816
4817		// By default, no WAI-ARIA live region - don't make a
4818		// screen reader speak captions over an audio track.
4819		tracksAriaLive: false,
4820
4821		// option to remove the [cc] button when no <track kind="subtitles"> are present
4822		hideCaptionsButtonWhenEmpty: true,
4823
4824		// If true and we only have one track, change captions to popup
4825		toggleCaptionsButtonWhenOnlyOne: false,
4826
4827		// #id or .class
4828		slidesSelector: ''
4829	});
4830
4831	$.extend(MediaElementPlayer.prototype, {
4832
4833		hasChapters: false,
4834
4835		cleartracks: function(player, controls, layers, media){
4836			if(player) {
4837				if(player.captions) player.captions.remove();
4838				if(player.chapters) player.chapters.remove();
4839				if(player.captionsText) player.captionsText.remove();
4840				if(player.captionsButton) player.captionsButton.remove();
4841			}
4842		},
4843		buildtracks: function(player, controls, layers, media) {
4844			if (player.tracks.length === 0)
4845				return;
4846
4847			var t = this,
4848				attr = t.options.tracksAriaLive ?
4849					'role="log" aria-live="assertive" aria-atomic="false"' : '',
4850				i;
4851
4852			if (t.domNode.textTracks) { // if browser will do native captions, prefer mejs captions, loop through tracks and hide
4853				for (i = t.domNode.textTracks.length - 1; i >= 0; i--) {
4854					t.domNode.textTracks[i].mode = "hidden";
4855				}
4856			}
4857			t.cleartracks(player, controls, layers, media);
4858			player.chapters =
4859					$('<div class="mejs-chapters mejs-layer"></div>')
4860						.prependTo(layers).hide();
4861			player.captions =
4862					$('<div class="mejs-captions-layer mejs-layer"><div class="mejs-captions-position mejs-captions-position-hover" ' +
4863					attr + '><span class="mejs-captions-text"></span></div></div>')
4864						.prependTo(layers).hide();
4865			player.captionsText = player.captions.find('.mejs-captions-text');
4866			player.captionsButton =
4867					$('<div class="mejs-button mejs-captions-button">'+
4868						'<button type="button" aria-controls="' + t.id + '" title="' + t.options.tracksText + '" aria-label="' + t.options.tracksText + '"></button>'+
4869						'<div class="mejs-captions-selector">'+
4870							'<ul>'+
4871								'<li>'+
4872									'<input type="radio" name="' + player.id + '_captions" id="' + player.id + '_captions_none" value="none" checked="checked" />' +
4873									'<label for="' + player.id + '_captions_none">' + mejs.i18n.t('None') +'</label>'+
4874								'</li>'	+
4875							'</ul>'+
4876						'</div>'+
4877					'</div>')
4878						.appendTo(controls);
4879
4880
4881			var subtitleCount = 0;
4882			for (i=0; i<player.tracks.length; i++) {
4883				if (player.tracks[i].kind == 'subtitles') {
4884					subtitleCount++;
4885				}
4886			}
4887
4888			// if only one language then just make the button a toggle
4889			if (t.options.toggleCaptionsButtonWhenOnlyOne && subtitleCount == 1){
4890				// click
4891				player.captionsButton.on('click',function() {
4892					if (player.selectedTrack === null) {
4893						lang = player.tracks[0].srclang;
4894					} else {
4895						lang = 'none';
4896					}
4897					player.setTrack(lang);
4898				});
4899			} else {
4900				// hover or keyboard focus
4901				player.captionsButton.on( 'mouseenter focusin', function() {
4902					$(this).find('.mejs-captions-selector').css('visibility','visible');
4903				})
4904
4905				// handle clicks to the language radio buttons
4906				.on('click','input[type=radio]',function() {
4907					lang = this.value;
4908					player.setTrack(lang);
4909				});
4910
4911				player.captionsButton.on( 'mouseleave focusout', function() {
4912					$(this).find(".mejs-captions-selector").css("visibility","hidden");
4913				});
4914
4915			}
4916
4917			if (!player.options.alwaysShowControls) {
4918				// move with controls
4919				player.container
4920					.bind('controlsshown', function () {
4921						// push captions above controls
4922						player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
4923
4924					})
4925					.bind('controlshidden', function () {
4926						if (!media.paused) {
4927							// move back to normal place
4928							player.container.find('.mejs-captions-position').removeClass('mejs-captions-position-hover');
4929						}
4930					});
4931			} else {
4932				player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
4933			}
4934
4935			player.trackToLoad = -1;
4936			player.selectedTrack = null;
4937			player.isLoadingTrack = false;
4938
4939			// add to list
4940			for (i=0; i<player.tracks.length; i++) {
4941				if (player.tracks[i].kind == 'subtitles') {
4942					player.addTrackButton(player.tracks[i].srclang, player.tracks[i].label);
4943				}
4944			}
4945
4946			// start loading tracks
4947			player.loadNextTrack();
4948
4949			media.addEventListener('timeupdate',function(e) {
4950				player.displayCaptions();
4951			}, false);
4952
4953			if (player.options.slidesSelector !== '') {
4954				player.slidesContainer = $(player.options.slidesSelector);
4955
4956				media.addEventListener('timeupdate',function(e) {
4957					player.displaySlides();
4958				}, false);
4959
4960			}
4961
4962			media.addEventListener('loadedmetadata', function(e) {
4963				player.displayChapters();
4964			}, false);
4965
4966			player.container.hover(
4967				function () {
4968					// chapters
4969					if (player.hasChapters) {
4970						player.chapters.css('visibility','visible');
4971						player.chapters.fadeIn(200).height(player.chapters.find('.mejs-chapter').outerHeight());
4972					}
4973				},
4974				function () {
4975					if (player.hasChapters && !media.paused) {
4976						player.chapters.fadeOut(200, function() {
4977							$(this).css('visibility','hidden');
4978							$(this).css('display','block');
4979						});
4980					}
4981				});
4982
4983			t.container.on('controlsresize', function() {
4984				t.adjustLanguageBox();
4985			});
4986			
4987			// check for autoplay
4988			if (player.node.getAttribute('autoplay') !== null) {
4989				player.chapters.css('visibility','hidden');
4990			}
4991		},
4992
4993		setTrack: function(lang){
4994
4995			var t = this,
4996				i;
4997
4998			if (lang == 'none') {
4999				t.selectedTrack = null;
5000				t.captionsButton.removeClass('mejs-captions-enabled');
5001			} else {
5002				for (i=0; i<t.tracks.length; i++) {
5003					if (t.tracks[i].srclang == lang) {
5004						if (t.selectedTrack === null)
5005							t.captionsButton.addClass('mejs-captions-enabled');
5006						t.selectedTrack = t.tracks[i];
5007						t.captions.attr('lang', t.selectedTrack.srclang);
5008						t.displayCaptions();
5009						break;
5010					}
5011				}
5012			}
5013		},
5014
5015		loadNextTrack: function() {
5016			var t = this;
5017
5018			t.trackToLoad++;
5019			if (t.trackToLoad < t.tracks.length) {
5020				t.isLoadingTrack = true;
5021				t.loadTrack(t.trackToLoad);
5022			} else {
5023				// add done?
5024				t.isLoadingTrack = false;
5025
5026				t.checkForTracks();
5027			}
5028		},
5029
5030		loadTrack: function(index){
5031			var
5032				t = this,
5033				track = t.tracks[index],
5034				after = function() {
5035
5036					track.isLoaded = true;
5037
5038					t.enableTrackButton(track.srclang, track.label);
5039
5040					t.loadNextTrack();
5041
5042				};
5043
5044
5045			$.ajax({
5046				url: track.src,
5047				dataType: "text",
5048				success: function(d) {
5049
5050					// parse the loaded file
5051					if (typeof d == "string" && (/<tt\s+xml/ig).exec(d)) {
5052						track.entries = mejs.TrackFormatParser.dfxp.parse(d);
5053					} else {
5054						track.entries = mejs.TrackFormatParser.webvtt.parse(d);
5055					}
5056
5057					after();
5058
5059					if (track.kind == 'chapters') {
5060						t.media.addEventListener('play', function(e) {
5061							if (t.media.duration > 0) {
5062								t.displayChapters(track);
5063							}
5064						}, false);
5065					}
5066
5067					if (track.kind == 'slides') {
5068						t.setupSlides(track);
5069					}
5070				},
5071				error: function() {
5072					t.removeTrackButton(track.srclang);
5073					t.loadNextTrack();
5074				}
5075			});
5076		},
5077
5078		enableTrackButton: function(lang, label) {
5079			var t = this;
5080
5081			if (label === '') {
5082				label = mejs.language.codes[lang] || lang;
5083			}
5084
5085			t.captionsButton
5086				.find('input[value=' + lang + ']')
5087					.prop('disabled',false)
5088				.siblings('label')
5089					.html( label );
5090
5091			// auto select
5092			if (t.options.startLanguage == lang) {
5093				$('#' + t.id + '_captions_' + lang).prop('checked', true).trigger('click');
5094			}
5095
5096			t.adjustLanguageBox();
5097		},
5098		
5099		removeTrackButton: function(lang) {
5100			var t = this;
5101			
5102			t.captionsButton.find('input[value=' + lang + ']').closest('li').remove();
5103			
5104			t.adjustLanguageBox();
5105		},
5106
5107		addTrackButton: function(lang, label) {
5108			var t = this;
5109			if (label === '') {
5110				label = mejs.language.codes[lang] || lang;
5111			}
5112
5113			t.captionsButton.find('ul').append(
5114				$('<li>'+
5115					'<input type="radio" name="' + t.id + '_captions" id="' + t.id + '_captions_' + lang + '" value="' + lang + '" disabled="disabled" />' +
5116					'<label for="' + t.id + '_captions_' + lang + '">' + label + ' (loading)' + '</label>'+
5117				'</li>')
5118			);
5119
5120			t.adjustLanguageBox();
5121
5122			// remove this from the dropdownlist (if it exists)
5123			t.container.find('.mejs-captions-translations option[value=' + lang + ']').remove();
5124		},
5125
5126		adjustLanguageBox:function() {
5127			var t = this;
5128			// adjust the size of the outer box
5129			t.captionsButton.find('.mejs-captions-selector').height(
5130				t.captionsButton.find('.mejs-captions-selector ul').outerHeight(true) +
5131				t.captionsButton.find('.mejs-captions-translations').outerHeight(true)
5132			);
5133		},
5134
5135		checkForTracks: function() {
5136			var
5137				t = this,
5138				hasSubtitles = false;
5139
5140			// check if any subtitles
5141			if (t.options.hideCaptionsButtonWhenEmpty) {
5142				for (i=0; i<t.tracks.length; i++) {
5143					if (t.tracks[i].kind == 'subtitles' && t.tracks[i].isLoaded) {
5144						hasSubtitles = true;
5145						break;
5146					}
5147				}
5148
5149				if (!hasSubtitles) {
5150					t.captionsButton.hide();
5151					t.setControlsSize();
5152				}
5153			}
5154		},
5155
5156		displayCaptions: function() {
5157
5158			if (typeof this.tracks == 'undefined')
5159				return;
5160
5161			var
5162				t = this,
5163				i,
5164				track = t.selectedTrack;
5165
5166			if (track !== null && track.isLoaded) {
5167				for (i=0; i<track.entries.times.length; i++) {
5168					if (t.media.currentTime >= track.entries.times[i].start && t.media.currentTime <= track.entries.times[i].stop) {
5169						// Set the line before the timecode as a class so the cue can be targeted if needed
5170						t.captionsText.html(track.entries.text[i]).attr('class', 'mejs-captions-text ' + (track.entries.times[i].identifier || ''));
5171						t.captions.show().height(0);
5172						return; // exit out if one is visible;
5173					}
5174				}
5175				t.captions.hide();
5176			} else {
5177				t.captions.hide();
5178			}
5179		},
5180
5181		setupSlides: function(track) {
5182			var t = this;
5183
5184			t.slides = track;
5185			t.slides.entries.imgs = [t.slides.entries.text.length];
5186			t.showSlide(0);
5187
5188		},
5189
5190		showSlide: function(index) {
5191			if (typeof this.tracks == 'undefined' || typeof this.slidesContainer == 'undefined') {
5192				return;
5193			}
5194
5195			var t = this,
5196				url = t.slides.entries.text[index],
5197				img = t.slides.entries.imgs[index];
5198
5199			if (typeof img == 'undefined' || typeof img.fadeIn == 'undefined') {
5200
5201				t.slides.entries.imgs[index] = img = $('<img src="' + url + '">')
5202						.on('load', function() {
5203							img.appendTo(t.slidesContainer)
5204								.hide()
5205								.fadeIn()
5206								.siblings(':visible')
5207									.fadeOut();
5208
5209						});
5210
5211			} else {
5212
5213				if (!img.is(':visible') && !img.is(':animated')) {
5214
5215					//
5216
5217					img.fadeIn()
5218						.siblings(':visible')
5219							.fadeOut();
5220				}
5221			}
5222
5223		},
5224
5225		displaySlides: function() {
5226
5227			if (typeof this.slides == 'undefined')
5228				return;
5229
5230			var
5231				t = this,
5232				slides = t.slides,
5233				i;
5234
5235			for (i=0; i<slides.entries.times.length; i++) {
5236				if (t.media.currentTime >= slides.entries.times[i].start && t.media.currentTime <= slides.entries.times[i].stop){
5237
5238					t.showSlide(i);
5239
5240					return; // exit out if one is visible;
5241				}
5242			}
5243		},
5244
5245		displayChapters: function() {
5246			var
5247				t = this,
5248				i;
5249
5250			for (i=0; i<t.tracks.length; i++) {
5251				if (t.tracks[i].kind == 'chapters' && t.tracks[i].isLoaded) {
5252					t.drawChapters(t.tracks[i]);
5253					t.hasChapters = true;
5254					break;
5255				}
5256			}
5257		},
5258
5259		drawChapters: function(chapters) {
5260			var
5261				t = this,
5262				i,
5263				dur,
5264				//width,
5265				//left,
5266				percent = 0,
5267				usedPercent = 0;
5268
5269			t.chapters.empty();
5270
5271			for (i=0; i<chapters.entries.times.length; i++) {
5272				dur = chapters.entries.times[i].stop - chapters.entries.times[i].start;
5273				percent = Math.floor(dur / t.media.duration * 100);
5274				if (percent + usedPercent > 100 || // too large
5275					i == chapters.entries.times.length-1 && percent + usedPercent < 100) // not going to fill it in
5276					{
5277					percent = 100 - usedPercent;
5278				}
5279				//width = Math.floor(t.width * dur / t.media.duration);
5280				//left = Math.floor(t.width * chapters.entries.times[i].start / t.media.duration);
5281				//if (left + width > t.width) {
5282				//	width = t.width - left;
5283				//}
5284
5285				t.chapters.append( $(
5286					'<div class="mejs-chapter" rel="' + chapters.entries.times[i].start + '" style="left: ' + usedPercent.toString() + '%;width: ' + percent.toString() + '%;">' +
5287						'<div class="mejs-chapter-block' + ((i==chapters.entries.times.length-1) ? ' mejs-chapter-block-last' : '') + '">' +
5288							'<span class="ch-title">' + chapters.entries.text[i] + '</span>' +
5289							'<span class="ch-time">' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].start, t.options) + '&ndash;' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].stop, t.options) + '</span>' +
5290						'</div>' +
5291					'</div>'));
5292				usedPercent += percent;
5293			}
5294
5295			t.chapters.find('div.mejs-chapter').click(function() {
5296				t.media.setCurrentTime( parseFloat( $(this).attr('rel') ) );
5297				if (t.media.paused) {
5298					t.media.play();
5299				}
5300			});
5301
5302			t.chapters.show();
5303		}
5304	});
5305
5306
5307
5308	mejs.language = {
5309		codes:  {
5310			af:'Afrikaans',
5311			sq:'Albanian',
5312			ar:'Arabic',
5313			be:'Belarusian',
5314			bg:'Bulgarian',
5315			ca:'Catalan',
5316			zh:'Chinese',
5317			'zh-cn':'Chinese Simplified',
5318			'zh-tw':'Chinese Traditional',
5319			hr:'Croatian',
5320			cs:'Czech',
5321			da:'Danish',
5322			nl:'Dutch',
5323			en:'English',
5324			et:'Estonian',
5325			fl:'Filipino',
5326			fi:'Finnish',
5327			fr:'French',
5328			gl:'Galician',
5329			de:'German',
5330			el:'Greek',
5331			ht:'Haitian Creole',
5332			iw:'Hebrew',
5333			hi:'Hindi',
5334			hu:'Hungarian',
5335			is:'Icelandic',
5336			id:'Indonesian',
5337			ga:'Irish',
5338			it:'Italian',
5339			ja:'Japanese',
5340			ko:'Korean',
5341			lv:'Latvian',
5342			lt:'Lithuanian',
5343			mk:'Macedonian',
5344			ms:'Malay',
5345			mt:'Maltese',
5346			no:'Norwegian',
5347			fa:'Persian',
5348			pl:'Polish',
5349			pt:'Portuguese',
5350			// 'pt-pt':'Portuguese (Portugal)',
5351			ro:'Romanian',
5352			ru:'Russian',
5353			sr:'Serbian',
5354			sk:'Slovak',
5355			sl:'Slovenian',
5356			es:'Spanish',
5357			sw:'Swahili',
5358			sv:'Swedish',
5359			tl:'Tagalog',
5360			th:'Thai',
5361			tr:'Turkish',
5362			uk:'Ukrainian',
5363			vi:'Vietnamese',
5364			cy:'Welsh',
5365			yi:'Yiddish'
5366		}
5367	};
5368
5369	/*
5370	Parses WebVTT format which should be formatted as
5371	================================
5372	WEBVTT
5373
5374	1
5375	00:00:01,1 --> 00:00:05,000
5376	A line of text
5377
5378	2
5379	00:01:15,1 --> 00:02:05,000
5380	A second line of text
5381
5382	===============================
5383
5384	Adapted from: http://www.delphiki.com/html5/playr
5385	*/
5386	mejs.TrackFormatParser = {
5387		webvtt: {
5388			pattern_timecode: /^((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{1,3})?) --\> ((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{3})?)(.*)$/,
5389
5390			parse: function(trackText) {
5391				var
5392					i = 0,
5393					lines = mejs.TrackFormatParser.split2(trackText, /\r?\n/),
5394					entries = {text:[], times:[]},
5395					timecode,
5396					text,
5397					identifier;
5398				for(; i<lines.length; i++) {
5399					timecode = this.pattern_timecode.exec(lines[i]);
5400
5401					if (timecode && i<lines.length) {
5402						if ((i - 1) >= 0 && lines[i - 1] !== '') {
5403							identifier = lines[i - 1];
5404						}
5405						i++;
5406						// grab all the (possibly multi-line) text that follows
5407						text = lines[i];
5408						i++;
5409						while(lines[i] !== '' && i<lines.length){
5410							text = text + '\n' + lines[i];
5411							i++;
5412						}
5413						text = $.trim(text).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>");
5414						// Text is in a different array so I can use .join
5415						entries.text.push(text);
5416						entries.times.push(
5417						{
5418							identifier: identifier,
5419							start: (mejs.Utility.convertSMPTEtoSeconds(timecode[1]) === 0) ? 0.200 : mejs.Utility.convertSMPTEtoSeconds(timecode[1]),
5420							stop: mejs.Utility.convertSMPTEtoSeconds(timecode[3]),
5421							settings: timecode[5]
5422						});
5423					}
5424					identifier = '';
5425				}
5426				return entries;
5427			}
5428		},
5429		// Thanks to Justin Capella: https://github.com/johndyer/mediaelement/pull/420
5430		dfxp: {
5431			parse: function(trackText) {
5432				trackText = $(trackText).filter("tt");
5433				var
5434					i = 0,
5435					container = trackText.children("div").eq(0),
5436					lines = container.find("p"),
5437					styleNode = trackText.find("#" + container.attr("style")),
5438					styles,
5439					text,
5440					entries = {text:[], times:[]};
5441
5442
5443				if (styleNode.length) {
5444					var attributes = styleNode.removeAttr("id").get(0).attributes;
5445					if (attributes.length) {
5446						styles = {};
5447						for (i = 0; i < attributes.length; i++) {
5448							styles[attributes[i].name.split(":")[1]] = attributes[i].value;
5449						}
5450					}
5451				}
5452
5453				for(i = 0; i<lines.length; i++) {
5454					var style;
5455					var _temp_times = {
5456						start: null,
5457						stop: null,
5458						style: null
5459					};
5460					if (lines.eq(i).attr("begin")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("begin"));
5461					if (!_temp_times.start && lines.eq(i-1).attr("end")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i-1).attr("end"));
5462					if (lines.eq(i).attr("end")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("end"));
5463					if (!_temp_times.stop && lines.eq(i+1).attr("begin")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i+1).attr("begin"));
5464					if (styles) {
5465						style = "";
5466						for (var _style in styles) {
5467							style += _style + ":" + styles[_style] + ";";
5468						}
5469					}
5470					if (style) _temp_times.style = style;
5471					if (_temp_times.start === 0) _temp_times.start = 0.200;
5472					entries.times.push(_temp_times);
5473					text = $.trim(lines.eq(i).html()).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>");
5474					entries.text.push(text);
5475					if (entries.times.start === 0) entries.times.start = 2;
5476				}
5477				return entries;
5478			}
5479		},
5480		split2: function (text, regex) {
5481			// normal version for compliant browsers
5482			// see below for IE fix
5483			return text.split(regex);
5484		}
5485	};
5486
5487	// test for browsers with bad String.split method.
5488	if ('x\n\ny'.split(/\n/gi).length != 3) {
5489		// add super slow IE8 and below version
5490		mejs.TrackFormatParser.split2 = function(text, regex) {
5491			var
5492				parts = [],
5493				chunk = '',
5494				i;
5495
5496			for (i=0; i<text.length; i++) {
5497				chunk += text.substring(i,i+1);
5498				if (regex.test(chunk)) {
5499					parts.push(chunk.replace(regex, ''));
5500					chunk = '';
5501				}
5502			}
5503			parts.push(chunk);
5504			return parts;
5505		};
5506	}
5507
5508})(mejs.$);
5509
5510/*
5511* ContextMenu Plugin
5512* 
5513*
5514*/
5515
5516(function($) {
5517
5518$.extend(mejs.MepDefaults,
5519	{ 'contextMenuItems': [
5520		// demo of a fullscreen option
5521		{ 
5522			render: function(player) {
5523				
5524				// check for fullscreen plugin
5525				if (typeof player.enterFullScreen == 'undefined')
5526					return null;
5527			
5528				if (player.isFullScreen) {
5529					return mejs.i18n.t('Turn off Fullscreen');
5530				} else {
5531					return mejs.i18n.t('Go Fullscreen');
5532				}
5533			},
5534			click: function(player) {
5535				if (player.isFullScreen) {
5536					player.exitFullScreen();
5537				} else {
5538					player.enterFullScreen();
5539				}
5540			}
5541		}
5542		,
5543		// demo of a mute/unmute button
5544		{ 
5545			render: function(player) {
5546				if (player.media.muted) {
5547					return mejs.i18n.t('Unmute');
5548				} else {
5549					return mejs.i18n.t('Mute');
5550				}
5551			},
5552			click: function(player) {
5553				if (player.media.muted) {
5554					player.setMuted(false);
5555				} else {
5556					player.setMuted(true);
5557				}
5558			}
5559		},
5560		// separator
5561		{
5562			isSeparator: true
5563		}
5564		,
5565		// demo of simple download video
5566		{ 
5567			render: function(player) {
5568				return mejs.i18n.t('Download Video');
5569			},
5570			click: function(player) {
5571				window.location.href = player.media.currentSrc;
5572			}
5573		}	
5574	]}
5575);
5576
5577
5578	$.extend(MediaElementPlayer.prototype, {
5579		buildcontextmenu: function(player, controls, layers, media) {
5580			
5581			// create context menu
5582			player.contextMenu = $('<div class="mejs-contextmenu"></div>')
5583								.appendTo($('body'))
5584								.hide();
5585			
5586			// create events for showing context menu
5587			player.container.bind('contextmenu', function(e) {
5588				if (player.isContextMenuEnabled) {
5589					e.preventDefault();
5590					player.renderContextMenu(e.clientX-1, e.clientY-1);
5591					return false;
5592				}
5593			});
5594			player.container.bind('click', function() {
5595				player.contextMenu.hide();
5596			});	
5597			player.contextMenu.bind('mouseleave', function() {
5598
5599				//
5600				player.startContextMenuTimer();
5601				
5602			});		
5603		},
5604
5605		cleancontextmenu: function(player) {
5606			player.contextMenu.remove();
5607		},
5608		
5609		isContextMenuEnabled: true,
5610		enableContextMenu: function() {
5611			this.isContextMenuEnabled = true;
5612		},
5613		disableContextMenu: function() {
5614			this.isContextMenuEnabled = false;
5615		},
5616		
5617		contextMenuTimeout: null,
5618		startContextMenuTimer: function() {
5619			//
5620			
5621			var t = this;
5622			
5623			t.killContextMenuTimer();
5624			
5625			t.contextMenuTimer = setTimeout(function() {
5626				t.hideContextMenu();
5627				t.killContextMenuTimer();
5628			}, 750);
5629		},
5630		killContextMenuTimer: function() {
5631			var timer = this.contextMenuTimer;
5632			
5633			//
5634			
5635			if (timer != null) {				
5636				clearTimeout(timer);
5637				delete timer;
5638				timer = null;
5639			}
5640		},		
5641		
5642		hideContextMenu: function() {
5643			this.contextMenu.hide();
5644		},
5645		
5646		renderContextMenu: function(x,y) {
5647			
5648			// alway re-render the items so that things like "turn fullscreen on" and "turn fullscreen off" are always written correctly
5649			var t = this,
5650				html = '',
5651				items = t.options.contextMenuItems;
5652			
5653			for (var i=0, il=items.length; i<il; i++) {
5654				
5655				if (items[i].isSeparator) {
5656					html += '<div class="mejs-contextmenu-separator"></div>';
5657				} else {
5658				
5659					var rendered = items[i].render(t);
5660				
5661					// render can return null if the item doesn't need to be used at the moment
5662					if (rendered != null) {
5663						html += '<div class="mejs-contextmenu-item" data-itemindex="' + i + '" id="element-' + (Math.random()*1000000) + '">' + rendered + '</div>';
5664					}
5665				}
5666			}
5667			
5668			// position and show the context menu
5669			t.contextMenu
5670				.empty()
5671				.append($(html))
5672				.css({top:y, left:x})
5673				.show();
5674				
5675			// bind events
5676			t.contextMenu.find('.mejs-contextmenu-item').each(function() {
5677							
5678				// which one is this?
5679				var $dom = $(this),
5680					itemIndex = parseInt( $dom.data('itemindex'), 10 ),
5681					item = t.options.contextMenuItems[itemIndex];
5682				
5683				// bind extra functionality?
5684				if (typeof item.show != 'undefined')
5685					item.show( $dom , t);
5686				
5687				// bind click action
5688				$dom.click(function() {			
5689					// perform click action
5690					if (typeof item.click != 'undefined')
5691						item.click(t);
5692					
5693					// close
5694					t.contextMenu.hide();				
5695				});				
5696			});	
5697			
5698			// stop the controls from hiding
5699			setTimeout(function() {
5700				t.killControlsTimer('rev3');	
5701			}, 100);
5702						
5703		}
5704	});
5705	
5706})(mejs.$);
5707(function($) {
5708	// skip back button
5709
5710	$.extend(mejs.MepDefaults, {
5711		skipBackInterval: 30,
5712		// %1 will be replaced with skipBackInterval in this string
5713		skipBackText: mejs.i18n.t('Skip back %1 seconds')
5714	});
5715
5716	$.extend(MediaElementPlayer.prototype, {
5717		buildskipback: function(player, controls, layers, media) {
5718			var
5719				t = this,
5720				// Replace %1 with skip back interval
5721				backText = t.options.skipBackText.replace('%1', t.options.skipBackInterval),
5722				// create the loop button
5723				loop =
5724				$('<div class="mejs-button mejs-skip-back-button">' +
5725					'<button type="button" aria-controls="' + t.id + '" title="' + backText + '" aria-label="' + backText + '">' + t.options.skipBackInterval + '</button>' +
5726				'</div>')
5727				// append it to the toolbar
5728				.appendTo(controls)
5729				// add a click toggle event
5730				.click(function() {
5731					media.setCurrentTime(Math.max(media.currentTime - t.options.skipBackInterval, 0));
5732					$(this).find('button').blur();
5733				});
5734		}
5735	});
5736
5737})(mejs.$);
5738
5739/**
5740 * Postroll plugin
5741 */
5742(function($) {
5743
5744	$.extend(mejs.MepDefaults, {
5745		postrollCloseText: mejs.i18n.t('Close')
5746	});
5747
5748	// Postroll
5749	$.extend(MediaElementPlayer.prototype, {
5750		buildpostroll: function(player, controls, layers, media) {
5751			var
5752				t = this,
5753				postrollLink = t.container.find('link[rel="postroll"]').attr('href');
5754
5755			if (typeof postrollLink !== 'undefined') {
5756				player.postroll =
5757					$('<div class="mejs-postroll-layer mejs-layer"><a class="mejs-postroll-close" onclick="$(this).parent().hide();return false;">' + t.options.postrollCloseText + '</a>
5757<div class="mejs-postroll-layer-content"></div></div>').prependTo(layers).hide();
5758
5759				t.media.addEventListener('ended', function (e) {
5760					$.ajax({
5761						dataType: 'html',
5762						url: postrollLink,
5763						success: function (data, textStatus) {
5764							layers.find('.mejs-postroll-layer-content').html(data);
5765						}
5766					});
5767					player.postroll.show();
5768				}, false);
5769			}
5770		}
5771	});
5772
5773})(mejs.$);
5774define("mediaelement", function(){});
5775
5776/*
5777* Google Analytics Plugin
5778* Requires
5779*
5780*/
5781
5782(function($) {
5783
5784$.extend(mejs.MepDefaults, {
5785	googleAnalyticsTitle: '',
5786	googleAnalyticsCategory: 'Videos',
5787	googleAnalyticsEventPlay: 'Play',
5788	googleAnalyticsEventPause: 'Pause',
5789	googleAnalyticsEventEnded: 'Ended',
5790	googleAnalyticsEventTime: 'Time'
5791});
5792
5793
5794$.extend(MediaElementPlayer.prototype, {
5795	buildgoogleanalytics: function(player, controls, layers, media) {
5796
5797		media.addEventListener('play', function() {
5798			if (typeof _gaq != 'undefined') {
5799				_gaq.push(['_trackEvent',
5800					player.options.googleAnalyticsCategory,
5801					player.options.googleAnalyticsEventPlay,
5802					(player.options.googleAnalyticsTitle === '') ? player.media.currentSrc : player.options.googleAnalyticsTitle
5803				]);
5804			}
5805		}, false);
5806
5807		media.addEventListener('pause', function() {
5808			if (typeof _gaq != 'undefined') {
5809				_gaq.push(['_trackEvent',
5810					player.options.googleAnalyticsCategory,
5811					player.options.googleAnalyticsEventPause,
5812					(player.options.googleAnalyticsTitle === '') ? player.media.currentSrc : player.options.googleAnalyticsTitle
5813				]);
5814			}
5815		}, false);
5816
5817		media.addEventListener('ended', function() {
5818			if (typeof _gaq != 'undefined') {
5819				_gaq.push(['_trackEvent',
5820					player.options.googleAnalyticsCategory,
5821					player.options.googleAnalyticsEventEnded,
5822					(player.options.googleAnalyticsTitle === '') ? player.media.currentSrc : player.options.googleAnalyticsTitle
5823				]);
5824			}
5825		}, false);
5826
5827		/*
5828		media.addEventListener('timeupdate', function() {
5829			if (typeof _gaq != 'undefined') {
5830				_gaq.push(['_trackEvent',
5831					player.options.googleAnalyticsCategory,
5832					player.options.googleAnalyticsEventEnded, 
5833					player.options.googleAnalyticsTime,
5834					(player.options.googleAnalyticsTitle === '') ? player.media.currentSrc : player.options.googleAnalyticsTitle,
5835					player.currentTime
5836				]);
5837			}
5838		}, true);
5839		*/
5840	}
5841});
5842
5843})(mejs.$);
5844
5845define("mep-feature-googleanalytics", function(){});
5846
5847/*
5848* analytics.js Google Analytics Plugin
5849* Requires JQuery
5850*/
5851
5852(function($) {
5853
5854$.extend(mejs.MepDefaults, {
5855	googleAnalyticsTitle: '',
5856	googleAnalyticsCategory: 'Videos',
5857	googleAnalyticsEventPlay: 'Play',
5858	googleAnalyticsEventPause: 'Pause',
5859	googleAnalyticsEventEnded: 'Ended',
5860	googleAnalyticsEventTime: 'Time'
5861});
5862
5863
5864$.extend(MediaElementPlayer.prototype, {
5865	builduniversalgoogleanalytics: function(player, controls, layers, media) {
5866
5867		media.addEventListener('play', function() {
5868			if (typeof ga != 'undefined') {
5869				ga('send', 'event',
5870					player.options.googleAnalyticsCategory,
5871					player.options.googleAnalyticsEventPlay,
5872					(player.options.googleAnalyticsTitle === '') ? player.media.currentSrc : player.options.googleAnalyticsTitle
5873				);
5874			}
5875		}, false);
5876
5877		media.addEventListener('pause', function() {
5878			if (typeof ga != 'undefined') {
5879				ga('send', 'event',
5880					player.options.googleAnalyticsCategory,
5881					player.options.googleAnalyticsEventPause,
5882					(player.options.googleAnalyticsTitle === '') ? player.media.currentSrc : player.options.googleAnalyticsTitle
5883				);
5884			}
5885		}, false);
5886
5887		media.addEventListener('ended', function() {
5888			if (typeof ga != 'undefined') {
5889				ga('send', 'event',
5890					player.options.googleAnalyticsCategory,
5891					player.options.googleAnalyticsEventEnded,
5892					(player.options.googleAnalyticsTitle === '') ? player.media.currentSrc : player.options.googleAnalyticsTitle
5893				);
5894			}
5895		}, false);
5896
5897		/*
5898		media.addEventListener('timeupdate', function() {
5899			if (typeof ga != 'undefined') {
5900				ga('send', 'event',
5901					player.options.googleAnalyticsCategory,
5902					player.options.googleAnalyticsEventEnded, 
5903					player.options.googleAnalyticsTime,
5904				  (player.options.googleAnalyticsTitle === '') ? player.media.currentSrc : player.options.googleAnalyticsTitle,
5905					player.currentTime
5906				);
5907			}
5908		}, true);
5909		*/
5910	}
5911});
5912
5913})(mejs.$);
5914
5915define("mep-feature-universalgoogleanalytics", function(){});
5916
5917/* global define */
5918define('wildcard-patterns-video',[
5919  'jquery',
5920  'mockup-patterns-base',
5921  'mediaelement',
5922  'mep-feature-googleanalytics',
5923  'mep-feature-universalgoogleanalytics'
5924], function($, Base) {
5925  'use strict';
5926
5927  var Media = Base.extend({
5928    name: 'media',
5929    trigger: '.pat-media',
5930    defaults: {
5931    },
5932    init: function() {
5933      var self = this;
5934      self.$el.mediaelementplayer({
5935        pluginPath: '++resource++wildcard-media/components/mediaelement/build/',
5936        features: ['playpause','current','progress','duration','tracks','volume','fullscreen',
5937                   'googleanalytics', 'universalgoogleanalytics']
5938      });
5939    }
5940  });
5941
5942  return Media;
5943
5944});
5945
5946/* global require */
5947
5948if(window.jQuery){
5949  define('jquery', [],function(){
5950    return window.jQuery;
5951  });
5952}
5953
5954
5955require([
5956  'jquery',
5957  'wildcard-patterns-video'
5958  ], function($){
5959  'use strict';
5960
5961  $(document).ready(function(){
5962
5963    $('span.wcvideo a').each(function(){
5964      var $a = $(this);
5965      var $span = $a.parents('span.wcvideo');
5966      var width, height;
5967      if($span.hasClass('video-large')){
5968        width = 720;
5969        height = 480;
5970      }else if($span.hasClass('video-small')){
5971        width = 320;
5972        height = 240;
5973      }
5974      $.ajax({
5975        url: $a.attr('href') + '/@@wildcard_video_macro',
5976        success: function(data){
5977          var $video = $(data);
5978          if(width && height){
5979            $video.find('[width]').attr('width', width);
5980            $video.find('[height]').attr('height', height);
5981          }
5982          $span.replaceWith($video);
5983          $video.find('video').mediaelementplayer({
5984            pluginPath: '++resource++wildcard-media/components/mediaelement/build/',
5985            features: ['playpause', 'current', 'progress', 'duration', 'tracks', 'volume', 'fullscreen',
5986                       'googleanalytics', 'universalgoogleanalytics']
5987          });
5988        }
5989      });
5990    });
5991    $('span.wcaudio a').each(function(){
5992      var $a = $(this);
5993      var $span = $a.parents('span.wcaudio');
5994      var $audio = $('<audio controls="controls" preload="none"' +
5995        'src="' + $a.attr('href') + '/@@view/++widget++form.widgets.IAudio.audio_file/@@download/file.mp3' + '"></audio>');
5996      $span.replaceWith($audio);
5997      $audio.mediaelementplayer({
5998        features: ['playpause', 'current', 'progress', 'duration', 'tracks', 'volume', 'fullscreen',
5999                   'googleanalytics', 'universalgoogleanalytics']
6000      });
6001    });
6002
6003    var selector = '.template-wildcardvideo #form-widgets-IVideo-upload_video_to_youtube-0';
6004    var checkFields = function(){
6005      if($(selector)[0].checked){
6006        $('#formfield-form-widgets-IVideo-youtube_url').hide();
6007      }else{
6008        $('#formfield-form-widgets-IVideo-youtube_url').show();
6009      }
6010    };
6011    if($(selector).change(checkFields).size() > 0){
6012      checkFields();
6013    }
6014
6015  });
6016
6017});
6018
6019define("js/bundle.js", function(){});
6020

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.