PageSourceSearch

https://hannahimlach.com/_js/cargo.video.package.js

js hannahimlach.com collected 2026-10-04 13:23:43 UTC 38,514 bytes, 1,440 lines download raw bytes

1/*!
2 * jMediaelement with additions, customizations
3 */
4(function($) {
5	var iosmessage = '<div>The video can not be displayed on this platform.</div>';
6	var cargo_poster = '/_gfx/posterframe.jpg';
7
8	var ua = navigator.userAgent;
9	$.browser.chrome = /chrome/.test(ua.toLowerCase());
10	$.browser.firefox = /firefox/.test(ua.toLowerCase());
11	$.browser.iPad = /iPad/i.test(ua);
12	$.browser.iPhone = (ua.toLowerCase().match(/iPhone/i) || ua.toLowerCase().match(/iPod/i));
13
14	// jMediaelement
15	$.webshims = $.webshims || {
16		ready: function(type, fn){
17			fn();
18		},
19		addReady: function(fn){
20			$(function(){
21				fn(document, $([]));
22			});
23		}
24	};
25	var webshims = $.webshims;
26
27	if(!window.Modernizr || !('opacity' in Modernizr)){
28		$('html').addClass($.support.opacity ? 'opacity' : 'no-opacity');
29	}
30
31	if ( ! $.fn.callProp) {
32		$.fn.callProp = function(prop, args) {
33			var ret;
34			if ( ! args)
35				args = [];
36
37			this.each(function() {
38				var fn = $.jme.attr(this, prop);
39
40				if ($.isFunction(fn)) {
41					ret = fn.apply(this, args);
42					if (ret !== undefined)
43						return false;
44				}
45			});
46
47			return (ret !== undefined) ? ret : this;
48		};
49	}
50
51	var props = {};
52	var fns = {};
53
54	$.jme = {
55		version: '2.0.0',
56		classNS: '',
57		options: {},
58		plugins: {},
59		data: function(elem, name, value){
60			var data = $(elem).data(ns+'jme') || $.data(elem, ns+'jme', {});
61			if(value === undefined){
62				return (name) ? data[name] : data;
63			} else {
64				data[name] = value;
65			}
66		},
67		registerPlugin: function(name, plugin){
68			this.plugins[name] = plugin;
69			if(!plugin.nodeName){
70				plugin.nodeName = '';
71			}
72			if(!plugin.className){
73				plugin.className = name;
74			}
75		},
76		defineMethod: function(name, fn){
77			fns[name] = fn;
78		},
79		defineProp: function(name, desc){
80			if(!desc.set){
81				if(desc.readonly){
82					desc.set = function(){
83						throw(name +' is readonly');
84					};
85				} else {
86					desc.set = $.noop;
87				}
88			}
89			if(!desc.get){
90				desc.get = function(elem){
91					return $.jme.data(elem, name);
92				};
93			}
94			props[name] = desc;
95		},
96		// Backported from jQuery 1.7.1
97		access: function( elems, key, value, exec, fn, pass ) {
98			var length = elems.length;
99
100			// Setting many attributes
101			if ( typeof key === "object" ) {
102				for ( var k in key ) {
103					$.jme.access( elems, k, key[k], exec, fn, value );
104				}
105				return elems;
106			}
107
108			// Setting one attribute
109			if ( value !== undefined ) {
110				// Optionally, function values get executed if exec is true
111				exec = !pass && exec && $.isFunction(value);
112	
113				for ( var i = 0; i < length; i++ ) {
114					fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
115				}
116
117				return elems;
118			}
119	
120			// Getting an attribute
121			return length ? fn( elems[0], key ) : undefined;
122		},
123		// Backported from jQuery 1.7.1, a modified and simplified $.prop()
124		elementValue: function(element, name, value) {
125			// don't get/set properties on text, comment and attribute nodes
126			if ( ! element || element.nodeType === 3 || element.nodeType === 8 || element.nodeType === 2)
127				return;
128
129			if (value !== undefined)
130				return (element[name] = value);
131
132			return element[name];
133		},
134		attr: (typeof $.prop === 'undefined') ? $.attr : $.prop,
135		prop: function(elem, name, value) {
136			if ( ! props[name])
137				return $.jme.attr(elem, name, value);
138
139			if (value === undefined) {
140				return props[name].get(elem);
141			} else {
142				var setValue = props[name].set(elem, value);
143				if (setValue === undefined)
144					setValue = value;
145
146				if (setValue != 'noDataSet')
147					$.jme.data(elem, name, setValue);
148			}
149		},
150		setText: function(name, text) {
151			var obj = name;
152			if (name && text) {
153				obj = {};
154				obj[name] = text;
155			}
156			$.each(obj, function(name, text) {
157				if ($.jme.plugins[name]) {
158					$.jme.plugins[name].text = text;
159				}
160			});
161		}
162	};
163
164	// Backported from jQuery 1.7.1, a modified $.fn.prop() plugin
165	$.fn.jmeValue = function(name, value) {
166		return $.jme.access(this, name, value, true, $.jme.elementValue);
167	};
168
169	$.fn.jmeProp = function(name, value) {
170		return $.jme.access(this, name, value, true, $.jme.prop);
171	};
172
173	$.fn.jmeFn = function(fn) {
174		var args = Array.prototype.slice.call( arguments, 1 );
175		var ret;
176		this.each(function(){
177			ret = fns[fn].apply(this, args);
178			if(ret !== undefined){
179				return false;
180			}
181		});
182		return (ret !== undefined) ? ret : this;
183	};
184
185	var baseSelector;
186	var pluginSelectors = [];
187	var ns = '';
188	var createSelectors = function(){
189		if(baseSelector){return;}
190		ns = $.jme.classNS;
191		baseSelector = 'div.' + ns + 'mediaplayer';
192		$.each($.jme.plugins, function(name, plugin){
193			plugin.className = ns + plugin.className;
194			plugin.selector = plugin.nodeName + '.' + plugin.className;
195			pluginSelectors.push(plugin.selector);
196			if(ns && plugin.pseudoClasses){
197				$.each(plugin.pseudoClasses, function(stateName, stateValue){
198					plugin.pseudoClasses[stateName] = ns+stateValue;
199				});
200			}
201
202		});
203		pluginSelectors = pluginSelectors.join(',');
204	};
205	var initJME = function(context, insertedElement){
206		$('video, audio', context)
207			.add(insertedElement.filter('video, audio'))
208			.filter('[data-muted]')
209			.each(function(){
210				this.jmeValue('muted', true);
211			})
212			.end()
213			.filter('[data-volume]')
214			.each(function(){
215				var defaultVolume = $(this).data('volume');
216				if(defaultVolume <= 1 && defaultVolume >= 0){
217					this.jmeValue('volume', defaultVolume);
218				}
219			})
220		;
221
222		$(baseSelector, context).add(insertedElement.filter(baseSelector)).jmePlayer();
223	};
224
225	var idlStates = {
226		ended: 1,
227		emptied: 1
228	};
229
230	$.jme.getDOMList = function(attr){
231		var list = [];
232		if(!attr){
233			attr = [];
234		}
235		if(typeof attr == 'string'){
236			attr = attr.split(' ');
237		}
238		$.each(attr, function(i, id){
239			if(id){
240				id = document.getElementById(id);
241				if(id){
242					list.push(id);
243				}
244			}
245		});
246		return list;
247	};
248
249	$.jme.getButtonText = function(button, classes){
250		var btnTextElem = $('span.jme-text', button);
251		if( ! btnTextElem[0])
252			btnTextElem = button;
253
254		var txt = btnTextElem.text().split('/');
255		var title_attr = $.attr(button, 'title');
256		var title = (title_attr === undefined) ? [] : title_attr.split('/');
257
258		var doText;
259		var doTitle;
260		var lastState;
261		if(txt.length == 2){
262			txt[0] = txt[0].trim();
263			txt[1] = txt[1].trim();
264			doText = true;
265		}
266		if(title.length == 2){
267			title[0] = title[0].trim();
268			title[1] = title[1].trim();
269			doTitle = true;
270		}
271		return function(state){
272			if(lastState === state){return;}
273			lastState = state;
274			if(doText){
275				btnTextElem.text(txt[state || 0]);
276			}
277			if(doTitle){
278				button.attr('title', txt[state || 0]);
279			}
280			if(classes){
281				button
282					.removeClass(classes[(state) ? 0 : 1])
283					.addClass(classes[state])
284				;
285			}
286		};
287	};
288
289	$.jme.timedClass = {
290		add: function(elem, className, max){
291			$.jme.timedClass.remove(elem, className);
292			elem = $(elem);
293			if(!max){
294				max = 20;
295			}
296			var data = $.data(elem[0], 'timedClass'+className, {
297				i: 0,
298				timer: setInterval(function(){
299					elem.removeClass(className+'-'+ data.i);
300					data.i++;
301					if(data.i > max){
302						data.i = 0;
303					}
304					elem.addClass(className+'-'+ data.i);
305				}, 25)
306			});
307		},
308		remove: function(elem, className){
309			elem = $(elem);
310			var data = $.data(elem[0], 'timedClass'+className);
311			if(data){
312				elem.removeClass(className+'-'+ data.i);
313				clearInterval(data.timer);
314				$.removeData(elem[0], 'timedClass'+className);
315			}
316		}
317	};
318
319	$.fn.jmeInit = function() {
320		return this.each(function() {
321			initJME($(this).parent(), $([]));
322		});
323	};
324
325	$.fn.jmePlayer = function(opts) {
326		var removeClasses = ['idle', 'pause', 'playing', 'ended', 'waiting', 'mediaerror'].map(function(state) {
327			return ns + 'state-' + state;
328		}).join(' ');
329		return this.each(function() {
330			if (opts)
331				$.jme.data(this, $.extend(true, {}, opts));
332
333			var media = $('audio, video', this).filter(':first');
334			var base = $(this);
335			var jmeData = $.jme.data(this);
336			var mediaData = $.jme.data(media[0]);
337			mediaData.player = base;
338			mediaData.media = media;
339
340			// For iOS, add corresponding CSS rules, and simply add the poster frame
341			if ($.browser.iPhone || $.browser.iPad) {
342				$('body').addClass('ios iphone');
343				media.jmeFn('videoIsSupported');
344				media.jmeFn('posterize');
345
346				return;
347			}
348
349			var insideControls = $(pluginSelectors, base);
350			var externalControls = $.jme.getDOMList($.jme.data(this, 'controls'));
351			var controls = insideControls.add($(externalControls));
352			var init;
353
354			if (!jmeData.media) {
355				init = true;
356				jmeData.media = media;
357				jmeData.player = base;
358
359				w = media.width();
360				h = media.height();
361				base.css({'width': w + 'px', 'height': h + 'px'});
362
363				var video_poster = media.jmeFn('posterize');
364				var poster = $(media).after('<div class="poster" style="background-image: url(' + video_poster + ')" />');
365				mediaData.poster = poster;
366
367				$('.poster', base).click(function() {
368					$('div.jme-default-media-overlay', base).trigger('click');
369				}).hover(function() {
370					// Mimic :hover behavior for initial play button
371					$('div.jme-default-media-overlay', base).toggleClass('postered');
372				});
373
374				media.click(function() {
375					$(this).jmeFn('togglePlay');
376				});
377				media.dblclick(function() {
378					base.jmeProp('fullscreen', !base.hasClass($.jme.classNS + 'player-fullscreen'));
379				});
380
381				media.jmeFn('videoIsSupported');
382
383				// Force Firefox to fall back
384				if ($.browser.firefox)
385					media.supportvid = false;
386
387				media
388					.bind('waiting', function(e) {
389						var has_played = $.attr(this, 'data-has-played');
390						if (has_played === undefined)
391							base.removeClass(ns + 'state-cycled');
392					})
393					.bind('playing', function(e) {
394						$.attr(this, 'data-has-played', true);
395						if ( ! base.hasClass(ns + 'state-cycled'))
396							base.addClass(ns + 'state-cycled');
397					})
398					.bind('emptied waiting playing ended pause updateJMEState mediaerror', function(e){
399						var state = e.type;
400						var readyState;
401						var paused;
402
403						if (state == 'updateJMEState') {
404							if ($.jme.attr(this, 'ended')) {
405								state = 'ended';
406							} else {
407								readyState = $.jme.attr(this, 'readyState');
408								paused = $.jme.attr(this, 'paused');
409								if ( ! paused && readyState < 3) {
410									state = 'waiting';
411								} else if ( ! paused && readyState > 2) {
412									state = 'playing';
413								} else {
414									state = 'idle';
415								}
416							}
417						}
418
419						if (idlStates[state]) {
420							state = 'idle';
421							base.removeClass(ns + 'state-cycled');
422							if (base.jmeProp('fullscreen'))
423								base.jmeProp('fullscreen', false);
424						}
425						// Disabled timer classing due to Safari-hate
426						/*
427						if(state == 'waiting'){
428							$.jme.timedClass.add(base, ns +'state-'+ state);
429						} else {
430							$.jme.timedClass.remove(base, ns +'state-'+ state);
431						}
432						// */
433
434						base.removeClass(removeClasses).addClass(ns + 'state-' + state);
435					})
436					.bind('volumechange updateJMEState', function(){
437						base[media.jmeValue('muted') ? 'addClass' : 'removeClass'](ns + 'state-muted');
438					})
439					.bind('emptied updateJMEState play playing waiting', function(e) {
440						var action = null;
441						if(e.type == 'emptied'){
442							action = 'addClass';
443						} else if(e.type == 'play' || e.type == 'waiting' || e.type == 'playing'){
444							action = 'removeClass';
445						} else if($.jme.attr(this, 'paused')){
446							action = 'addClass';
447						}
448
449						if (action !== null)
450							base[action](ns + 'state-initial');
451					})
452					.triggerHandler('updateJMEState')
453				;
454
455				var focusenterTimer;
456				var foverTimer;
457				base
458					.bind('useractive', function(){
459						base.addClass(ns + 'useractive');
460						base.removeClass(ns + 'cursor-hidden');
461					})
462					.bind('userinactive', {idletime: 2500}, function() {
463						base.removeClass(ns + 'useractive');
464						if (!base.hasClass(ns + 'state-pause'))
465							base.addClass(ns + 'cursor-hidden');
466					})
467					.bind('focusin', function(){
468						clearTimeout(focusenterTimer);
469						base.addClass(ns+'focusenter');
470					})
471					.bind('focusout', function(){
472						clearTimeout(focusenterTimer);
473						focusenterTimer = setTimeout(function() {
474							base.removeClass(ns+'focusenter');
475						}, 1);
476					})
477
478					.bind('mouseenter focusin', function() {
479						clearTimeout(foverTimer);
480						base.addClass(ns + 'fover');
481						base.addClass(ns + 'useractive');
482					})
483					.bind('mouseleave focusout', function() {
484						clearTimeout(foverTimer);
485						foverTimer = setTimeout(function() {
486							base.removeClass(ns + 'fover');
487							base.removeClass(ns + 'useractive');
488						}, 1);
489					})
490				;
491			}
492
493			base.jmeFn('addControls', controls);
494			if (init && jmeData.controlbar) {
495				base.jmeProp('controlbar', true);
496			}
497		});
498	};
499
500
501/**
502 * Determine if the browser supports the video tag, can play the type of video supplied
503 *
504 * @return	Boolean, whether supported
505 */
506	$.jme.defineMethod('videoIsSupported', function() {
507		var data = $.jme.data(this) || {};
508		if ( ! data.media)
509			return false;
510
511		var detect = document.createElement('video') || false;
512		if ( ! detect)
513			return false;
514
515		// Judging based on first source, as only one is expected for now
516		// @TODO: Loop through sources for possible multiple source support
517		var file = data.media.jmeProp('srces')[0].src;
518		var filetype = file.substring(file.lastIndexOf('.') + 1);
519		var mimeTypes = {
520			'video/ogg': ['ogg', 'ogv', 'ogm'],
521			'video/mpeg': ['mpg', 'mpeg','mpe'],
522			'video/mp4': ['mp4', 'mpg4', 'm4v'],
523			'video/quicktime': ['mov', 'qt'],
524			'video/x-msvideo': ['avi'],
525			'video/x-ms-asf': ['asf', 'asx'],
526			'video/3gpp': ['3gp', '3gpp'],
527			'video/webm': ['webm']
528		};
529		
530		$.each(mimeTypes, function(name, arr) {
531			$.each(arr, function(val) {
532				keyname = arr[val];
533				if (filetype == keyname) {
534					data.media.supportvid = (detect.canPlayType(name) === "maybe" || detect.canPlayType(name) === "probably");
535					return data.media.supportvid;
536				}
537			});
538		});
539
540		return false;
541	});
542
543
544/**
545 * Add default poster frame should one not be specified.
546 *
547 * @return	The media element's poster attribute for the current jme instance
548 */
549	$.jme.defineMethod('posterize', function() {
550		var data = $.jme.data(this) || {};
551		if ( ! data.media)
552			return null;
553
554		var poster = data.media.attr('poster');
555		if (poster === undefined) {
556			poster = cargo_poster;
557			data.media.attr('poster', cargo_poster);
558		}
559
560		return poster;
561	});
562
563	$.jme.defineProp('isPlaying', {
564		get: function(elem) {
565			return (!$.jme.attr(elem, 'ended') && !$.jme.attr(elem, 'paused') && $.jme.attr(elem, 'readyState') > 1 && !$.data(elem, 'mediaerror'));
566		},
567		readonly: true
568	});
569
570	$.jme.defineProp('player', {
571		get: function(elem){
572			return $.jme.data(this, 'player') || null;
573		},
574		readonly: true
575	});
576
577	$.jme.defineProp('media', {
578		get: function(elem){
579			return $.jme.data(this, 'media') || null;
580		},
581		readonly: true
582	});
583
584	$.jme.defineProp('srces', {
585		get: function(elem) {
586			var data = $.jme.data(elem);
587			var src = $(data.media).attr('src');
588			var srces = [];
589
590			if (src !== undefined)
591				return [{src: src}];
592
593			srces = $.map($('source', data.media).get(), function(source) {
594				var src = {src: $.attr(source, 'src')};
595				var tmp = $.attr(source, 'media');
596				if (tmp !== undefined)
597					src.media = tmp;
598
599				tmp = $.attr(source, 'type');
600				if (tmp !== undefined)
601					src.type = tmp;
602
603				return src;
604			});
605
606			return srces;
607		},
608		set: function(elem, srces){
609			var data = $.jme.data(elem);
610
611			var setSrc = function(i, src){
612				if(typeof src == 'string'){
613					src = {src: src};
614				}
615				$(document.createElement('source')).attr(src).appendTo(data.media);
616
617			};
618			data.media.removeAttr('src').find('source').remove();
619			if($.isArray(srces)){
620				$.each(srces, setSrc);
621			} else {
622				setSrc(0, srces);
623			}
624			data.media.callProp('load');
625			return 'noDataSet';
626		}
627	});
628
629	$.jme.defineMethod('togglePlay', function() {
630		var is_playing = props.isPlaying.get(this);
631		var action = (is_playing) ? 'pause' : 'play';
632
633		// Sometimes needs a nudge after play has ended to remove the idle state before replaying
634		if ( ! is_playing) {
635			var base = $.jme.data(this, 'player');
636			if (base.hasClass($.jme.classNS + 'state-idle') && ! base.hasClass($.jme.classNS + 'state-initial')) {
637				base.removeClass(ns + 'state-cycled');
638				$(this).callProp('pause');
639				base.triggerHandler('mouseenter');
640			}
641		}
642
643		$(this).callProp(action);
644	});
645
646	$.jme.defineProp('controlbar', {
647		set: function(elem, value) {
648			// Exit when a control bar is not used, i.e. on iOS
649			if ($.browser.iPhone || $.browser.iPad)
650				return;
651
652			value = !!value;
653			var data = $.jme.data(elem);
654			var controlBar = $('div.jme-default-media-overlay, div.jme-default-control-bar', data.player);
655			var mediaControls = $.jme.plugins["media-controls"] ;
656			var structure = '';
657			var controls;
658
659			if(value && !controlBar[0] && data.media.supportvid){
660				if(data._controlbar){
661					data._controlbar.appendTo(data.player);
662				} else {
663					// Remove default browser video controls
664					data.media.jmeValue('controls', false);
665
666					$.each(mediaControls.pluginOrder, function(i, name) {
667						var plugin = $.jme.plugins[name];
668						if(plugin && plugin.structure){
669							structure += plugin.structure.replace('{%class%}', ns+name).replace('{%text%}', plugin.text || '');
670						} else if(name && name.indexOf('<') != -1){
671							structure += name;
672						}
673					});
674					data._controlbar = $( mediaControls.barStructure );
675					controlBar = data._controlbar.find('div.jme-cb-box').addClass(ns+'media-controls');
676					controls = data._controlbar.filter('div.jme-default-media-overlay').addClass(ns+'play-pause');
677					controls =  controls.add(  controlBar );
678					controls = controls.add( $(structure).appendTo(controlBar) );
679					data._controlbar.appendTo(data.player);
680					data.player.jmeFn('addControls', controls);
681				}
682
683			// for mediaerror fallback
684			} else if(value && !controlBar[0] && !data.media.supportvid && !data.media.hidden){
685				$(elem).addClass('mediaerror');
686				if($.browser.iPad || $.browser.iPhone) {
687					var fallback = data.media.hide();
688					var iosmsg = $(iosmessage).insertAfter(data.media).addClass('ios-message');
689
690				} else {
691					var fallback = data.media.hide().children(':not(source, track)').clone().insertAfter(data.media);
692				}
693				data.media.hidden = true;
694			} else if(!value) {
695				controlBar.detach();
696			}
697			controlBar = null;
698			controls = null;
699			return value;
700		}
701	});
702
703	$.jme.defineMethod('addControls', function(controls) {
704		var data = $.jme.data(this) || {};
705		if ( ! data.media)
706			return;
707
708		controls = $(controls);
709		$.each($.jme.plugins, function(name, plugin){
710			controls
711				.filter('.'+plugin.className)
712				.add(controls.find('.'+plugin.className))
713				.each(function(){
714					var control = $(this);
715					var options = $.jme.data(this);
716					options.player = data.player;
717					options.media = data.media;
718					if(options.rendered){return;}
719					options.rendered = true;
720					if(plugin.options){
721						$.each(plugin.options, function(option, value){
722							if(!(option in options)){
723								options[option] = value;
724							}
725						});
726					}
727					plugin._create(control, data.media, data.player, options);
728					control = null;
729				})
730			;
731		});
732
733		data.player.triggerHandler('controlsadded');
734	});
735
736	$.jme.defineMethod('updateControlbar', function(){
737		var timeSlider = $('.' + $.jme.classNS + 'time-slider', this);
738		if (timeSlider[0] && timeSlider.css('position') !== 'absolute') {
739			var width = timeSlider.parent().width();
740			var elemWidths = 0;
741			timeSlider.siblings()
742				.each(function(){
743					if(this !== timeSlider[0] && $.css(this, 'position') !== 'absolute'){
744						elemWidths += Math.floor($(this).outerWidth(true));
745					}
746				})
747			;
748			timeSlider.width(width - elemWidths - (timeSlider.outerWidth(true) - timeSlider.width()) - 0.5);
749		}
750	});
751
752	$.jme.registerPlugin('media-controls', {
753		pluginOrder: ['play-pause', '<div class="media-bar"><div class="vertical_border first"></div>', '<div class="time">', 'currenttime-display', '<span> / </span>', 'duration-display','</div>', 'time-slider', '<div class="vertical_border second"></div><div class="volume-controls">', 'mute-unmute', 'volume-slider', '</div><div class="vertical_border third"></div>', 'fullscreen', '</div>'],
754		barStructure: '<div class="jme-default-media-overlay"></div><div class="jme-default-control-bar"><div class="jme-cb-box"></div></div>',
755		_create: function(control, media, base, options) {
756			// No longer in use - pure CSS layout now works, no reliance on javascript resizing
757			/*
758			var update = function() {
759				control.jmeFn('updateControlbar');
760			};
761			media.bind('loadedmetadata volumechange play pause ended emptied', update);
762			base.bind('updatetimeformat controlsadded playerdimensionchange', update);
763			update();
764			// @TODO Timed business may be hated by Safari, investigate why
765			var timer;
766			var update = function(){
767				clearTimeout(timer);
768				control.jmeFn('updateControlbar');
769				timer = setTimeout(function(){
770					control.jmeFn('updateControlbar');
771				}, 9);
772			};
773			setTimeout(function(){
774				media.bind('loadedmetadata volumechange play pause ended emptied', update);
775				base.bind('updatetimeformat controlsadded playerdimensionchange', update);
776			}, 1);
777			update();
778			// */
779		}
780	});
781
782
783	(function(){
784		var activity = {
785			add: function(elem, cfg, name){
786				var data 		= $.data(elem, 'jmeuseractivity') || $.data(elem, 'jmeuseractivity', {idletime: 1500, idle: true, trigger: {}}),
787					jElm 		= $(elem),
788					setInactive = function(){
789						if(!data.idle){
790							data.idle = true;
791							if ( data.trigger.userinactive ) {
792								jElm.trigger('userinactive');
793							}
794						}
795					},
796					x, y,
797					setActive 	= function(e){
798						if(!e || (e.type === 'mousemove' && e.pageX === x && e.pageY === y)){return;}
799						if(e.type === 'mousemove'){
800							 x = e.pageX;
801							 y = e.pageY;
802						}
803						if(data.idleTimer){
804							clearTimeout(data.idleTimer);
805						}
806						data.idleTimer = setTimeout(setInactive, data.idletime);
807						if(data.idle){
808							data.idle = false;
809							if( data.trigger.useractive ){
810								jElm.trigger('useractive');
811							}
812						}
813					}
814				;
815
816				data.idletime = (cfg || {}).idletime || data.idletime;
817				if(cfg && 'idle' in cfg){
818					data.idle = cfg.idle;
819				}
820				data.trigger[name] = true;
821
822				if( !data.bound ){
823					jElm
824						.bind('mouseleave.jmeuseractivity', setInactive)
825						.bind('mousemove.jmeuseractivity focusin.jmeuseractivity mouseenter.jmeuseractivity keydown.jmeuseractivity keyup.jmeuseractivity mousedown.jmeuseractivity', setActive)
826					;
827					data.bound = true;
828				}
829				if(!data.idle){
830					setActive({type: 'initunidled'});
831				}
832			},
833			remove: function(elem, name){
834				var data = $.data(elem, 'jmeuseractivity') || $.data(elem, 'jmeuseractivity', {idletime: 1500, idle: true, trigger: {}});
835				data.trigger[name] = false;
836				if(!data.trigger.useractive && !data.trigger.userinactive){
837					$(elem).unbind('.jmeuseractivity');
838					data.bound = false;
839				}
840			}
841		};
842		$.each(['useractive', 'userinactive'], function(i, name){
843			$.event.special[name] = {
844				setup: function(cfg){
845					activity.add(this, cfg, name);
846				},
847				teardown: function(){
848					activity.remove(this, name);
849				}
850			};
851		});
852	})();
853
854
855	$(function() {
856		createSelectors();
857		// No inserting JME into every video element in the document on ready
858		// Replaced with new plugin function jmeInit()
859		// initJME(document, $([]));
860	});
861
862
863	webshims.ready('mediaelement', function(){
864		webshims.addReady(function(context, insertedElement){
865			if(context !== document){
866				initJME(context, insertedElement);
867			}
868		});
869	});
870
871})(jQuery);
872
873
874(function($) {
875	var pseudoClasses = 'pseudoClasses';
876
877	var playStates = {
878		play: 1,
879		playing: 1
880	};
881
882	var loadJqueryUI = function(){
883		if($.webshims.loader){
884			$.webshims.loader.loadList(['jquery-ui']);
885			if($.webshims.modules['input-widgets'].src){
886				$.webshims.loader.loadList(['input-widgets']);
887			}
888		}
889	};
890	var btnStructure = '<button class="{%class%}"><span class="jme-icon"></span><span class="jme-text">{%text%}</span></button>';
891	var defaultStructure = '<div class="{%class%}"></div>';
892	$.jme.registerPlugin('play-pause', {
893		pseudoClasses: {
894			play: 'state-paused',
895			pause: 'state-playing'
896		},
897		structure: btnStructure,
898		text: 'play / pause',
899		_create: function(control, media, base){
900			var textFn = $.jme.getButtonText(control, [this[pseudoClasses].play, this[pseudoClasses].pause]);
901			media
902				.bind('play playing ended pause updateJMEState', function(e){
903					var state = e.type;
904					if(state == 'updateJMEState'){
905						state = (media.jmeProp('isPlaying') )? 1 : 0;
906					} else if(playStates[state]){
907						state = 1;
908					} else {
909						state = 0;
910					}
911					textFn(state);
912				})
913				.triggerHandler('updateJMEState')
914			;
915			control.bind('click', function() {
916				media.jmeFn('togglePlay');
917				return false;
918			});
919		}
920	});
921
922	$.jme.registerPlugin('mute-unmute', {
923		pseudoClasses: {
924			mute: 'state-mute',
925			unmute: 'state-unmute'
926		},
927		structure: btnStructure,
928		text: 'mute / unmute',
929		_create: function(control, media, base){
930			var textFn = $.jme.getButtonText(control, [this[pseudoClasses].mute, this[pseudoClasses].unmute]);
931
932			media.bind('volumechange updateJMEState', function(e) {
933				var is_muted = (media.jmeValue('muted') === true) ? 1 : 0;
934				textFn(is_muted);
935			}).triggerHandler('updateJMEState');
936
937			control.bind('click', function() {
938				// this.removeClass();
939				var mute_switch = (media.jmeValue('muted') === true) ? false : true;
940				media.jmeValue('muted', mute_switch);
941				return false;
942			});
943		}
944	});
945
946	var onSliderReady = function(fn){
947		var complete = function(){
948			if(!$.fn._uiSlider){
949				if(!$.mobile || !$.mobile.slider){
950					$.fn._uiSlider = $.fn.slider;
951				} else {
952					$.widget('jme._uiSlider', $.ui.slider.prototype);
953				}
954			}
955			fn();
956		};
957		$.webshims.ready('jquery-ui', function(){
958			if(!$.ui || !$.ui.slider){
959				$.webshims.ready('input-widgets', complete);
960			} else {
961				complete();
962			}
963		});
964	};
965
966	function createGetSetHandler(get, set){
967		var throttleTimer;
968		var blockedTimer;
969		var blocked;
970		return {
971			get: function(){
972				if(blocked){return;}
973				return get.apply(this, arguments);
974			},
975			set: function(){
976				clearTimeout(throttleTimer);
977				clearTimeout(blockedTimer);
978
979				var that = this;
980				var args = arguments;
981				blocked = true;
982				throttleTimer = setTimeout(function(){
983					set.apply(that, args);
984					blockedTimer = setTimeout(function(){
985						blocked = false;
986					}, 30);
987				}, 0);
988			}
989		};
990	}
991
992	$.jme.registerPlugin('volume-slider', {
993		structure: defaultStructure,
994		_create: function(control, media, base){
995			var volume = createGetSetHandler(
996				function() {
997					var volume = media.jmeValue('volume');
998					if (volume !== undefined)
999						control._uiSlider('value', volume);
1000				},
1001				function(value) {
1002					media.jmeValue({
1003						'volume': value,
1004						'muted': false
1005					});
1006				}
1007			);
1008			var createFn = function(){
1009				control._uiSlider({
1010					range: 'min',
1011					max: 1,
1012					step: 0.05,
1013					value: media.jmeValue('volume'),
1014					slide: function(e, data) {
1015						if (e.originalEvent)
1016							volume.set(data.value);
1017					}
1018				});
1019				media.bind('volumechange', volume.get);
1020			};
1021
1022			onSliderReady(createFn);
1023		}
1024	});
1025
1026	$.jme.registerPlugin('time-slider', {
1027		structure: defaultStructure,
1028		options: {
1029			format: ['mm', 'ss']
1030		},
1031		_create: function(control, media, base){
1032			var time = createGetSetHandler(
1033				function(){
1034					var time = media.jmeValue('currentTime');
1035					if ( ! isNaN(time))
1036						control._uiSlider('value', time);
1037				},
1038				function(value) {
1039					try { media.jmeValue('currentTime', value); } catch(error){}
1040				}
1041			);
1042			var createFn = function(){
1043				var duration = media.jmeValue('duration');
1044				control._uiSlider({
1045					range: 'min',
1046					max: duration || 1,
1047					disabled: ! duration,
1048					step: 0.1,
1049					value: media.jmeValue('currentTime'),
1050					slide: function(e, data) {
1051						if (e.originalEvent)
1052							time.set(data.value);
1053					}
1054				});
1055				media.bind('timeupdate', time.get);
1056				media.bind('emptied', function() {
1057					control._uiSlider('option', 'disabled', true);
1058				});
1059				media.bind('durationchange', function() {
1060					control._uiSlider('option', 'disabled', false)
1061					control._uiSlider('option', 'max', media.jmeValue('duration'))
1062				});
1063			};
1064			base.jmeFn('addControls', $('<div class="'+ $.jme.classNS +'buffer-progress" />').prependTo(control) );
1065			onSliderReady(createFn);
1066		}
1067	});
1068
1069	$.jme.defineMethod('concerningRange', function(type, time) {
1070		var elem = this;
1071		var ret = {start: 0, end: 0};
1072		if ( ! type)
1073			type = 'buffered';
1074
1075		type = $.jme.attr(elem, type);
1076
1077		if (time == null)
1078			time = $.jme.elementValue(elem, 'currentTime');
1079
1080		if ( ! type || ! ('length' in type))
1081			return ret;
1082
1083		for (var i = 0, len = type.length; i < len; i++) {
1084			ret.start = type.start(i);
1085			ret.end = type.end(i);
1086			if (ret.start <= time && ret.end >= time)
1087				break;
1088		}
1089
1090		return ret;
1091	});
1092
1093	$.jme.defineProp('progress', {
1094		get: function(elem){
1095			var data = $.jme.data(elem);
1096			if ( ! data.media)
1097				return 0;
1098
1099			var duration = data.media.jmeValue('duration');
1100			if (isNaN(duration) || duration === undefined)
1101				return 0;
1102
1103			var progress = (data.media.jmeFn('concerningRange').end / duration) * 100;
1104			if (progress > 99.4)
1105				progress = 100;
1106
1107			return progress || 0;
1108		},
1109		readonly: true
1110	});
1111
1112	$.jme.registerPlugin('buffer-progress', {
1113		_create: function(control, media, base, options) {
1114			var indicator = $('<div class="' + $.jme.classNS + 'buffer-progress-indicator" />').appendTo(control);
1115			var drawBufferProgress = function() {
1116				var progress = media.jmeProp('progress');
1117				if (options.progress !== progress) {
1118					options.progress = progress;
1119					indicator.css('width', progress + '%');
1120				}
1121			};
1122			media.bind('progress playing', drawBufferProgress);
1123			media.bind('emptied', function() {
1124				indicator.css('width', 0);
1125				options.progress = 0;
1126			});
1127		}
1128	});
1129
1130	var times = {
1131		hh: 60000,
1132		mm: 60,
1133		ss: 1,
1134		ms: 1/1000
1135	};
1136	var formatTime = function(sec, format){
1137		var data;
1138		if(!format){
1139			format = ['mm', 'ss'];
1140		}
1141		if(sec == null){
1142			data = $.jme.data(this);
1143			sec = data.media.jmeValue('duration');
1144		}
1145		if(!sec){
1146			sec = 0;
1147		}
1148		var formated = [];
1149		var frac;
1150		for(var i = 0, len = format.length; i < len; i++){
1151			if(format[i] == 'ms' && i == len -1 ){
1152				frac = Math.round( (sec / times[format[i]]) / 10);
1153			} else {
1154				frac = parseInt(sec / times[format[i]], 10);
1155				sec = sec % times[format[i]];
1156			}
1157			if(frac < 10){
1158				frac = '0'+frac;
1159			}
1160			formated.push( frac );
1161		}
1162
1163		return formated.join(':');
1164	};
1165	$.jme.defineMethod('formatTime', formatTime);
1166
1167	$.jme.defineProp('format', {
1168		set: function(elem, format){
1169			if(!$.isArray(format)){
1170				format = format.split(':');
1171			}
1172			var data = $.jme.data(elem);
1173			data.format = format;
1174			$(elem).triggerHandler('updatetimeformat');
1175			data.player.triggerHandler('updatetimeformat');
1176			return 'noDataSet';
1177		}
1178	});
1179
1180	$.jme.registerPlugin('duration-display', {
1181		structure: defaultStructure,
1182		options: {
1183			format: "mm:ss"
1184		},
1185		_create: function(control, media, base, options){
1186			if(typeof options.format == 'string'){
1187				options.format = options.format.split(':');
1188			}
1189			var showDuration = function(){
1190				control.html(formatTime(media.jmeValue('duration'), options.format));
1191			};
1192			media.bind('durationchange emptied', showDuration);
1193
1194			control
1195				.bind('updatetimeformat', showDuration)
1196				.jmeProp('format', options.format)
1197			;
1198		}
1199	});
1200
1201	$.jme.defineProp('countdown', {
1202		set: function(elem, value){
1203
1204			var data = $.jme.data(elem);
1205			data.countdown = !!value;
1206			$(elem).triggerHandler('updatetimeformat');
1207			data.player.triggerHandler('updatetimeformat');
1208			return 'noDataSet';
1209		}
1210	});
1211
1212	$.jme.registerPlugin('currenttime-display', {
1213		structure: defaultStructure,
1214		options: {
1215			format: "mm:ss",
1216			countdown: false
1217		},
1218		_create: function(control, media, base, options){
1219			if(typeof options.format == 'string'){
1220				options.format = options.format.split(':');
1221			}
1222
1223			var showTime = function(e) {
1224				var currentTime = media.jmeValue('currentTime');
1225				if (options.countdown) {
1226					currentTime = (media.jmeValue('duration') || 0) - currentTime;
1227					if (currentTime < 0.7)
1228						currentTime = 0;
1229				}
1230				control.html(formatTime(currentTime, options.format));
1231			};
1232			media.bind('timeupdate emptied durationchange', showTime);
1233
1234			control
1235				.bind('updatetimeformat', showTime)
1236				.jmeProp('format', options.format)
1237			;
1238		}
1239	});
1240
1241	// Adapted from http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/
1242	$.jme.fullscreen = (function() {
1243		var parentData;
1244		var tmpData;
1245		var fullScreenApi = {
1246			supportsFullScreen: false,
1247			isFullScreen: function() { return false; },
1248			requestFullScreen: function(elem){
1249				parentData = [];
1250				$(elem).parentsUntil('body').each(function(){
1251					var pos =  $.curCSS(this, 'position');
1252					var left = this.scrollLeft;
1253					var top = this.scrollTop;
1254					var changed;
1255					tmpData = {elemStyle: this.style, elem: this};
1256					if(pos !== 'static'){
1257						changed = true;
1258						tmpData.pos = tmpData.elemStyle.position;
1259						this.style.position = 'static';
1260					}
1261					if(left){
1262						changed = true;
1263						tmpData.left = left;
1264					}
1265					if(top){
1266						changed = true;
1267						tmpData.top = top;
1268					}
1269					if(changed){
1270						parentData.push(tmpData);
1271					}
1272				});
1273				tmpData = null;
1274			},
1275			cancelFullScreen: function(){
1276				if(parentData){
1277					$.each(parentData, function(i, data){
1278						if('pos' in data){
1279							data.elemStyle.position = data.pos;
1280						}
1281						if(data.left){
1282							data.elem.scrollLeft = data.left;
1283						}
1284						if(data.top){
1285							data.elem.scrollTop = data.top;
1286						}
1287						data = null;
1288					});
1289				}
1290			},
1291			eventName: 'fullscreenchange',
1292			prefix: ''
1293		},
1294		browserPrefixes = 'webkit moz o ms khtml'.split(' ');
1295
1296		// check for native support
1297		if (typeof document.cancelFullScreen != 'undefined') {
1298			fullScreenApi.supportsFullScreen = true;
1299		} else {
1300			// check for fullscreen support by vendor prefix
1301			for (var i = 0, il = browserPrefixes.length; i < il; i++ ) {
1302				fullScreenApi.prefix = browserPrefixes[i];
1303				if (typeof document[fullScreenApi.prefix + 'CancelFullScreen' ] != 'undefined' ) {
1304					fullScreenApi.supportsFullScreen = true;
1305					break;
1306				}
1307			}
1308		}
1309
1310		// update methods to do something useful
1311		if (fullScreenApi.supportsFullScreen) {
1312			fullScreenApi.eventName = fullScreenApi.prefix + 'fullscreenchange';
1313
1314			fullScreenApi.isFullScreen = function() {
1315				switch (fullScreenApi.prefix) {
1316					case '':
1317					return document.fullScreen;
1318					case 'webkit':
1319					return document.webkitIsFullScreen;
1320					default:
1321					return document[fullScreenApi.prefix + 'FullScreen'];
1322				}
1323			};
1324			fullScreenApi.requestFullScreen = function(el) {
1325				return (fullScreenApi.prefix === '') ? el.requestFullScreen() : el[this.prefix + 'RequestFullScreen']();
1326			};
1327			fullScreenApi.cancelFullScreen = function(el) {
1328				return (fullScreenApi.prefix === '') ? document.cancelFullScreen() : document[this.prefix + 'CancelFullScreen']();
1329			};
1330		}
1331
1332		return fullScreenApi;
1333	})();
1334
1335	$.jme.defineProp('fullscreen', {
1336		set: function(elem, value){
1337			var data = $.jme.data(elem);
1338			value = !!value;
1339			if(!data || !data.player){return 'noDataSet';}
1340			if(value){
1341				if(data.player.hasClass($.jme.classNS+'player-fullscreen')){return 'noDataSet';}
1342
1343				data.scrollPos = {
1344					top: $(window).scrollTop(),
1345					left: $(window).scrollLeft()
1346				};
1347
1348				$(document)
1349					.unbind('.jmefullscreen')
1350					.bind('keydown.jmefullscreen', function(e){
1351						if(e.keyCode == 27){
1352							data.player.jmeProp('fullscreen', false);
1353							return false;
1354						}
1355					})
1356				;
1357
1358				try {
1359					$.jme.fullscreen.requestFullScreen(data.player[0]);
1360				} catch(er){}
1361
1362
1363				$('html').addClass($.jme.classNS+'has-media-fullscreen');
1364
1365				data.player.addClass($.jme.classNS+'player-fullscreen');
1366
1367				if($.browser.chrome) {
1368
1369					$('.jme-default-control-bar').delay(100).css('position','absolute');
1370
1371				}
1372				data.media.addClass($.jme.classNS+'media-fullscreen');
1373
1374				if($.jme.fullscreen.supportsFullScreen){
1375					$(document)
1376						.bind($.jme.fullscreen.eventName+'.jmefullscreen', function(e){
1377							var isFullscreen = $.jme.fullscreen.isFullScreen();
1378							if(isFullscreen && elem == e.target){
1379								$(elem).triggerHandler('playerdimensionchange', ['fullscreen']);
1380							} else if(!isFullscreen) {
1381								data.player.jmeProp('fullscreen', false);
1382							}
1383						})
1384					;
1385
1386				}
1387				data.player.triggerHandler('playerdimensionchange', ['fullwindow']);
1388				data.media.callProp('play');
1389			} else {
1390				if(!data.player.hasClass($.jme.classNS+'player-fullscreen')){return 'noDataSet';}
1391				$(document).unbind('.jmefullscreen');
1392				$('html').removeClass($.jme.classNS+'has-media-fullscreen');
1393				data.player.removeClass($.jme.classNS+'player-fullscreen');
1394				data.media.removeClass($.jme.classNS+'media-fullscreen');
1395
1396				try {
1397					$.jme.fullscreen.cancelFullScreen();
1398				} catch(er){}
1399
1400
1401				data.player.triggerHandler('playerdimensionchange');
1402				if(data.scrollPos){
1403					$(window).scrollTop(data.scrollPos.top);
1404					$(window).scrollLeft(data.scrollPos.left);
1405					delete data.scrollPos;
1406				}
1407			}
1408			return 'noDataSet';
1409		},
1410		get: function(elem){
1411			var data = $.jme.data(elem);
1412			if(!data || !data.player){return;}
1413			var fs = data.player.hasClass($.jme.classNS+'player-fullscreen');
1414			if(!fs){return false;}
1415			return $.jme.fullscreen.isFullScreen() || 'fullwindow';
1416		}
1417	});
1418
1419	$.jme.registerPlugin('fullscreen', {
1420		pseudoClasses: {
1421			enter: 'state-enterfullscreen',
1422			exit: 'state-exitfullscreen'
1423		},
1424		structure: btnStructure,
1425		text: 'enter fullscreen / exit fullscreen',
1426		_create: function(control, media, base){
1427			var textFn = $.jme.getButtonText(control, [this[pseudoClasses].enter, this[pseudoClasses].exit]);
1428			var updateControl = function(){
1429				textFn(base.hasClass($.jme.classNS + 'player-fullscreen') ? 1 : 0);
1430			};
1431			base.bind('playerdimensionchange', updateControl);
1432			control.bind('click', function(){
1433				base.jmeProp('fullscreen', !base.hasClass($.jme.classNS + 'player-fullscreen'));
1434				return false;
1435			});
1436			updateControl();
1437		}
1438	});
1439
1440})(jQuery);

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.