/*!
- MediaElement.js
- HTML5 <video> and <audio> shim and player
- http://mediaelementjs.com/
- Creates a JavaScript object that mimics HTML5 MediaElement API
- for browsers that don't understand HTML5 or can't play the provided codec
- Can play MP4 (H.264), Ogg, WebM, FLV, WMV, WMA, ACC, and MP3
- Copyright 2010-2013, John Dyer (http://j.hn)
- License: MIT
- /
// Namespace var mejs = mejs || {};
// version number mejs.version = '2.13.1';
// player number (for missing, same id attr)
mejs.meIndex = 0;
// media types accepted by plugins mejs.plugins = { silverlight: [ {version: [3,0], types: ['video/mp4','video/m4v','video/mov','video/wmv','audio/wma','audio/m4a','audio/mp3','audio/wav','audio/mpeg']} ], flash: [ {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']} //,{version: [12,0], types: ['video/webm']} // for future reference (hopefully!) ], youtube: [ {version: null, types: ['video/youtube', 'video/x-youtube', 'audio/youtube', 'audio/x-youtube']} ], vimeo: [ {version: null, types: ['video/vimeo', 'video/x-vimeo']} ] };
/* Utility methods
- /
mejs.Utility = { encodeUrl: function(url) { return encodeURIComponent(url); //.replace(/\?/gi,'%3F').replace(/=/gi,'%3D').replace(/&/gi,'%26'); }, escapeHTML: function(s) { return s.toString().split('&').join('&').split('<').join('<').split('"').join('"'); }, absolutizeUrl: function(url) { var el = document.createElement('div'); el.innerHTML = '<a href="' + this.escapeHTML(url) + '">x</a>'; return el.firstChild.href; }, getScriptPath: function(scriptNames) { var i = 0, j, codePath = , testname = , slashPos, filenamePos, scriptUrl, scriptPath, scriptFilename, scripts = document.getElementsByTagName('script'), il = scripts.length, jl = scriptNames.length;
// go through all <script> tags for (; i < il; i++) { scriptUrl = scripts[i].src; slashPos = scriptUrl.lastIndexOf('/'); if (slashPos > -1) { scriptFilename = scriptUrl.substring(slashPos + 1); scriptPath = scriptUrl.substring(0, slashPos + 1); } else { scriptFilename = scriptUrl; scriptPath = ; }
// see if any <script> tags have a file name that matches the for (j = 0; j < jl; j++) { testname = scriptNames[j]; filenamePos = scriptFilename.indexOf(testname); if (filenamePos > -1) { codePath = scriptPath; break; } }
// if we found a path, then break and return it if (codePath !== ) { break; } }
// send the best path back return codePath; }, secondsToTimeCode: function(time, forceHours, showFrameCount, fps) { //add framecount if (typeof showFrameCount == 'undefined') { showFrameCount=false; } else if(typeof fps == 'undefined') { fps = 25; }
var hours = Math.floor(time / 3600) % 24, minutes = Math.floor(time / 60) % 60, seconds = Math.floor(time % 60), frames = Math.floor(((time % 1)*fps).toFixed(3)), result = ( (forceHours || hours > 0) ? (hours < 10 ? '0' + hours : hours) + ':' : ) + (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds) + ((showFrameCount) ? ':' + (frames < 10 ? '0' + frames : frames) : );
return result; },
timeCodeToSeconds: function(hh_mm_ss_ff, forceHours, showFrameCount, fps){ if (typeof showFrameCount == 'undefined') { showFrameCount=false; } else if(typeof fps == 'undefined') { fps = 25; }
var tc_array = hh_mm_ss_ff.split(":"), tc_hh = parseInt(tc_array[0], 10), tc_mm = parseInt(tc_array[1], 10), tc_ss = parseInt(tc_array[2], 10), tc_ff = 0, tc_in_seconds = 0;
if (showFrameCount) { tc_ff = parseInt(tc_array[3])/fps; }
tc_in_seconds = ( tc_hh * 3600 ) + ( tc_mm * 60 ) + tc_ss + tc_ff;
return tc_in_seconds; },
convertSMPTEtoSeconds: function (SMPTE) {
if (typeof SMPTE != 'string')
return false;
SMPTE = SMPTE.replace(',', '.');
var secs = 0, decimalLen = (SMPTE.indexOf('.') != -1) ? SMPTE.split('.')[1].length : 0, multiplier = 1;
SMPTE = SMPTE.split(':').reverse();
for (var i = 0; i < SMPTE.length; i++) { multiplier = 1; if (i > 0) { multiplier = Math.pow(60, i); } secs += Number(SMPTE[i]) * multiplier; } return Number(secs.toFixed(decimalLen)); },
/* borrowed from SWFObject: http://code.google.com/p/swfobject/source/browse/trunk/swfobject/src/swfobject.js#474 */ removeSwf: function(id) { var obj = document.getElementById(id); if (obj && /object|embed/i.test(obj.nodeName)) { if (mejs.MediaFeatures.isIE) { obj.style.display = "none"; (function(){ if (obj.readyState == 4) { mejs.Utility.removeObjectInIE(id); } else { setTimeout(arguments.callee, 10); } })(); } else { obj.parentNode.removeChild(obj); } } }, removeObjectInIE: function(id) { var obj = document.getElementById(id); if (obj) { for (var i in obj) { if (typeof obj[i] == "function") { obj[i] = null; } } obj.parentNode.removeChild(obj); } } };
// Core detector, plugins are added below
mejs.PluginDetector = {
// main public function to test a plug version number PluginDetector.hasPluginVersion('flash',[9,0,125]); hasPluginVersion: function(plugin, v) { var pv = this.plugins[plugin]; v[1] = v[1] || 0; v[2] = v[2] || 0; 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; },
// cached values nav: window.navigator, ua: window.navigator.userAgent.toLowerCase(),
// stored version numbers plugins: [],
// runs detectPlugin() and stores the version number addPlugin: function(p, pluginName, mimeType, activeX, axDetect) { this.plugins[p] = this.detectPlugin(pluginName, mimeType, activeX, axDetect); },
// get the version number from the mimetype (all but IE) or ActiveX (IE) detectPlugin: function(pluginName, mimeType, activeX, axDetect) {
var version = [0,0,0], description, i, ax;
// Firefox, Webkit, Opera if (typeof(this.nav.plugins) != 'undefined' && typeof this.nav.plugins[pluginName] == 'object') { description = this.nav.plugins[pluginName].description; if (description && !(typeof this.nav.mimeTypes != 'undefined' && this.nav.mimeTypes[mimeType] && !this.nav.mimeTypes[mimeType].enabledPlugin)) { version = description.replace(pluginName, ).replace(/^\s+/,).replace(/\sr/gi,'.').split('.'); for (i=0; i<version.length; i++) { version[i] = parseInt(version[i].match(/\d+/), 10); } } // Internet Explorer / ActiveX } else if (typeof(window.ActiveXObject) != 'undefined') { try { ax = new ActiveXObject(activeX); if (ax) { version = axDetect(ax); } } catch (e) { } } return version; } };
// Add Flash detection mejs.PluginDetector.addPlugin('flash','Shockwave Flash','application/x-shockwave-flash','ShockwaveFlash.ShockwaveFlash', function(ax) { // adapted from SWFObject var version = [], d = ax.GetVariable("$version"); if (d) { d = d.split(" ")[1].split(","); version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)]; } return version; });
// Add Silverlight detection mejs.PluginDetector.addPlugin('silverlight','Silverlight Plug-In','application/x-silverlight-2','AgControl.AgControl', function (ax) { // Silverlight cannot report its version number to IE // but it does have a isVersionSupported function, so we have to loop through it to get a version number. // adapted from http://www.silverlightversion.com/ var v = [0,0,0,0], loopMatch = function(ax, v, i, n) { while(ax.isVersionSupported(v[0]+ "."+ v[1] + "." + v[2] + "." + v[3])){ v[i]+=n; } v[i] -= n; }; loopMatch(ax, v, 0, 1); loopMatch(ax, v, 1, 1); loopMatch(ax, v, 2, 10000); // the third place in the version number is usually 5 digits (4.0.xxxxx) loopMatch(ax, v, 2, 1000); loopMatch(ax, v, 2, 100); loopMatch(ax, v, 2, 10); loopMatch(ax, v, 2, 1); loopMatch(ax, v, 3, 1);
return v; }); // add adobe acrobat /* PluginDetector.addPlugin('acrobat','Adobe Acrobat','application/pdf','AcroPDF.PDF', function (ax) { var version = [], d = ax.GetVersions().split(',')[0].split('=')[1].split('.');
if (d) { version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)]; } return version; });
- /
// necessary detection (fixes for <IE9) mejs.MediaFeatures = { init: function() { var t = this, d = document, nav = mejs.PluginDetector.nav, ua = mejs.PluginDetector.ua.toLowerCase(), i, v, html5Elements = ['source','track','audio','video'];
// detect browsers (only the ones that have some kind of quirk we need to work around) t.isiPad = (ua.match(/ipad/i) !== null); t.isiPhone = (ua.match(/iphone/i) !== null); t.isiOS = t.isiPhone || t.isiPad; t.isAndroid = (ua.match(/android/i) !== null); t.isBustedAndroid = (ua.match(/android 2\.[12]/) !== null); t.isBustedNativeHTTPS = (location.protocol === 'https:' && (ua.match(/android [12]\./) !== null || ua.match(/macintosh.* version.* safari/) !== null)); t.isIE = (nav.appName.toLowerCase().match(/trident/gi) !== null); t.isChrome = (ua.match(/chrome/gi) !== null); t.isFirefox = (ua.match(/firefox/gi) !== null); t.isWebkit = (ua.match(/webkit/gi) !== null); t.isGecko = (ua.match(/gecko/gi) !== null) && !t.isWebkit && !t.isIE; t.isOpera = (ua.match(/opera/gi) !== null); t.hasTouch = ('ontouchstart' in window && window.ontouchstart != null);
// borrowed from Modernizr t.svg = !! document.createElementNS && !! document.createElementNS('http://www.w3.org/2000/svg','svg').createSVGRect;
// create HTML5 media elements for IE before 9, get a <video> element for fullscreen detection for (i=0; i<html5Elements.length; i++) { v = document.createElement(html5Elements[i]); }
t.supportsMediaTag = (typeof v.canPlayType !== 'undefined' || t.isBustedAndroid);
// Fix for IE9 on Windows 7N / Windows 7KN (Media Player not installer) try{ v.canPlayType("video/mp4"); }catch(e){ t.supportsMediaTag = false; }
// detect native JavaScript fullscreen (Safari/Firefox only, Chrome still fails)
// iOS t.hasSemiNativeFullScreen = (typeof v.webkitEnterFullscreen !== 'undefined');
// W3C t.hasNativeFullscreen = (typeof v.requestFullscreen !== 'undefined');
// webkit/firefox/IE11+ t.hasWebkitNativeFullScreen = (typeof v.webkitRequestFullScreen !== 'undefined'); t.hasMozNativeFullScreen = (typeof v.mozRequestFullScreen !== 'undefined'); t.hasMsNativeFullScreen = (typeof v.msRequestFullscreen !== 'undefined');
t.hasTrueNativeFullScreen = (t.hasWebkitNativeFullScreen || t.hasMozNativeFullScreen || t.hasMsNativeFullScreen); t.nativeFullScreenEnabled = t.hasTrueNativeFullScreen;
// Enabled? if (t.hasMozNativeFullScreen) { t.nativeFullScreenEnabled = document.mozFullScreenEnabled; } else if (t.hasMsNativeFullScreen) { t.nativeFullScreenEnabled = document.msFullscreenEnabled; }
if (t.isChrome) { t.hasSemiNativeFullScreen = false; }
if (t.hasTrueNativeFullScreen) {
t.fullScreenEventName = ; if (t.hasWebkitNativeFullScreen) { t.fullScreenEventName = 'webkitfullscreenchange';
} else if (t.hasMozNativeFullScreen) { t.fullScreenEventName = 'mozfullscreenchange';
} else if (t.hasMsNativeFullScreen) { t.fullScreenEventName = 'MSFullscreenChange'; }
t.isFullScreen = function() { if (v.mozRequestFullScreen) { return d.mozFullScreen;
} else if (v.webkitRequestFullScreen) { return d.webkitIsFullScreen;
} else if (v.hasMsNativeFullScreen) { return d.msFullscreenElement !== null; } }
t.requestFullScreen = function(el) {
if (t.hasWebkitNativeFullScreen) { el.webkitRequestFullScreen();
} else if (t.hasMozNativeFullScreen) { el.mozRequestFullScreen();
} else if (t.hasMsNativeFullScreen) { el.msRequestFullscreen();
} }
t.cancelFullScreen = function() { if (t.hasWebkitNativeFullScreen) { document.webkitCancelFullScreen();
} else if (t.hasMozNativeFullScreen) { document.mozCancelFullScreen();
} else if (t.hasMsNativeFullScreen) { document.msExitFullscreen();
} }
}
// OS X 10.5 can't do this even if it says it can :(
if (t.hasSemiNativeFullScreen && ua.match(/mac os x 10_5/i)) {
t.hasNativeFullScreen = false;
t.hasSemiNativeFullScreen = false;
}
} }; mejs.MediaFeatures.init();
/* extension methods to <video> or <audio> object to bring it into parity with PluginMediaElement (see below)
- /
mejs.HtmlMediaElement = { pluginType: 'native', isFullScreen: false,
setCurrentTime: function (time) { this.currentTime = time; },
setMuted: function (muted) { this.muted = muted; },
setVolume: function (volume) { this.volume = volume; },
// for parity with the plugin versions stop: function () { this.pause(); },
// This can be a url string // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}] setSrc: function (url) {
// Fix for IE9 which can't set .src when there are <source> elements. Awesome, right? var existingSources = this.getElementsByTagName('source'); while (existingSources.length > 0){ this.removeChild(existingSources[0]); }
if (typeof url == 'string') { this.src = url; } else { var i, media;
for (i=0; i<url.length; i++) { media = url[i]; if (this.canPlayType(media.type)) { this.src = media.src; break; } } } },
setVideoSize: function (width, height) { this.width = width; this.height = height; } };
/* Mimics the <video/audio> element by calling Flash's External Interface or Silverlights [ScriptableMember]
- /
mejs.PluginMediaElement = function (pluginid, pluginType, mediaUrl) { this.id = pluginid; this.pluginType = pluginType; this.src = mediaUrl; this.events = {}; this.attributes = {}; };
// JavaScript values and ExternalInterface methods that match HTML5 video properties methods // http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/fl/video/FLVPlayback.html // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html mejs.PluginMediaElement.prototype = {
// special pluginElement: null, pluginType: , isFullScreen: false,
// not implemented :( playbackRate: -1, defaultPlaybackRate: -1, seekable: [], played: [],
// HTML5 read-only properties paused: true, ended: false, seeking: false, duration: 0, error: null, tagName: ,
// HTML5 get/set properties, but only set (updated by event handlers) muted: false, volume: 1, currentTime: 0,
// HTML5 methods play: function () { if (this.pluginApi != null) { if (this.pluginType == 'youtube') { this.pluginApi.playVideo(); } else { this.pluginApi.playMedia(); } this.paused = false; } }, load: function () { if (this.pluginApi != null) { if (this.pluginType == 'youtube') { } else { this.pluginApi.loadMedia(); }
this.paused = false; } }, pause: function () { if (this.pluginApi != null) { if (this.pluginType == 'youtube') { this.pluginApi.pauseVideo(); } else { this.pluginApi.pauseMedia(); }
this.paused = true;
}
},
stop: function () {
if (this.pluginApi != null) {
if (this.pluginType == 'youtube') {
this.pluginApi.stopVideo();
} else {
this.pluginApi.stopMedia();
}
this.paused = true;
}
},
canPlayType: function(type) {
var i,
j,
pluginInfo,
pluginVersions = mejs.plugins[this.pluginType];
for (i=0; i<pluginVersions.length; i++) { pluginInfo = pluginVersions[i];
// test if user has the correct plugin version if (mejs.PluginDetector.hasPluginVersion(this.pluginType, pluginInfo.version)) {
// test for plugin playback types for (j=0; j<pluginInfo.types.length; j++) { // find plugin that can play the type if (type == pluginInfo.types[j]) { return 'probably'; } } } }
return ; },
positionFullscreenButton: function(x,y,visibleAndAbove) { if (this.pluginApi != null && this.pluginApi.positionFullscreenButton) { this.pluginApi.positionFullscreenButton(Math.floor(x),Math.floor(y),visibleAndAbove); } },
hideFullscreenButton: function() { if (this.pluginApi != null && this.pluginApi.hideFullscreenButton) { this.pluginApi.hideFullscreenButton(); } },
// custom methods since not all JavaScript implementations support get/set
// This can be a url string // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}] setSrc: function (url) { if (typeof url == 'string') { this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(url)); this.src = mejs.Utility.absolutizeUrl(url); } else { var i, media;
for (i=0; i<url.length; i++) { media = url[i]; if (this.canPlayType(media.type)) { this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(media.src)); this.src = mejs.Utility.absolutizeUrl(url); break; } } }
}, setCurrentTime: function (time) { if (this.pluginApi != null) { if (this.pluginType == 'youtube') { this.pluginApi.seekTo(time); } else { this.pluginApi.setCurrentTime(time); }
this.currentTime = time; } }, setVolume: function (volume) { if (this.pluginApi != null) { // same on YouTube and MEjs if (this.pluginType == 'youtube') { this.pluginApi.setVolume(volume * 100); } else { this.pluginApi.setVolume(volume); } this.volume = volume; } }, setMuted: function (muted) { if (this.pluginApi != null) { if (this.pluginType == 'youtube') { if (muted) { this.pluginApi.mute(); } else { this.pluginApi.unMute(); } this.muted = muted; this.dispatchEvent('volumechange'); } else { this.pluginApi.setMuted(muted); } this.muted = muted; } },
// additional non-HTML5 methods setVideoSize: function (width, height) {
//if (this.pluginType == 'flash' || this.pluginType == 'silverlight') { if ( this.pluginElement.style) { this.pluginElement.style.width = width + 'px'; this.pluginElement.style.height = height + 'px'; } if (this.pluginApi != null && this.pluginApi.setVideoSize) { this.pluginApi.setVideoSize(width, height); } //} },
setFullscreen: function (fullscreen) { if (this.pluginApi != null && this.pluginApi.setFullscreen) { this.pluginApi.setFullscreen(fullscreen); } },
enterFullScreen: function() { if (this.pluginApi != null && this.pluginApi.setFullscreen) { this.setFullscreen(true); }
},
exitFullScreen: function() { if (this.pluginApi != null && this.pluginApi.setFullscreen) { this.setFullscreen(false); } },
// start: fake events addEventListener: function (eventName, callback, bubble) { this.events[eventName] = this.events[eventName] || []; this.events[eventName].push(callback); }, removeEventListener: function (eventName, callback) { if (!eventName) { this.events = {}; return true; } var callbacks = this.events[eventName]; if (!callbacks) return true; if (!callback) { this.events[eventName] = []; return true; } for (i = 0; i < callbacks.length; i++) { if (callbacks[i] === callback) { this.events[eventName].splice(i, 1); return true; } } return false; }, dispatchEvent: function (eventName) { var i, args, callbacks = this.events[eventName];
if (callbacks) { args = Array.prototype.slice.call(arguments, 1); for (i = 0; i < callbacks.length; i++) { callbacks[i].apply(null, args); } } }, // end: fake events
// fake DOM attribute methods hasAttribute: function(name){ return (name in this.attributes); }, removeAttribute: function(name){ delete this.attributes[name]; }, getAttribute: function(name){ if (this.hasAttribute(name)) { return this.attributes[name]; } return ; }, setAttribute: function(name, value){ this.attributes[name] = value; },
remove: function() { mejs.Utility.removeSwf(this.pluginElement.id); mejs.MediaPluginBridge.unregisterPluginElement(this.pluginElement.id); } };
// Handles calls from Flash/Silverlight and reports them as native <video/audio> events and properties mejs.MediaPluginBridge = {
pluginMediaElements:{}, htmlMediaElements:{},
registerPluginElement: function (id, pluginMediaElement, htmlMediaElement) { this.pluginMediaElements[id] = pluginMediaElement; this.htmlMediaElements[id] = htmlMediaElement; },
unregisterPluginElement: function (id) { delete this.pluginMediaElements[id]; delete this.htmlMediaElements[id]; },
// when Flash/Silverlight is ready, it calls out to this method initPlugin: function (id) {
var pluginMediaElement = this.pluginMediaElements[id], htmlMediaElement = this.htmlMediaElements[id];
if (pluginMediaElement) { // find the javascript bridge switch (pluginMediaElement.pluginType) { case "flash": pluginMediaElement.pluginElement = pluginMediaElement.pluginApi = document.getElementById(id); break; case "silverlight": pluginMediaElement.pluginElement = document.getElementById(pluginMediaElement.id); pluginMediaElement.pluginApi = pluginMediaElement.pluginElement.Content.MediaElementJS; break; }
if (pluginMediaElement.pluginApi != null && pluginMediaElement.success) { pluginMediaElement.success(pluginMediaElement, htmlMediaElement); } } },
// receives events from Flash/Silverlight and sends them out as HTML5 media events // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html fireEvent: function (id, eventName, values) {
var e, i, bufferedTime, pluginMediaElement = this.pluginMediaElements[id];
if(!pluginMediaElement){
return; }
// fake event object to mimic real HTML media event. e = { type: eventName, target: pluginMediaElement };
// attach all values to element and event object for (i in values) { pluginMediaElement[i] = values[i]; e[i] = values[i]; }
// fake the newer W3C buffered TimeRange (loaded and total have been removed) bufferedTime = values.bufferedTime || 0;
e.target.buffered = e.buffered = { start: function(index) { return 0; }, end: function (index) { return bufferedTime; }, length: 1 };
pluginMediaElement.dispatchEvent(e.type, e); } };
/* Default options
- /
mejs.MediaElementDefaults = { // allows testing on HTML5, flash, silverlight // auto: attempts to detect what the browser can do // auto_plugin: prefer plugins and then attempt native HTML5 // native: forces HTML5 playback // shim: disallows HTML5, will attempt either Flash or Silverlight // none: forces fallback view mode: 'auto', // remove or reorder to change plugin priority and availability plugins: ['flash','silverlight','youtube','vimeo'], // shows debug errors on screen enablePluginDebug: false, // use plugin for browsers that have trouble with Basic Authentication on HTTPS sites httpsBasicAuthSite: false, // overrides the type specified, useful for dynamic instantiation type: , // path to Flash and Silverlight plugins pluginPath: mejs.Utility.getScriptPath(['mediaelement.js','mediaelement.min.js','mediaelement-and-player.js','mediaelement-and-player.min.js']), // name of flash file flashName: 'flashmediaelement.swf', // streamer for RTMP streaming flashStreamer: , // turns on the smoothing filter in Flash enablePluginSmoothing: false, // enabled pseudo-streaming (seek) on .mp4 files enablePseudoStreaming: false, // start query parameter sent to server for pseudo-streaming pseudoStreamingStartQueryParam: 'start', // name of silverlight file silverlightName: 'silverlightmediaelement.xap', // default if the <video width> is not specified defaultVideoWidth: 480, // default if the <video height> is not specified defaultVideoHeight: 270, // overrides <video width> pluginWidth: -1, // overrides <video height> pluginHeight: -1, // additional plugin variables in 'key=value' form pluginVars: [], // rate in milliseconds for Flash and Silverlight to fire the timeupdate event // larger number is less accurate, but less strain on plugin->JavaScript bridge timerRate: 250, // initial volume for player startVolume: 0.8, success: function () { }, error: function () { } };
/* Determines if a browser supports the <video> or <audio> element and returns either the native element or a Flash/Silverlight version that mimics HTML5 MediaElement
- /
mejs.MediaElement = function (el, o) { return mejs.HtmlMediaElementShim.create(el,o); };
mejs.HtmlMediaElementShim = {
create: function(el, o) { var options = mejs.MediaElementDefaults, htmlMediaElement = (typeof(el) == 'string') ? document.getElementById(el) : el, tagName = htmlMediaElement.tagName.toLowerCase(), isMediaTag = (tagName === 'audio' || tagName === 'video'), src = (isMediaTag) ? htmlMediaElement.getAttribute('src') : htmlMediaElement.getAttribute('href'), poster = htmlMediaElement.getAttribute('poster'), autoplay = htmlMediaElement.getAttribute('autoplay'), preload = htmlMediaElement.getAttribute('preload'), controls = htmlMediaElement.getAttribute('controls'), playback, prop;
// extend options for (prop in o) { options[prop] = o[prop]; }
// clean up attributes src = (typeof src == 'undefined' || src === null || src == ) ? null : src; poster = (typeof poster == 'undefined' || poster === null) ? : poster; preload = (typeof preload == 'undefined' || preload === null || preload === 'false') ? 'none' : preload; autoplay = !(typeof autoplay == 'undefined' || autoplay === null || autoplay === 'false'); controls = !(typeof controls == 'undefined' || controls === null || controls === 'false');
// test for HTML5 and plugin capabilities playback = this.determinePlayback(htmlMediaElement, options, mejs.MediaFeatures.supportsMediaTag, isMediaTag, src); playback.url = (playback.url !== null) ? mejs.Utility.absolutizeUrl(playback.url) : ;
if (playback.method == 'native') { // second fix for android if (mejs.MediaFeatures.isBustedAndroid) { htmlMediaElement.src = playback.url; htmlMediaElement.addEventListener('click', function() { htmlMediaElement.play(); }, false); }
// add methods to native HTMLMediaElement return this.updateNative(playback, options, autoplay, preload); } else if (playback.method !== ) { // create plugin to mimic HTMLMediaElement
return this.createPlugin( playback, options, poster, autoplay, preload, controls); } else { // boo, no HTML5, no Flash, no Silverlight. this.createErrorMessage( playback, options, poster );
return this; } },
determinePlayback: function(htmlMediaElement, options, supportsMediaTag, isMediaTag, src) { var mediaFiles = [], i, j, k, l, n, type, result = { method: , url: , htmlMediaElement: htmlMediaElement, isVideo: (htmlMediaElement.tagName.toLowerCase() != 'audio')}, pluginName, pluginVersions, pluginInfo, dummy, media;
// STEP 1: Get URL and type from <video src> or <source src>
// supplied type overrides <video type> and <source type> if (typeof options.type != 'undefined' && options.type !== ) {
// accept either string or array of types if (typeof options.type == 'string') { mediaFiles.push({type:options.type, url:src}); } else {
for (i=0; i<options.type.length; i++) { mediaFiles.push({type:options.type[i], url:src}); } }
// test for src attribute first } else if (src !== null) { type = this.formatType(src, htmlMediaElement.getAttribute('type')); mediaFiles.push({type:type, url:src});
// then test for <source> elements } else { // test <source> types to see if they are usable for (i = 0; i < htmlMediaElement.childNodes.length; i++) { n = htmlMediaElement.childNodes[i]; if (n.nodeType == 1 && n.tagName.toLowerCase() == 'source') { src = n.getAttribute('src'); type = this.formatType(src, n.getAttribute('type')); media = n.getAttribute('media');
if (!media || !window.matchMedia || (window.matchMedia && window.matchMedia(media).matches)) { mediaFiles.push({type:type, url:src}); } } } }
// in the case of dynamicly created players // check for audio types if (!isMediaTag && mediaFiles.length > 0 && mediaFiles[0].url !== null && this.getTypeFromFile(mediaFiles[0].url).indexOf('audio') > -1) { result.isVideo = false; }
// STEP 2: Test for playback method
// special case for Android which sadly doesn't implement the canPlayType function (always returns ) if (mejs.MediaFeatures.isBustedAndroid) { htmlMediaElement.canPlayType = function(type) { return (type.match(/video\/(mp4|m4v)/gi) !== null) ? 'maybe' : ; }; }
// test for native playback first
if (supportsMediaTag && (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'native') && !(mejs.MediaFeatures.isBustedNativeHTTPS && options.httpsBasicAuthSite === true)) {
if (!isMediaTag) {
// create a real HTML5 Media Element dummy = document.createElement( result.isVideo ? 'video' : 'audio'); htmlMediaElement.parentNode.insertBefore(dummy, htmlMediaElement); htmlMediaElement.style.display = 'none';
// use this one from now on result.htmlMediaElement = htmlMediaElement = dummy; }
for (i=0; i<mediaFiles.length; i++) { // normal check if (htmlMediaElement.canPlayType(mediaFiles[i].type).replace(/no/, ) !== // special case for Mac/Safari 5.0.3 which answers to canPlayType('audio/mp3') but 'maybe' to canPlayType('audio/mpeg') || htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/mp3/,'mpeg')).replace(/no/, ) !== ) { result.method = 'native'; result.url = mediaFiles[i].url; break; } }
if (result.method === 'native') { if (result.url !== null) { htmlMediaElement.src = result.url; }
// if `auto_plugin` mode, then cache the native result but try plugins. if (options.mode !== 'auto_plugin') { return result; } } }
// if native playback didn't work, then test plugins if (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'shim') { for (i=0; i<mediaFiles.length; i++) { type = mediaFiles[i].type;
// test all plugins in order of preference [silverlight, flash] for (j=0; j<options.plugins.length; j++) {
pluginName = options.plugins[j];
// test version of plugin (for future features) pluginVersions = mejs.plugins[pluginName];
for (k=0; k<pluginVersions.length; k++) { pluginInfo = pluginVersions[k];
// test if user has the correct plugin version
// for youtube/vimeo if (pluginInfo.version == null ||
mejs.PluginDetector.hasPluginVersion(pluginName, pluginInfo.version)) {
// test for plugin playback types for (l=0; l<pluginInfo.types.length; l++) { // find plugin that can play the type if (type == pluginInfo.types[l]) { result.method = pluginName; result.url = mediaFiles[i].url; return result; } } } } } } }
// at this point, being in 'auto_plugin' mode implies that we tried plugins but failed. // if we have native support then return that. if (options.mode === 'auto_plugin' && result.method === 'native') { return result; }
// what if there's nothing to play? just grab the first available if (result.method === && mediaFiles.length > 0) { result.url = mediaFiles[0].url; }
return result; },
formatType: function(url, type) { var ext;
// if no type is supplied, fake it with the extension if (url && !type) { return this.getTypeFromFile(url); } else { // only return the mime part of the type in case the attribute contains the codec // see http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#the-source-element // `video/mp4; codecs="avc1.42E01E, mp4a.40.2"` becomes `video/mp4`
if (type && ~type.indexOf(';')) { return type.substr(0, type.indexOf(';')); } else { return type; } } },
getTypeFromFile: function(url) { url = url.split('?')[0]; var ext = url.substring(url.lastIndexOf('.') + 1).toLowerCase(); return (/(mp4|m4v|ogg|ogv|webm|webmv|flv|wmv|mpeg|mov)/gi.test(ext) ? 'video' : 'audio') + '/' + this.getTypeFromExtension(ext); },
getTypeFromExtension: function(ext) {
switch (ext) { case 'mp4': case 'm4v': return 'mp4'; case 'webm': case 'webma': case 'webmv': return 'webm'; case 'ogg': case 'oga': case 'ogv': return 'ogg'; default: return ext; } },
createErrorMessage: function(playback, options, poster) { var htmlMediaElement = playback.htmlMediaElement, errorContainer = document.createElement('div');
errorContainer.className = 'me-cannotplay';
try { errorContainer.style.width = htmlMediaElement.width + 'px'; errorContainer.style.height = htmlMediaElement.height + 'px'; } catch (e) {}
if (options.customError) {
errorContainer.innerHTML = options.customError;
} else {
errorContainer.innerHTML = (poster !== ) ?
'<a href="' + playback.url + '"><img src="' + poster + '" width="100%" height="100%" /></a>' :
'<a href="' + playback.url + '">' + mejs.i18n.t('Download File') + '</a>';
}
htmlMediaElement.parentNode.insertBefore(errorContainer, htmlMediaElement); htmlMediaElement.style.display = 'none';
options.error(htmlMediaElement); },
createPlugin:function(playback, options, poster, autoplay, preload, controls) { var htmlMediaElement = playback.htmlMediaElement, width = 1, height = 1, pluginid = 'me_' + playback.method + '_' + (mejs.meIndex++), pluginMediaElement = new mejs.PluginMediaElement(pluginid, playback.method, playback.url), container = document.createElement('div'), specialIEContainer, node, initVars;
// copy tagName from html media element pluginMediaElement.tagName = htmlMediaElement.tagName
// copy attributes from html media element to plugin media element for (var i = 0; i < htmlMediaElement.attributes.length; i++) { var attribute = htmlMediaElement.attributes[i]; if (attribute.specified == true) { pluginMediaElement.setAttribute(attribute.name, attribute.value); } }
// check for placement inside atag (sometimes WYSIWYG editors do this) node = htmlMediaElement.parentNode; while (node !== null && node.tagName.toLowerCase() != 'body') { if (node.parentNode.tagName.toLowerCase() == 'p') { node.parentNode.parentNode.insertBefore(node, node.parentNode); break; } node = node.parentNode; } if (playback.isVideo) { width = (options.pluginWidth > 0) ? options.pluginWidth : (options.videoWidth > 0) ? options.videoWidth : (htmlMediaElement.getAttribute('width') !== null) ? htmlMediaElement.getAttribute('width') : options.defaultVideoWidth; height = (options.pluginHeight > 0) ? options.pluginHeight : (options.videoHeight > 0) ? options.videoHeight : (htmlMediaElement.getAttribute('height') !== null) ? htmlMediaElement.getAttribute('height') : options.defaultVideoHeight; // in case of '%' make sure it's encoded width = mejs.Utility.encodeUrl(width); height = mejs.Utility.encodeUrl(height); } else { if (options.enablePluginDebug) { width = 320; height = 240; } } // register plugin pluginMediaElement.success = options.success; mejs.MediaPluginBridge.registerPluginElement(pluginid, pluginMediaElement, htmlMediaElement); // add container (must be added to DOM before inserting HTML for IE) container.className = 'me-plugin'; container.id = pluginid + '_container'; if (playback.isVideo) { htmlMediaElement.parentNode.insertBefore(container, htmlMediaElement); } else { document.body.insertBefore(container, document.body.childNodes[0]); } // flash/silverlight vars initVars = [ 'id=' + pluginid, 'isvideo=' + ((playback.isVideo) ? "true" : "false"), 'autoplay=' + ((autoplay) ? "true" : "false"), 'preload=' + preload, 'width=' + width, 'startvolume=' + options.startVolume, 'timerrate=' + options.timerRate, 'flashstreamer=' + options.flashStreamer, 'height=' + height, 'pseudostreamstart=' + options.pseudoStreamingStartQueryParam]; if (playback.url !== null) { if (playback.method == 'flash') { initVars.push('file=' + mejs.Utility.encodeUrl(playback.url)); } else { initVars.push('file=' + playback.url); } } if (options.enablePluginDebug) { initVars.push('debug=true'); } if (options.enablePluginSmoothing) { initVars.push('smoothing=true'); } if (options.enablePseudoStreaming) { initVars.push('pseudostreaming=true'); } if (controls) { initVars.push('controls=true'); // shows controls in the plugin if desired } if (options.pluginVars) { initVars = initVars.concat(options.pluginVars); } switch (playback.method) { case 'silverlight': container.innerHTML = '<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" id="' + pluginid + '" name="' + pluginid + '" width="' + width + '" height="' + height + '" class="mejs-shim">' + '<param name="initParams" value="' + initVars.join(',') + '" />' + '<param name="windowless" value="true" />' + '<param name="background" value="black" />' + '<param name="minRuntimeVersion" value="3.0.0.0" />' + '<param name="autoUpgrade" value="true" />' + '<param name="source" value="' + options.pluginPath + options.silverlightName + '" />' + '</object>'; break; case 'flash': if (mejs.MediaFeatures.isIE) { specialIEContainer = document.createElement('div'); container.appendChild(specialIEContainer); specialIEContainer.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' + 'id="' + pluginid + '" width="' + width + '" height="' + height + '" class="mejs-shim">' + '<param name="movie" value="' + options.pluginPath + options.flashName + '?x=' + (new Date()) + '" />' + '<param name="flashvars" value="' + initVars.join('&') + '" />' + '<param name="quality" value="high" />' + '<param name="bgcolor" value="#000000" />' + '<param name="wmode" value="transparent" />' + '<param name="allowScriptAccess" value="always" />' + '<param name="allowFullScreen" value="true" />' + '</object>'; } else { container.innerHTML = '<embed id="' + pluginid + '" name="' + pluginid + '" ' + 'play="true" ' + 'loop="false" ' + 'quality="high" ' + 'bgcolor="#000000" ' + 'wmode="transparent" ' + 'allowScriptAccess="always" ' + 'allowFullScreen="true" ' + 'type="application/x-shockwave-flash" pluginspage="//www.macromedia.com/go/getflashplayer" ' + 'src="' + options.pluginPath + options.flashName + '" ' + 'flashvars="' + initVars.join('&') + '" ' + 'width="' + width + '" ' + 'height="' + height + '" ' + 'class="mejs-shim"></embed>'; } break; case 'youtube': var videoId = playback.url.substr(playback.url.lastIndexOf('=')+1); youtubeSettings = { container: container, containerId: container.id, pluginMediaElement: pluginMediaElement, pluginId: pluginid, videoId: videoId, height: height, width: width }; if (mejs.PluginDetector.hasPluginVersion('flash', [10,0,0]) ) { mejs.YouTubeApi.createFlash(youtubeSettings); } else { mejs.YouTubeApi.enqueueIframe(youtubeSettings); } break; // DEMO Code. Does NOT work. case 'vimeo': // pluginMediaElement.vimeoid = playback.url.substr(playback.url.lastIndexOf('/')+1); container.innerHTML ='<iframe src="http://player.vimeo.com/video/' + pluginMediaElement.vimeoid + '?portrait=0&byline=0&title=0" width="' + width +'" height="' + height +'" frameborder="0" class="mejs-shim"></iframe>'; /* container.innerHTML = '<object width="' + width + '" height="' + height + '" class="mejs-shim">' + '<param name="allowfullscreen" value="true" />' + '<param name="allowscriptaccess" value="always" />' + '<param name="flashvars" value="api=1" />' + '<param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=' + pluginMediaElement.vimeoid + '&server=vimeo.com&show_title=0&show_byline=0&show_portrait=0&color=00adef&fullscreen=1&autoplay=0&loop=0" />' + '<embed src="//vimeo.com/moogaloop.swf?api=1&clip_id=' + pluginMediaElement.vimeoid + '&server=vimeo.com&show_title=0&show_byline=0&show_portrait=0&color=00adef&fullscreen=1&autoplay=0&loop=0" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="' + width + '" height="' + height + '" class="mejs-shim"></embed>' + '</object>'; */ break; } // hide original element htmlMediaElement.style.display = 'none'; // prevent browser from autoplaying when using a plugin htmlMediaElement.removeAttribute('autoplay'); // FYI: options.success will be fired by the MediaPluginBridge return pluginMediaElement; }, updateNative: function(playback, options, autoplay, preload) { var htmlMediaElement = playback.htmlMediaElement, m; // add methods to video object to bring it into parity with Flash Object for (m in mejs.HtmlMediaElement) { htmlMediaElement[m] = mejs.HtmlMediaElement[m]; } /* Chrome now supports preload="none" if (mejs.MediaFeatures.isChrome) { // special case to enforce preload attribute (Chrome doesn't respect this) if (preload === 'none' && !autoplay) { // forces the browser to stop loading (note: fails in IE9) htmlMediaElement.src = ; htmlMediaElement.load(); htmlMediaElement.canceledPreload = true; htmlMediaElement.addEventListener('play',function() { if (htmlMediaElement.canceledPreload) { htmlMediaElement.src = playback.url; htmlMediaElement.load(); htmlMediaElement.play(); htmlMediaElement.canceledPreload = false; } }, false); // for some reason Chrome forgets how to autoplay sometimes. } else if (autoplay) { htmlMediaElement.load(); htmlMediaElement.play(); } } */ // fire success code options.success(htmlMediaElement, htmlMediaElement); return htmlMediaElement; } }; /* - test on IE (object vs. embed) - determine when to use iframe (Firefox, Safari, Mobile) vs. Flash (Chrome, IE) - fullscreen?
- /
- (function(context, exports, undefined) {
- (function(exports, undefined) {
- (function(exports, undefined) {
- (function(exports, undefined) {
.addClass(t.$media[0].className) .insertBefore(t.$media);
// add classes for user and content t.container.addClass( (mf.isAndroid ? 'mejs-android ' : ) + (mf.isiOS ? 'mejs-ios ' : ) + (mf.isiPad ? 'mejs-ipad ' : ) + (mf.isiPhone ? 'mejs-iphone ' : ) + (t.isVideo ? 'mejs-video ' : 'mejs-audio ') );
// move the <video/video> tag into the right spot
if (mf.isiOS) {
// sadly, you can't move nodes in iOS, so we have to destroy and recreate it! var $newMedia = t.$media.clone();
t.container.find('.mejs-mediaelement').append($newMedia);
t.$media.remove(); t.$node = t.$media = $newMedia; t.node = t.media = $newMedia[0]
} else {
// normal way of moving it into place (doesn't work on iOS) t.container.find('.mejs-mediaelement').append(t.$media); }
// find parts t.controls = t.container.find('.mejs-controls'); t.layers = t.container.find('.mejs-layers');
// determine the size
/* size priority: (1) videoWidth (forced), (2) style="width;height;" (3) width attribute, (4) defaultVideoWidth (for unspecified cases) */
var tagType = (t.isVideo ? 'video' : 'audio'), capsTagName = tagType.substring(0,1).toUpperCase() + tagType.substring(1);
if (t.options[tagType + 'Width'] > 0 || t.options[tagType + 'Width'].toString().indexOf('%') > -1) {
t.width = t.options[tagType + 'Width'];
} else if (t.media.style.width !== && t.media.style.width !== null) {
t.width = t.media.style.width;
} else if (t.media.getAttribute('width') !== null) {
t.width = t.$media.attr('width');
} else {
t.width = t.options['default' + capsTagName + 'Width'];
}
if (t.options[tagType + 'Height'] > 0 || t.options[tagType + 'Height'].toString().indexOf('%') > -1) { t.height = t.options[tagType + 'Height']; } else if (t.media.style.height !== && t.media.style.height !== null) { t.height = t.media.style.height; } else if (t.$media[0].getAttribute('height') !== null) { t.height = t.$media.attr('height'); } else { t.height = t.options['default' + capsTagName + 'Height']; }
// set the size, while we wait for the plugins to load below t.setPlayerSize(t.width, t.height);
// create MediaElementShim meOptions.pluginWidth = t.width; meOptions.pluginHeight = t.height; }
// create MediaElement shim mejs.MediaElement(t.$media[0], meOptions);
if (typeof(t.container) != 'undefined' && t.controlsAreVisible){ // controls are shown when loaded t.container.trigger('controlsshown'); } },
showControls: function(doAnimation) { var t = this;
doAnimation = typeof doAnimation == 'undefined' || doAnimation;
if (t.controlsAreVisible) return;
if (doAnimation) { t.controls .css('visibility','visible') .stop(true, true).fadeIn(200, function() { t.controlsAreVisible = true; t.container.trigger('controlsshown'); });
// any additional controls people might add and want to hide t.container.find('.mejs-control') .css('visibility','visible') .stop(true, true).fadeIn(200, function() {t.controlsAreVisible = true;});
} else { t.controls .css('visibility','visible') .css('display','block');
// any additional controls people might add and want to hide t.container.find('.mejs-control') .css('visibility','visible') .css('display','block');
t.controlsAreVisible = true; t.container.trigger('controlsshown'); }
t.setControlsSize();
},
hideControls: function(doAnimation) { var t = this;
doAnimation = typeof doAnimation == 'undefined' || doAnimation;
if (!t.controlsAreVisible || t.options.alwaysShowControls) return;
if (doAnimation) { // fade out main controls t.controls.stop(true, true).fadeOut(200, function() { $(this) .css('visibility','hidden') .css('display','block');
t.controlsAreVisible = false; t.container.trigger('controlshidden'); });
// any additional controls people might add and want to hide t.container.find('.mejs-control').stop(true, true).fadeOut(200, function() { $(this) .css('visibility','hidden') .css('display','block'); }); } else {
// hide main controls t.controls .css('visibility','hidden') .css('display','block');
// hide others t.container.find('.mejs-control') .css('visibility','hidden') .css('display','block');
t.controlsAreVisible = false; t.container.trigger('controlshidden'); } },
controlsTimer: null,
startControlsTimer: function(timeout) {
var t = this;
timeout = typeof timeout != 'undefined' ? timeout : 1500;
t.killControlsTimer('start');
t.controlsTimer = setTimeout(function() { // t.hideControls(); t.killControlsTimer('hide'); }, timeout); },
killControlsTimer: function(src) {
var t = this;
if (t.controlsTimer !== null) { clearTimeout(t.controlsTimer); delete t.controlsTimer; t.controlsTimer = null; } },
controlsEnabled: true,
disableControls: function() { var t= this;
t.killControlsTimer(); t.hideControls(false); this.controlsEnabled = false; },
enableControls: function() { var t= this;
t.showControls(false);
t.controlsEnabled = true; },
// Sets up all controls and events
meReady: function(media, domNode) {
var t = this,
mf = mejs.MediaFeatures,
autoplayAttr = domNode.getAttribute('autoplay'),
autoplay = !(typeof autoplayAttr == 'undefined' || autoplayAttr === null || autoplayAttr === 'false'),
featureIndex,
feature;
// make sure it can't create itself again if a plugin reloads if (t.created) { return; } else { t.created = true; }
t.media = media; t.domNode = domNode;
if (!(mf.isAndroid && t.options.AndroidUseNativeControls) && !(mf.isiPad && t.options.iPadUseNativeControls) && !(mf.isiPhone && t.options.iPhoneUseNativeControls)) {
// two built in features t.buildposter(t, t.controls, t.layers, t.media); t.buildkeyboard(t, t.controls, t.layers, t.media); t.buildoverlays(t, t.controls, t.layers, t.media);
// grab for use by features t.findTracks();
// add user-defined features/controls for (featureIndex in t.options.features) { feature = t.options.features[featureIndex]; if (t['build' + feature]) { try { t['build' + feature](t, t.controls, t.layers, t.media); } catch (e) { // TODO: report control error //throw e; // // } } }
t.container.trigger('controlsready');
// reset all layers and controls t.setPlayerSize(t.width, t.height); t.setControlsSize();
// controls fade
if (t.isVideo) {
if (mejs.MediaFeatures.hasTouch) {
// for touch devices (iOS, Android) // show/hide without animation on touch
t.$media.bind('touchstart', function() {
// toggle controls
if (t.controlsAreVisible) {
t.hideControls(false);
} else {
if (t.controlsEnabled) {
t.showControls(false);
}
}
});
} else {
// create callback here since it needs access to current // MediaElement object mejs.MediaElementPlayer.prototype.clickToPlayPauseCallback = function() { //
if (t.options.clickToPlayPause) { if (t.media.paused) { t.media.play(); } else { t.media.pause(); } } };
// click to play/pause t.media.addEventListener('click', t.clickToPlayPauseCallback, false);
// show/hide controls t.container .bind('mouseenter mouseover', function () { if (t.controlsEnabled) { if (!t.options.alwaysShowControls) { t.killControlsTimer('enter'); t.showControls(); t.startControlsTimer(2500); } } }) .bind('mousemove', function() { if (t.controlsEnabled) { if (!t.controlsAreVisible) { t.showControls(); } //t.killControlsTimer('move'); if (!t.options.alwaysShowControls) { t.startControlsTimer(2500); } } }) .bind('mouseleave', function () { if (t.controlsEnabled) { if (!t.media.paused && !t.options.alwaysShowControls) { t.startControlsTimer(1000); } } }); }
if(t.options.hideVideoControlsOnLoad) { t.hideControls(false); }
// check for autoplay if (autoplay && !t.options.alwaysShowControls) { t.hideControls(); }
// resizer if (t.options.enableAutosize) { t.media.addEventListener('loadedmetadata', function(e) { // if the <video height> was not set and the options.videoHeight was not set // then resize to the real dimensions if (t.options.videoHeight <= 0 && t.domNode.getAttribute('height') === null && !isNaN(e.target.videoHeight)) { t.setPlayerSize(e.target.videoWidth, e.target.videoHeight); t.setControlsSize(); t.media.setVideoSize(e.target.videoWidth, e.target.videoHeight); } }, false); } }
// EVENTS
// FOCUS: when a video starts playing, it takes focus from other players (possibily pausing them) media.addEventListener('play', function() { var playerIndex;
// go through all other players for (playerIndex in mejs.players) { var p = mejs.players[playerIndex]; if (p.id != t.id && t.options.pauseOtherPlayers && !p.paused && !p.ended) { p.pause(); } p.hasFocus = false; }
t.hasFocus = true; },false);
// ended for all
t.media.addEventListener('ended', function (e) {
if(t.options.autoRewind) {
try{
t.media.setCurrentTime(0);
} catch (exp) {
} } t.media.pause();
if (t.setProgressRail) { t.setProgressRail(); } if (t.setCurrentRail) { t.setCurrentRail(); }
if (t.options.loop) { t.media.play(); } else if (!t.options.alwaysShowControls && t.controlsEnabled) { t.showControls(); } }, false);
// resize on the first play t.media.addEventListener('loadedmetadata', function(e) { if (t.updateDuration) { t.updateDuration(); } if (t.updateCurrent) { t.updateCurrent(); }
if (!t.isFullScreen) { t.setPlayerSize(t.width, t.height); t.setControlsSize(); } }, false);
// webkit has trouble doing this without a delay
setTimeout(function () {
t.setPlayerSize(t.width, t.height);
t.setControlsSize();
}, 50);
// adjust controls whenever window sizes (used to be in fullscreen only) t.globalBind('resize', function() {
// don't resize for fullscreen mode if ( !(t.isFullScreen || (mejs.MediaFeatures.hasTrueNativeFullScreen && document.webkitIsFullScreen)) ) { t.setPlayerSize(t.width, t.height); }
// always adjust controls t.setControlsSize(); });
// TEMP: needs to be moved somewhere else if (t.media.pluginType == 'youtube') { t.container.find('.mejs-overlay-play').hide(); } }
// force autoplay for HTML5 if (autoplay && media.pluginType == 'native') { media.load(); media.play(); }
if (t.options.success) {
if (typeof t.options.success == 'string') { window[t.options.success](t.media, t.domNode, t); } else { t.options.success(t.media, t.domNode, t); } } },
handleError: function(e) { var t = this;
t.controls.hide();
// Tell user that the file cannot be played if (t.options.error) { t.options.error(e); } },
setPlayerSize: function(width,height) { var t = this;
if (typeof width != 'undefined') { t.width = width; }
if (typeof height != 'undefined') { t.height = height; }
// detect 100% mode - use currentStyle for IE since css() doesn't return percentages
if (t.height.toString().indexOf('%') > 0 || t.$node.css('max-width') === '100%' || parseInt(t.$node.css('max-width').replace(/px/,), 10) / t.$node.offsetParent().width() === 1 || (t.$node[0].currentStyle && t.$node[0].currentStyle.maxWidth === '100%')) {
// do we have the native dimensions yet? var nativeWidth = t.isVideo ? ((t.media.videoWidth && t.media.videoWidth > 0) ? t.media.videoWidth : t.options.defaultVideoWidth) : t.options.defaultAudioWidth, nativeHeight = t.isVideo ? ((t.media.videoHeight && t.media.videoHeight > 0) ? t.media.videoHeight : t.options.defaultVideoHeight) : t.options.defaultAudioHeight, parentWidth = t.container.parent().closest(':visible').width(), newHeight = t.isVideo || !t.options.autosizeProgress ? parseInt(parentWidth * nativeHeight/nativeWidth, 10) : nativeHeight;
if (t.container.parent()[0].tagName.toLowerCase() === 'body') { // && t.container.siblings().count == 0) { parentWidth = $(window).width(); newHeight = $(window).height(); }
if ( newHeight != 0 && parentWidth != 0 ) { // set outer container size t.container .width(parentWidth) .height(newHeight);
// set native <video> or <audio> and shims t.$media.add(t.container.find('.mejs-shim')) .width('100%') .height('100%');
// if shim is ready, send the size to the embeded plugin if (t.isVideo) { if (t.media.setVideoSize) { t.media.setVideoSize(parentWidth, newHeight); } }
// set the layers t.layers.children('.mejs-layer') .width('100%') .height('100%'); }
} else {
t.container .width(t.width) .height(t.height);
t.layers.children('.mejs-layer') .width(t.width) .height(t.height);
}
// special case for big play button so it doesn't go over the controls area var playLayer = t.layers.find('.mejs-overlay-play'), playButton = playLayer.find('.mejs-overlay-button');
playLayer.height(t.container.height() - t.controls.height()); playButton.css('margin-top', '-' + (playButton.height()/2 - t.controls.height()/2).toString() + 'px' );
},
setControlsSize: function() { var t = this, usedWidth = 0, railWidth = 0, rail = t.controls.find('.mejs-time-rail'), total = t.controls.find('.mejs-time-total'), current = t.controls.find('.mejs-time-current'), loaded = t.controls.find('.mejs-time-loaded'), others = rail.siblings();
// allow the size to come from custom CSS
if (t.options && !t.options.autosizeProgress) {
// Also, frontends devs can be more flexible
// due the opportunity of absolute positioning.
railWidth = parseInt(rail.css('width'));
}
// attempt to autosize if (railWidth === 0 || !railWidth) {
// find the size of all the other controls besides the rail others.each(function() { var $this = $(this); if ($this.css('position') != 'absolute' && $this.is(':visible')) { usedWidth += $(this).outerWidth(true); } });
// fit the rail into the remaining space railWidth = t.controls.width() - usedWidth - (rail.outerWidth(true) - rail.width()); }
// outer area rail.width(railWidth); // dark space total.width(railWidth - (total.outerWidth(true) - total.width()));
if (t.setProgressRail) t.setProgressRail(); if (t.setCurrentRail) t.setCurrentRail(); },
buildposter: function(player, controls, layers, media) {
var t = this,
poster =
.appendTo(layers), posterUrl = player.$media.attr('poster');
// prioriy goes to option (this is useful if you need to support iOS 3.x (iOS completely fails with poster) if (player.options.poster !== ) { posterUrl = player.options.poster; }
// second, try the real poster if (posterUrl !== && posterUrl != null) { t.setPoster(posterUrl); } else { poster.hide(); }
media.addEventListener('play',function() { poster.hide(); }, false);
if(player.options.showPosterWhenEnded && player.options.autoRewind){ media.addEventListener('ended',function() { poster.show(); }, false); } },
setPoster: function(url) { var t = this, posterDiv = t.container.find('.mejs-poster'), posterImg = posterDiv.find('img');
if (posterImg.length == 0) { posterImg = $('<img width="100%" height="100%" />').appendTo(posterDiv); }
posterImg.attr('src', url); posterDiv.css({'background-image' : 'url(' + url + ')'}); },
buildoverlays: function(player, controls, layers, media) {
var t = this;
if (!player.isVideo) return;
var loading =
$(' ').hide() // start out hidden .appendTo(layers), error =
$(' ').hide() // start out hidden .appendTo(layers), // this needs to come last so it's on top bigPlay =
$(' ').appendTo(layers) .click(function() {
if (t.options.clickToPlayPause) { if (media.paused) { media.play(); } else { media.pause(); } }
});
/* if (mejs.MediaFeatures.isiOS || mejs.MediaFeatures.isAndroid) { bigPlay.remove(); loading.remove(); } */
// show/hide big play button
media.addEventListener('play',function() {
bigPlay.hide();
loading.hide();
controls.find('.mejs-time-buffering').hide();
error.hide();
}, false);
media.addEventListener('playing', function() { bigPlay.hide(); loading.hide(); controls.find('.mejs-time-buffering').hide(); error.hide(); }, false);
media.addEventListener('seeking', function() { loading.show(); controls.find('.mejs-time-buffering').show(); }, false);
media.addEventListener('seeked', function() { loading.hide(); controls.find('.mejs-time-buffering').hide(); }, false);
media.addEventListener('pause',function() { if (!mejs.MediaFeatures.isiPhone) { bigPlay.show(); } }, false);
media.addEventListener('waiting', function() { loading.show(); controls.find('.mejs-time-buffering').show(); }, false);
// show/hide loading
media.addEventListener('loadeddata',function() {
// for some reason Chrome is firing this event
//if (mejs.MediaFeatures.isChrome && media.getAttribute && media.getAttribute('preload') === 'none')
// return;
loading.show(); controls.find('.mejs-time-buffering').show(); }, false); media.addEventListener('canplay',function() { loading.hide(); controls.find('.mejs-time-buffering').hide(); }, false);
// error handling media.addEventListener('error',function() { loading.hide(); controls.find('.mejs-time-buffering').hide(); error.show(); error.find('mejs-overlay-error').html("Error loading this resource"); }, false); },
buildkeyboard: function(player, controls, layers, media) {
var t = this;
// listen for key presses t.globalBind('keydown', function(e) {
if (player.hasFocus && player.options.enableKeyboard) {
// find a matching key for (var i=0, il=player.options.keyActions.length; i<il; i++) { var keyAction = player.options.keyActions[i];
for (var j=0, jl=keyAction.keys.length; j<jl; j++) { if (e.keyCode == keyAction.keys[j]) { e.preventDefault(); keyAction.action(player, media, e.keyCode); return false; } } } }
return true; });
// check if someone clicked outside a player region, then kill its focus t.globalBind('click', function(event) { if ($(event.target).closest('.mejs-container').length == 0) { player.hasFocus = false; } });
},
findTracks: function() { var t = this, tracktags = t.$media.find('track');
// store for use by plugins t.tracks = []; tracktags.each(function(index, track) {
track = $(track);
t.tracks.push({ srclang: (track.attr('srclang')) ? track.attr('srclang').toLowerCase() : , src: track.attr('src'), kind: track.attr('kind'), label: track.attr('label') || , entries: [], isLoaded: false }); }); }, changeSkin: function(className) { this.container[0].className = 'mejs-container ' + className; this.setPlayerSize(this.width, this.height); this.setControlsSize(); }, play: function() { this.media.play(); }, pause: function() { try { this.media.pause(); } catch (e) {} }, load: function() { this.media.load(); }, setMuted: function(muted) { this.media.setMuted(muted); }, setCurrentTime: function(time) { this.media.setCurrentTime(time); }, getCurrentTime: function() { return this.media.currentTime; }, setVolume: function(volume) { this.media.setVolume(volume); }, getVolume: function() { return this.media.volume; }, setSrc: function(src) { this.media.setSrc(src); }, remove: function() { var t = this, featureIndex, feature;
// invoke features cleanup for (featureIndex in t.options.features) { feature = t.options.features[featureIndex]; if (t['clean' + feature]) { try { t['clean' + feature](t); } catch (e) { // TODO: report control error //throw e; // // } } }
// grab video and put it back in place if (!t.isDynamic) { t.$media.prop('controls', true); // detach events from the video // TODO: detach event listeners better than this; // also detach ONLY the events attached by this plugin! t.$node.clone().show().insertBefore(t.container); t.$node.remove(); } else { t.$node.insertBefore(t.container); }
if (t.media.pluginType !== 'native') { t.media.remove(); }
// Remove the player from the mejs.players object so that pauseOtherPlayers doesn't blow up when trying to pause a non existance flash api. delete mejs.players[t.id];
t.container.remove(); t.globalUnbind(); delete t.node.player; } };
(function(){ var rwindow = /^((after|before)print|(before)?unload|hashchange|message|o(ff|n)line|page(hide|show)|popstate|resize|storage)\b/;
function splitEvents(events, id) { // add player ID as an event namespace so it's easier to unbind them all later var ret = {d: [], w: []}; $.each((events || ).split(' '), function(k, v){ var eventname = v + '.' + id; if (eventname.indexOf('.') === 0) { ret.d.push(eventname); ret.w.push(eventname); } else { ret[rwindow.test(v) ? 'w' : 'd'].push(eventname); } }); ret.d = ret.d.join(' '); ret.w = ret.w.join(' '); return ret; }
mejs.MediaElementPlayer.prototype.globalBind = function(events, data, callback) { var t = this; events = splitEvents(events, t.id); if (events.d) $(document).bind(events.d, data, callback); if (events.w) $(window).bind(events.w, data, callback); };
mejs.MediaElementPlayer.prototype.globalUnbind = function(events, callback) { var t = this; events = splitEvents(events, t.id); if (events.d) $(document).unbind(events.d, callback); if (events.w) $(window).unbind(events.w, callback); }; })();
// turn into jQuery plugin if (typeof jQuery != 'undefined') { jQuery.fn.mediaelementplayer = function (options) { if (options === false) { this.each(function () { var player = jQuery(this).data('mediaelementplayer'); if (player) { player.remove(); } jQuery(this).removeData('mediaelementplayer'); }); } else { this.each(function () { jQuery(this).data('mediaelementplayer', new mejs.MediaElementPlayer(this, options)); }); } return this; }; }
$(document).ready(function() { // auto enable using JSON attribute $('.mejs-player').mediaelementplayer(); });
// push out to window window.MediaElementPlayer = mejs.MediaElementPlayer;
})(mejs.$);
(function($) {
$.extend(mejs.MepDefaults, { playpauseText: mejs.i18n.t('Play/Pause') });
// PLAY/pause BUTTON $.extend(MediaElementPlayer.prototype, { buildplaypause: function(player, controls, layers, media) { var t = this, play =
$(' ').appendTo(controls) .click(function(e) { e.preventDefault();
if (media.paused) { media.play(); } else { media.pause(); }
return false; });
media.addEventListener('play',function() { play.removeClass('mejs-play').addClass('mejs-pause'); }, false); media.addEventListener('playing',function() { play.removeClass('mejs-play').addClass('mejs-pause'); }, false);
media.addEventListener('pause',function() {
play.removeClass('mejs-pause').addClass('mejs-play');
}, false);
media.addEventListener('paused',function() {
play.removeClass('mejs-pause').addClass('mejs-play');
}, false);
}
});
})(mejs.$);
(function($) {
$.extend(mejs.MepDefaults, { stopText: 'Stop' });
// STOP BUTTON $.extend(MediaElementPlayer.prototype, { buildstop: function(player, controls, layers, media) { var t = this, stop =
$(' ').appendTo(controls) .click(function() { if (!media.paused) { media.pause(); } if (media.currentTime > 0) { media.setCurrentTime(0);
media.pause();
controls.find('.mejs-time-current').width('0px'); controls.find('.mejs-time-handle').css('left', '0px'); controls.find('.mejs-time-float-current').html( mejs.Utility.secondsToTimeCode(0) ); controls.find('.mejs-currenttime').html( mejs.Utility.secondsToTimeCode(0) ); layers.find('.mejs-poster').show(); } }); } });
})(mejs.$);
(function($) { // progress/loaded bar $.extend(MediaElementPlayer.prototype, { buildprogress: function(player, controls, layers, media) {
$('''+ ''+ ''+ ''+ ''+ '' + '00:00' + '' + ''+ ''+
'.appendTo(controls); controls.find('.mejs-time-buffering').hide();
var t = this, total = controls.find('.mejs-time-total'), loaded = controls.find('.mejs-time-loaded'), current = controls.find('.mejs-time-current'), handle = controls.find('.mejs-time-handle'), timefloat = controls.find('.mejs-time-float'), timefloatcurrent = controls.find('.mejs-time-float-current'), handleMouseMove = function (e) { // mouse position relative to the object var x = e.pageX, offset = total.offset(), width = total.outerWidth(true), percentage = 0, newTime = 0, pos = 0;
if (media.duration) {
if (x < offset.left) {
x = offset.left;
} else if (x > width + offset.left) {
x = width + offset.left;
}
pos = x - offset.left; percentage = (pos / width); newTime = (percentage <= 0.02) ? 0 : percentage * media.duration;
// seek to where the mouse is if (mouseIsDown && newTime !== media.currentTime) { media.setCurrentTime(newTime); }
// position floating time box if (!mejs.MediaFeatures.hasTouch) { timefloat.css('left', pos); timefloatcurrent.html( mejs.Utility.secondsToTimeCode(newTime) ); timefloat.show(); } } }, mouseIsDown = false, mouseIsOver = false;
// handle clicks //controls.find('.mejs-time-rail').delegate('span', 'click', handleMouseMove); total .bind('mousedown', function (e) { // only handle left clicks if (e.which === 1) { mouseIsDown = true; handleMouseMove(e); t.globalBind('mousemove.dur', function(e) { handleMouseMove(e); }); t.globalBind('mouseup.dur', function (e) { mouseIsDown = false; timefloat.hide(); t.globalUnbind('.dur'); }); return false; } }) .bind('mouseenter', function(e) { mouseIsOver = true; t.globalBind('mousemove.dur', function(e) { handleMouseMove(e); }); if (!mejs.MediaFeatures.hasTouch) { timefloat.show(); } }) .bind('mouseleave',function(e) { mouseIsOver = false; if (!mouseIsDown) { t.globalUnbind('.dur'); timefloat.hide(); } });
// loading media.addEventListener('progress', function (e) { player.setProgressRail(e); player.setCurrentRail(e); }, false);
// current time media.addEventListener('timeupdate', function(e) { player.setProgressRail(e); player.setCurrentRail(e); }, false);
// store for later use
t.loaded = loaded;
t.total = total;
t.current = current;
t.handle = handle;
},
setProgressRail: function(e) {
var t = this, target = (e != undefined) ? e.target : t.media, percent = null;
// newest HTML5 spec has buffered array (FF4, Webkit) if (target && target.buffered && target.buffered.length > 0 && target.buffered.end && target.duration) { // TODO: account for a real array with multiple values (only Firefox 4 has this so far) percent = target.buffered.end(0) / target.duration; } // Some browsers (e.g., FF3.6 and Safari 5) cannot calculate target.bufferered.end() // to be anything other than 0. If the byte count is available we use this instead. // Browsers that support the else if do not seem to have the bufferedBytes value and // should skip to there. Tested in Safari 5, Webkit head, FF3.6, Chrome 6, IE 7/8. else if (target && target.bytesTotal != undefined && target.bytesTotal > 0 && target.bufferedBytes != undefined) { percent = target.bufferedBytes / target.bytesTotal; } // Firefox 3 with an Ogg file seems to go this way else if (e && e.lengthComputable && e.total != 0) { percent = e.loaded/e.total; }
// finally update the progress bar if (percent !== null) { percent = Math.min(1, Math.max(0, percent)); // update loaded bar if (t.loaded && t.total) { t.loaded.width(t.total.width() * percent); } } }, setCurrentRail: function() {
var t = this;
if (t.media.currentTime != undefined && t.media.duration) {
// update bar and handle if (t.total && t.handle) { var newWidth = Math.round(t.total.width() * t.media.currentTime / t.media.duration), handlePos = newWidth - Math.round(t.handle.outerWidth(true) / 2);
t.current.width(newWidth); t.handle.css('left', handlePos); } }
} }); })(mejs.$);
(function($) {
// options $.extend(mejs.MepDefaults, { duration: -1, timeAndDurationSeparator: ' | ' });
// current and duration 00:00 / 00:00
$.extend(MediaElementPlayer.prototype, {
buildcurrent: function(player, controls, layers, media) {
var t = this;
'' + (player.options.alwaysShowHours ? '00:' : ) + (player.options.showTimecodeFrameCount? '00:00:00':'00:00')+ ''+
'.appendTo(controls);
t.currenttime = t.controls.find('.mejs-currenttime');
media.addEventListener('timeupdate',function() { player.updateCurrent(); }, false); },
buildduration: function(player, controls, layers, media) {
var t = this;
if (controls.children().last().find('.mejs-currenttime').length > 0) { $(t.options.timeAndDurationSeparator + '' + (t.options.duration > 0 ? mejs.Utility.secondsToTimeCode(t.options.duration, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25) : ((player.options.alwaysShowHours ? '00:' : ) + (player.options.showTimecodeFrameCount? '00:00:00':'00:00')) ) + '') .appendTo(controls.find('.mejs-time')); } else {
// add class to current time controls.find('.mejs-currenttime').parent().addClass('mejs-currenttime-container');
$(''' + (t.options.duration > 0 ? mejs.Utility.secondsToTimeCode(t.options.duration, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25) : ((player.options.alwaysShowHours ? '00:' : ) + (player.options.showTimecodeFrameCount? '00:00:00':'00:00')) ) + '' +
'.appendTo(controls); }
t.durationD = t.controls.find('.mejs-duration');
media.addEventListener('timeupdate',function() { player.updateDuration(); }, false); },
updateCurrent: function() { var t = this;
if (t.currenttime) { t.currenttime.html(mejs.Utility.secondsToTimeCode(t.media.currentTime, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25)); } },
updateDuration: function() { var t = this;
//Toggle the long video class if the video is longer than an hour. t.container.toggleClass("mejs-long-video", t.media.duration > 3600);
if (t.durationD && (t.options.duration > 0 || t.media.duration)) { t.durationD.html(mejs.Utility.secondsToTimeCode(t.options.duration > 0 ? t.options.duration : t.media.duration, t.options.alwaysShowHours, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25)); } } });
})(mejs.$);
(function($) {
$.extend(mejs.MepDefaults, { muteText: mejs.i18n.t('Mute Toggle'), hideVolumeOnTouchDevices: true,
audioVolume: 'horizontal', videoVolume: 'vertical' });
$.extend(MediaElementPlayer.prototype, { buildvolume: function(player, controls, layers, media) {
// Android and iOS don't support volume controls if (mejs.MediaFeatures.hasTouch && this.options.hideVolumeOnTouchDevices) return;
var t = this, mode = (t.isVideo) ? t.options.videoVolume : t.options.audioVolume, mute = (mode == 'horizontal') ?
// horizontal version
$(' ' + ' ') .appendTo(controls) :
// vertical version
$(' ').appendTo(controls), volumeSlider = t.container.find('.mejs-volume-slider, .mejs-horizontal-volume-slider'), volumeTotal = t.container.find('.mejs-volume-total, .mejs-horizontal-volume-total'), volumeCurrent = t.container.find('.mejs-volume-current, .mejs-horizontal-volume-current'), volumeHandle = t.container.find('.mejs-volume-handle, .mejs-horizontal-volume-handle'),
positionVolumeHandle = function(volume, secondTry) {
if (!volumeSlider.is(':visible') && typeof secondTry == 'undefined') { volumeSlider.show(); positionVolumeHandle(volume, true); volumeSlider.hide() return; }
// correct to 0-1 volume = Math.max(0,volume); volume = Math.min(volume,1);
// ajust mute button style if (volume == 0) { mute.removeClass('mejs-mute').addClass('mejs-unmute'); } else { mute.removeClass('mejs-unmute').addClass('mejs-mute'); }
// position slider if (mode == 'vertical') { var
// height of the full size volume slider background totalHeight = volumeTotal.height(),
// top/left of full size volume slider background totalPosition = volumeTotal.position(),
// the new top position based on the current volume // 70% volume on 100px height == top:30px newTop = totalHeight - (totalHeight * volume);
// handle volumeHandle.css('top', Math.round(totalPosition.top + newTop - (volumeHandle.height() / 2)));
// show the current visibility volumeCurrent.height(totalHeight - newTop ); volumeCurrent.css('top', totalPosition.top + newTop); } else { var
// height of the full size volume slider background totalWidth = volumeTotal.width(),
// top/left of full size volume slider background totalPosition = volumeTotal.position(),
// the new left position based on the current volume newLeft = totalWidth * volume;
// handle volumeHandle.css('left', Math.round(totalPosition.left + newLeft - (volumeHandle.width() / 2)));
// rezize the current part of the volume bar volumeCurrent.width( Math.round(newLeft) ); } }, handleVolumeMove = function(e) {
var volume = null, totalOffset = volumeTotal.offset();
// calculate the new volume based on the moust position if (mode == 'vertical') {
var railHeight = volumeTotal.height(), totalTop = parseInt(volumeTotal.css('top').replace(/px/,),10), newY = e.pageY - totalOffset.top;
volume = (railHeight - newY) / railHeight;
// the controls just hide themselves (usually when mouse moves too far up) if (totalOffset.top == 0 || totalOffset.left == 0) return;
} else { var railWidth = volumeTotal.width(), newX = e.pageX - totalOffset.left;
volume = newX / railWidth; }
// ensure the volume isn't outside 0-1 volume = Math.max(0,volume); volume = Math.min(volume,1);
// position the slider and handle positionVolumeHandle(volume);
// set the media object (this will trigger the volumechanged event) if (volume == 0) { media.setMuted(true); } else { media.setMuted(false); } media.setVolume(volume); }, mouseIsDown = false, mouseIsOver = false;
// SLIDER
mute .hover(function() { volumeSlider.show(); mouseIsOver = true; }, function() { mouseIsOver = false;
if (!mouseIsDown && mode == 'vertical') { volumeSlider.hide(); } });
volumeSlider .bind('mouseover', function() { mouseIsOver = true; }) .bind('mousedown', function (e) { handleVolumeMove(e); t.globalBind('mousemove.vol', function(e) { handleVolumeMove(e); }); t.globalBind('mouseup.vol', function () { mouseIsDown = false; t.globalUnbind('.vol');
if (!mouseIsOver && mode == 'vertical') { volumeSlider.hide(); } }); mouseIsDown = true;
return false; });
// MUTE button
mute.find('button').click(function() {
media.setMuted( !media.muted );
});
// listen for volume change events from other sources media.addEventListener('volumechange', function(e) { if (!mouseIsDown) { if (media.muted) { positionVolumeHandle(0); mute.removeClass('mejs-mute').addClass('mejs-unmute'); } else { positionVolumeHandle(media.volume); mute.removeClass('mejs-unmute').addClass('mejs-mute'); } } }, false);
if (t.container.is(':visible')) { // set initial volume positionVolumeHandle(player.options.startVolume);
// mutes the media and sets the volume icon muted if the initial volume is set to 0
if (player.options.startVolume === 0) { media.setMuted(true); }
// shim gets the startvolume as a parameter, but we have to set it on the native <video> and <audio> elements if (media.pluginType === 'native') { media.setVolume(player.options.startVolume); } } } });
})(mejs.$);
(function($) {
$.extend(mejs.MepDefaults, { usePluginFullScreen: true, newWindowCallback: function() { return ;}, fullscreenText: mejs.i18n.t('Fullscreen') });
$.extend(MediaElementPlayer.prototype, {
isFullScreen: false,
isNativeFullScreen: false,
isInIframe: false,
buildfullscreen: function(player, controls, layers, media) {
if (!player.isVideo) return;
player.isInIframe = (window.location != window.parent.location);
// native events if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
// chrome doesn't alays fire this in an iframe var func = function(e) { if (player.isFullScreen) { if (mejs.MediaFeatures.isFullScreen()) { player.isNativeFullScreen = true; // reset the controls once we are fully in full screen player.setControlsSize(); } else { player.isNativeFullScreen = false; // when a user presses ESC // make sure to put the player back into place player.exitFullScreen(); } } };
if (mejs.MediaFeatures.hasMozNativeFullScreen) { player.globalBind(mejs.MediaFeatures.fullScreenEventName, func); } else { player.container.bind(mejs.MediaFeatures.fullScreenEventName, func); } }
var t = this, normalHeight = 0, normalWidth = 0, container = player.container, fullscreenBtn =
$(' ').appendTo(controls);
if (t.media.pluginType === 'native' || (!t.options.usePluginFullScreen && !mejs.MediaFeatures.isFirefox)) {
fullscreenBtn.click(function() { var isFullScreen = (mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || player.isFullScreen;
if (isFullScreen) { player.exitFullScreen(); } else { player.enterFullScreen(); } });
} else {
var hideTimeout = null, supportsPointerEvents = (function() { // TAKEN FROM MODERNIZR var element = document.createElement('x'), documentElement = document.documentElement, getComputedStyle = window.getComputedStyle, supports; if(!('pointerEvents' in element.style)){ return false; } element.style.pointerEvents = 'auto'; element.style.pointerEvents = 'x'; documentElement.appendChild(element); supports = getComputedStyle && getComputedStyle(element, ).pointerEvents === 'auto'; documentElement.removeChild(element); return !!supports; })();
//
if (supportsPointerEvents && !mejs.MediaFeatures.isOpera) { // opera doesn't allow this :(
// allows clicking through the fullscreen button and controls down directly to Flash
/* When a user puts his mouse over the fullscreen button, the controls are disabled So we put a div over the video and another one on iether side of the fullscreen button that caputre mouse movement and restore the controls once the mouse moves outside of the fullscreen button */
var fullscreenIsDisabled = false, restoreControls = function() { if (fullscreenIsDisabled) { // hide the hovers for (var i in hoverDivs) { hoverDivs[i].hide(); }
// restore the control bar fullscreenBtn.css('pointer-events', ); t.controls.css('pointer-events', );
// prevent clicks from pausing video t.media.removeEventListener('click', t.clickToPlayPauseCallback);
// store for later fullscreenIsDisabled = false; } }, hoverDivs = {}, hoverDivNames = ['top', 'left', 'right', 'bottom'], i, len, positionHoverDivs = function() { var fullScreenBtnOffsetLeft = fullscreenBtn.offset().left - t.container.offset().left, fullScreenBtnOffsetTop = fullscreenBtn.offset().top - t.container.offset().top, fullScreenBtnWidth = fullscreenBtn.outerWidth(true), fullScreenBtnHeight = fullscreenBtn.outerHeight(true), containerWidth = t.container.width(), containerHeight = t.container.height();
for (i in hoverDivs) { hoverDivs[i].css({position: 'absolute', top: 0, left: 0}); //, backgroundColor: '#f00'}); }
// over video, but not controls hoverDivs['top'] .width( containerWidth ) .height( fullScreenBtnOffsetTop );
// over controls, but not the fullscreen button hoverDivs['left'] .width( fullScreenBtnOffsetLeft ) .height( fullScreenBtnHeight ) .css({top: fullScreenBtnOffsetTop});
// after the fullscreen button hoverDivs['right'] .width( containerWidth - fullScreenBtnOffsetLeft - fullScreenBtnWidth ) .height( fullScreenBtnHeight ) .css({top: fullScreenBtnOffsetTop, left: fullScreenBtnOffsetLeft + fullScreenBtnWidth});
// under the fullscreen button hoverDivs['bottom'] .width( containerWidth ) .height( containerHeight - fullScreenBtnHeight - fullScreenBtnOffsetTop ) .css({top: fullScreenBtnOffsetTop + fullScreenBtnHeight}); };
t.globalBind('resize', function() { positionHoverDivs(); });
for (i = 0, len = hoverDivNames.length; i < len; i++) { hoverDivs[hoverDivNames[i]] = $('<div class="mejs-fullscreen-hover" />').appendTo(t.container).mouseover(restoreControls).hide(); }
// on hover, kill the fullscreen button's HTML handling, allowing clicks down to Flash fullscreenBtn.on('mouseover',function() {
if (!t.isFullScreen) {
var buttonPos = fullscreenBtn.offset(), containerPos = player.container.offset();
// move the button in Flash into place media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, false);
// allows click through fullscreenBtn.css('pointer-events', 'none'); t.controls.css('pointer-events', 'none');
// restore click-to-play t.media.addEventListener('click', t.clickToPlayPauseCallback);
// show the divs that will restore things for (i in hoverDivs) { hoverDivs[i].show(); }
positionHoverDivs();
fullscreenIsDisabled = true; }
});
// restore controls anytime the user enters or leaves fullscreen media.addEventListener('fullscreenchange', function(e) { t.isFullScreen = !t.isFullScreen; // don't allow plugin click to pause video - messes with // plugin's controls if (t.isFullScreen) { t.media.removeEventListener('click', t.clickToPlayPauseCallback); } else { t.media.addEventListener('click', t.clickToPlayPauseCallback); } restoreControls(); });
// the mouseout event doesn't work on the fullscren button, because we already killed the pointer-events
// so we use the document.mousemove event to restore controls when the mouse moves outside the fullscreen button
t.globalBind('mousemove', function(e) {
// if the mouse is anywhere but the fullsceen button, then restore it all if (fullscreenIsDisabled) {
var fullscreenBtnPos = fullscreenBtn.offset();
if (e.pageY < fullscreenBtnPos.top || e.pageY > fullscreenBtnPos.top + fullscreenBtn.outerHeight(true) ||
e.pageX < fullscreenBtnPos.left || e.pageX > fullscreenBtnPos.left + fullscreenBtn.outerWidth(true)
) {
fullscreenBtn.css('pointer-events', ); t.controls.css('pointer-events', );
fullscreenIsDisabled = false; } } });
} else {
// the hover state will show the fullscreen button in Flash to hover up and click
fullscreenBtn .on('mouseover', function() {
if (hideTimeout !== null) { clearTimeout(hideTimeout); delete hideTimeout; }
var buttonPos = fullscreenBtn.offset(), containerPos = player.container.offset();
media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, true);
}) .on('mouseout', function() {
if (hideTimeout !== null) { clearTimeout(hideTimeout); delete hideTimeout; }
hideTimeout = setTimeout(function() { media.hideFullscreenButton(); }, 1500);
});
}
}
player.fullscreenBtn = fullscreenBtn;
t.globalBind('keydown',function (e) { if (((mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || t.isFullScreen) && e.keyCode == 27) { player.exitFullScreen(); } });
},
cleanfullscreen: function(player) { player.exitFullScreen(); },
containerSizeTimeout: null,
enterFullScreen: function() {
var t = this;
// firefox+flash can't adjust plugin sizes without resetting :( if (t.media.pluginType !== 'native' && (mejs.MediaFeatures.isFirefox || t.options.usePluginFullScreen)) { //t.media.setFullscreen(true); //player.isFullScreen = true; return; }
// set it to not show scroll bars so 100% will work
$(document.documentElement).addClass('mejs-fullscreen');
// store sizing normalHeight = t.container.height(); normalWidth = t.container.width();
// attempt to do true fullscreen (Safari 5.1 and Firefox Nightly only for now) if (t.media.pluginType === 'native') { if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
mejs.MediaFeatures.requestFullScreen(t.container[0]); //return;
if (t.isInIframe) { // sometimes exiting from fullscreen doesn't work // notably in Chrome <iframe>. Fixed in version 17 setTimeout(function checkFullscreen() {
if (t.isNativeFullScreen) {
// check if the video is suddenly not really fullscreen if ($(window).width() !== screen.width) { // manually exit t.exitFullScreen(); } else { // test again setTimeout(checkFullscreen, 500); } }
}, 500);
}
} else if (mejs.MediaFeatures.hasSemiNativeFullScreen) { t.media.webkitEnterFullscreen(); return; } }
// check for iframe launch if (t.isInIframe) { var url = t.options.newWindowCallback(this);
if (url !== ) {
// launch immediately if (!mejs.MediaFeatures.hasTrueNativeFullScreen) { t.pause(); window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no'); return; } else { setTimeout(function() { if (!t.isNativeFullScreen) { t.pause(); window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no'); } }, 250); } }
}
// full window code
// make full size t.container .addClass('mejs-container-fullscreen') .width('100%') .height('100%'); //.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});
// Only needed for safari 5.1 native full screen, can cause display issues elsewhere // Actually, it seems to be needed for IE8, too //if (mejs.MediaFeatures.hasTrueNativeFullScreen) { t.containerSizeTimeout = setTimeout(function() { t.container.css({width: '100%', height: '100%'}); t.setControlsSize(); }, 500); //}
if (t.media.pluginType === 'native') { t.$media .width('100%') .height('100%'); } else { t.container.find('.mejs-shim') .width('100%') .height('100%');
//if (!mejs.MediaFeatures.hasTrueNativeFullScreen) { t.media.setVideoSize($(window).width(),$(window).height()); //} }
t.layers.children('div') .width('100%') .height('100%');
if (t.fullscreenBtn) { t.fullscreenBtn .removeClass('mejs-fullscreen') .addClass('mejs-unfullscreen'); }
t.setControlsSize(); t.isFullScreen = true; },
exitFullScreen: function() {
var t = this;
// Prevent container from attempting to stretch a second time clearTimeout(t.containerSizeTimeout);
// firefox can't adjust plugins if (t.media.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) { t.media.setFullscreen(false); //player.isFullScreen = false; return; }
// come outo of native fullscreen if (mejs.MediaFeatures.hasTrueNativeFullScreen && (mejs.MediaFeatures.isFullScreen() || t.isFullScreen)) { mejs.MediaFeatures.cancelFullScreen(); }
// restore scroll bars to document
$(document.documentElement).removeClass('mejs-fullscreen');
t.container .removeClass('mejs-container-fullscreen') .width(normalWidth) .height(normalHeight); //.css({position: , left: , top: , right: , bottom: , overflow: 'inherit', width: normalWidth + 'px', height: normalHeight + 'px', 'z-index': 1});
if (t.media.pluginType === 'native') { t.$media .width(normalWidth) .height(normalHeight); } else { t.container.find('.mejs-shim') .width(normalWidth) .height(normalHeight);
t.media.setVideoSize(normalWidth, normalHeight); }
t.layers.children('div') .width(normalWidth) .height(normalHeight);
t.fullscreenBtn .removeClass('mejs-unfullscreen') .addClass('mejs-fullscreen');
t.setControlsSize(); t.isFullScreen = false; } });
})(mejs.$);
(function($) {
// add extra default options $.extend(mejs.MepDefaults, { // this will automatically turn on a <track> startLanguage: ,
tracksText: mejs.i18n.t('Captions/Subtitles'),
// option to remove the [cc] button when no <track kind="subtitles"> are present hideCaptionsButtonWhenEmpty: true,
// If true and we only have one track, change captions to popup toggleCaptionsButtonWhenOnlyOne: false,
// #id or .class slidesSelector: });
$.extend(MediaElementPlayer.prototype, {
hasChapters: false,
buildtracks: function(player, controls, layers, media) { if (player.tracks.length == 0) return;
var t = this, i, options = ;
if (t.domNode.textTracks) { // if browser will do native captions, prefer mejs captions, loop through tracks and hide for (var i = t.domNode.textTracks.length - 1; i >= 0; i--) { t.domNode.textTracks[i].mode = "hidden"; } } player.chapters =
$('').prependTo(layers).hide(); player.captions =
$(' ').prependTo(layers).hide(); player.captionsText = player.captions.find('.mejs-captions-text'); player.captionsButton =
$(' ').appendTo(controls);
var subtitleCount = 0;
for (i=0; i<player.tracks.length; i++) {
if (player.tracks[i].kind == 'subtitles') {
subtitleCount++;
}
}
// if only one language then just make the button a toggle if (t.options.toggleCaptionsButtonWhenOnlyOne && subtitleCount == 1){ // click player.captionsButton.on('click',function() { if (player.selectedTrack == null) { var lang = player.tracks[0].srclang; } else { var lang = 'none'; } player.setTrack(lang); }); } else { // hover player.captionsButton.hover(function() { $(this).find('.mejs-captions-selector').css('visibility','visible'); }, function() { $(this).find('.mejs-captions-selector').css('visibility','hidden'); })
// handle clicks to the language radio buttons .on('click','input[type=radio]',function() { lang = this.value; player.setTrack(lang); });
}
if (!player.options.alwaysShowControls) { // move with controls player.container .bind('controlsshown', function () { // push captions above controls player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
}) .bind('controlshidden', function () { if (!media.paused) { // move back to normal place player.container.find('.mejs-captions-position').removeClass('mejs-captions-position-hover'); } }); } else { player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover'); }
player.trackToLoad = -1; player.selectedTrack = null; player.isLoadingTrack = false;
// add to list for (i=0; i<player.tracks.length; i++) { if (player.tracks[i].kind == 'subtitles') { player.addTrackButton(player.tracks[i].srclang, player.tracks[i].label); } }
// start loading tracks player.loadNextTrack();
media.addEventListener('timeupdate',function(e) {
player.displayCaptions();
}, false);
if (player.options.slidesSelector != ) { player.slidesContainer = $(player.options.slidesSelector);
media.addEventListener('timeupdate',function(e) { player.displaySlides(); }, false);
}
media.addEventListener('loadedmetadata', function(e) { player.displayChapters(); }, false);
player.container.hover( function () { // chapters if (player.hasChapters) { player.chapters.css('visibility','visible'); player.chapters.fadeIn(200).height(player.chapters.find('.mejs-chapter').outerHeight()); } }, function () { if (player.hasChapters && !media.paused) { player.chapters.fadeOut(200, function() { $(this).css('visibility','hidden'); $(this).css('display','block'); }); } });
// check for autoplay if (player.node.getAttribute('autoplay') !== null) { player.chapters.css('visibility','hidden'); } },
setTrack: function(lang){
var t = this, i;
if (lang == 'none') { t.selectedTrack = null; t.captionsButton.removeClass('mejs-captions-enabled'); } else { for (i=0; i<t.tracks.length; i++) { if (t.tracks[i].srclang == lang) { if (t.selectedTrack == null) t.captionsButton.addClass('mejs-captions-enabled'); t.selectedTrack = t.tracks[i]; t.captions.attr('lang', t.selectedTrack.srclang); t.displayCaptions(); break; } } } },
loadNextTrack: function() { var t = this;
t.trackToLoad++; if (t.trackToLoad < t.tracks.length) { t.isLoadingTrack = true; t.loadTrack(t.trackToLoad); } else { // add done? t.isLoadingTrack = false;
t.checkForTracks(); } },
loadTrack: function(index){ var t = this, track = t.tracks[index], after = function() {
track.isLoaded = true;
// create button //t.addTrackButton(track.srclang); t.enableTrackButton(track.srclang, track.label);
t.loadNextTrack();
};
$.ajax({
url: track.src,
dataType: "text",
success: function(d) {
// parse the loaded file if (typeof d == "string" && (/<tt\s+xml/ig).exec(d)) { track.entries = mejs.TrackFormatParser.dfxp.parse(d); } else { track.entries = mejs.TrackFormatParser.webvvt.parse(d); }
after();
if (track.kind == 'chapters') { t.media.addEventListener('play', function(e) { if (t.media.duration > 0) { t.displayChapters(track); } }, false); }
if (track.kind == 'slides') { t.setupSlides(track); } }, error: function() { t.loadNextTrack(); } }); },
enableTrackButton: function(lang, label) { var t = this;
if (label === ) { label = mejs.language.codes[lang] || lang; }
t.captionsButton .find('input[value=' + lang + ']') .prop('disabled',false) .siblings('label') .html( label );
// auto select if (t.options.startLanguage == lang) { $('#' + t.id + '_captions_' + lang).click(); }
t.adjustLanguageBox(); },
addTrackButton: function(lang, label) { var t = this; if (label === ) { label = mejs.language.codes[lang] || lang; }
t.captionsButton.find('ul').append(
$(');
t.adjustLanguageBox();
// remove this from the dropdownlist (if it exists) t.container.find('.mejs-captions-translations option[value=' + lang + ']').remove(); },
adjustLanguageBox:function() { var t = this; // adjust the size of the outer box t.captionsButton.find('.mejs-captions-selector').height( t.captionsButton.find('.mejs-captions-selector ul').outerHeight(true) + t.captionsButton.find('.mejs-captions-translations').outerHeight(true) ); },
checkForTracks: function() { var t = this, hasSubtitles = false;
// check if any subtitles if (t.options.hideCaptionsButtonWhenEmpty) { for (i=0; i<t.tracks.length; i++) { if (t.tracks[i].kind == 'subtitles') { hasSubtitles = true; break; } }
if (!hasSubtitles) { t.captionsButton.hide(); t.setControlsSize(); } } },
displayCaptions: function() {
if (typeof this.tracks == 'undefined') return;
var t = this, i, track = t.selectedTrack;
if (track != null && track.isLoaded) { for (i=0; i<track.entries.times.length; i++) { if (t.media.currentTime >= track.entries.times[i].start && t.media.currentTime <= track.entries.times[i].stop){ t.captionsText.html(track.entries.text[i]); t.captions.show().height(0); return; // exit out if one is visible; } } t.captions.hide(); } else { t.captions.hide(); } },
setupSlides: function(track) { var t = this;
t.slides = track; t.slides.entries.imgs = [t.slides.entries.text.length]; t.showSlide(0);
},
showSlide: function(index) { if (typeof this.tracks == 'undefined' || typeof this.slidesContainer == 'undefined') { return; }
var t = this, url = t.slides.entries.text[index], img = t.slides.entries.imgs[index];
if (typeof img == 'undefined' || typeof img.fadeIn == 'undefined') {
t.slides.entries.imgs[index] = img = $('<img src="' + url + '">') .on('load', function() { img.appendTo(t.slidesContainer) .hide() .fadeIn() .siblings(':visible') .fadeOut();
});
} else {
if (!img.is(':visible') && !img.is(':animated')) {
//
img.fadeIn() .siblings(':visible') .fadeOut(); } }
},
displaySlides: function() {
if (typeof this.slides == 'undefined') return;
var t = this, slides = t.slides, i;
for (i=0; i<slides.entries.times.length; i++) { if (t.media.currentTime >= slides.entries.times[i].start && t.media.currentTime <= slides.entries.times[i].stop){
t.showSlide(i);
return; // exit out if one is visible; } } },
displayChapters: function() { var t = this, i;
for (i=0; i<t.tracks.length; i++) { if (t.tracks[i].kind == 'chapters' && t.tracks[i].isLoaded) { t.drawChapters(t.tracks[i]); t.hasChapters = true; break; } } },
drawChapters: function(chapters) { var t = this, i, dur, //width, //left, percent = 0, usedPercent = 0;
t.chapters.empty();
for (i=0; i<chapters.entries.times.length; i++) { dur = chapters.entries.times[i].stop - chapters.entries.times[i].start; percent = Math.floor(dur / t.media.duration * 100); if (percent + usedPercent > 100 || // too large i == chapters.entries.times.length-1 && percent + usedPercent < 100) // not going to fill it in { percent = 100 - usedPercent; } //width = Math.floor(t.width * dur / t.media.duration); //left = Math.floor(t.width * chapters.entries.times[i].start / t.media.duration); //if (left + width > t.width) { // width = t.width - left; //}
t.chapters.append( $(
''' + chapters.entries.text[i] + '' + '' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].start) + '–' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].stop) + '' +
'usedPercent += percent; }
t.chapters.find('div.mejs-chapter').click(function() { t.media.setCurrentTime( parseFloat( $(this).attr('rel') ) ); if (t.media.paused) { t.media.play(); } });
t.chapters.show(); } });
mejs.language = { codes: { af:'Afrikaans', sq:'Albanian', ar:'Arabic', be:'Belarusian', bg:'Bulgarian', ca:'Catalan', zh:'Chinese', 'zh-cn':'Chinese Simplified', 'zh-tw':'Chinese Traditional', hr:'Croatian', cs:'Czech', da:'Danish', nl:'Dutch', en:'English', et:'Estonian', tl:'Filipino', fi:'Finnish', fr:'French', gl:'Galician', de:'German', el:'Greek', ht:'Haitian Creole', iw:'Hebrew', hi:'Hindi', hu:'Hungarian', is:'Icelandic', id:'Indonesian', ga:'Irish', it:'Italian', ja:'Japanese', ko:'Korean', lv:'Latvian', lt:'Lithuanian', mk:'Macedonian', ms:'Malay', mt:'Maltese', no:'Norwegian', fa:'Persian', pl:'Polish', pt:'Portuguese', //'pt-pt':'Portuguese (Portugal)', ro:'Romanian', ru:'Russian', sr:'Serbian', sk:'Slovak', sl:'Slovenian', es:'Spanish', sw:'Swahili', sv:'Swedish', tl:'Tagalog', th:'Thai', tr:'Turkish', uk:'Ukrainian', vi:'Vietnamese', cy:'Welsh', yi:'Yiddish' } };
/* Parses WebVVT format which should be formatted as ================================ WEBVTT
1 00:00:01,1 --> 00:00:05,000 A line of text
2 00:01:15,1 --> 00:02:05,000 A second line of text
===============================
Adapted from: http://www.delphiki.com/html5/playr */ mejs.TrackFormatParser = { webvvt: { // match start "chapter-" (or anythingelse) pattern_identifier: /^([a-zA-z]+-)?[0-9]+$/, pattern_timecode: /^([0-9]{2}:[0-9]{2}:[0-9]{2}([,.][0-9]{1,3})?) --\> ([0-9]{2}:[0-9]{2}:[0-9]{2}([,.][0-9]{3})?)(.*)$/,
parse: function(trackText) { var i = 0, lines = mejs.TrackFormatParser.split2(trackText, /\r?\n/), entries = {text:[], times:[]}, timecode, text; for(; i<lines.length; i++) { // check for the line number if (this.pattern_identifier.exec(lines[i])){ // skip to the next line where the start --> end time code should be i++; timecode = this.pattern_timecode.exec(lines[i]);
if (timecode && i<lines.length){ i++; // grab all the (possibly multi-line) text that follows text = lines[i]; i++; while(lines[i] !== && i<lines.length){ text = text + '\n' + lines[i]; i++; } text = $.trim(text).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>"); // Text is in a different array so I can use .join entries.text.push(text); entries.times.push( { start: (mejs.Utility.convertSMPTEtoSeconds(timecode[1]) == 0) ? 0.200 : mejs.Utility.convertSMPTEtoSeconds(timecode[1]), stop: mejs.Utility.convertSMPTEtoSeconds(timecode[3]), settings: timecode[5] }); } } } return entries; } }, // Thanks to Justin Capella: https://github.com/johndyer/mediaelement/pull/420 dfxp: { parse: function(trackText) { trackText = $(trackText).filter("tt"); var i = 0, container = trackText.children("div").eq(0), lines = container.find("p"), styleNode = trackText.find("#" + container.attr("style")), styles, begin, end, text, entries = {text:[], times:[]};
if (styleNode.length) {
var attributes = styleNode.removeAttr("id").get(0).attributes;
if (attributes.length) {
styles = {};
for (i = 0; i < attributes.length; i++) {
styles[attributes[i].name.split(":")[1]] = attributes[i].value;
}
}
}
for(i = 0; i<lines.length; i++) { var style; var _temp_times = { start: null, stop: null, style: null }; if (lines.eq(i).attr("begin")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("begin")); if (!_temp_times.start && lines.eq(i-1).attr("end")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i-1).attr("end")); if (lines.eq(i).attr("end")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("end")); if (!_temp_times.stop && lines.eq(i+1).attr("begin")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i+1).attr("begin")); if (styles) { style = ""; for (var _style in styles) { style += _style + ":" + styles[_style] + ";"; } } if (style) _temp_times.style = style; if (_temp_times.start == 0) _temp_times.start = 0.200; entries.times.push(_temp_times); 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>"); entries.text.push(text); if (entries.times.start == 0) entries.times.start = 2; } return entries; } }, split2: function (text, regex) { // normal version for compliant browsers // see below for IE fix return text.split(regex); } };
// test for browsers with bad String.split method. if ('x\n\ny'.split(/\n/gi).length != 3) { // add super slow IE8 and below version mejs.TrackFormatParser.split2 = function(text, regex) { var parts = [], chunk = , i;
for (i=0; i<text.length; i++) { chunk += text.substring(i,i+1); if (regex.test(chunk)) { parts.push(chunk.replace(regex, )); chunk = ; } } parts.push(chunk); return parts; } }
})(mejs.$);
/*
- ContextMenu Plugin
- /
(function($) {
$.extend(mejs.MepDefaults, { 'contextMenuItems': [ // demo of a fullscreen option { render: function(player) {
// check for fullscreen plugin if (typeof player.enterFullScreen == 'undefined') return null;
if (player.isFullScreen) { return mejs.i18n.t('Turn off Fullscreen'); } else { return mejs.i18n.t('Go Fullscreen'); } }, click: function(player) { if (player.isFullScreen) { player.exitFullScreen(); } else { player.enterFullScreen(); } } } , // demo of a mute/unmute button { render: function(player) { if (player.media.muted) { return mejs.i18n.t('Unmute'); } else { return mejs.i18n.t('Mute'); } }, click: function(player) { if (player.media.muted) { player.setMuted(false); } else { player.setMuted(true); } } }, // separator { isSeparator: true } , // demo of simple download video { render: function(player) { return mejs.i18n.t('Download Video'); }, click: function(player) { window.location.href = player.media.currentSrc; } } ]} );
$.extend(MediaElementPlayer.prototype, {
buildcontextmenu: function(player, controls, layers, media) {
// create context menu
player.contextMenu = $('').appendTo($('body')) .hide();
// create events for showing context menu player.container.bind('contextmenu', function(e) { if (player.isContextMenuEnabled) { e.preventDefault(); player.renderContextMenu(e.clientX-1, e.clientY-1); return false; } }); player.container.bind('click', function() { player.contextMenu.hide(); }); player.contextMenu.bind('mouseleave', function() {
// player.startContextMenuTimer();
}); },
cleancontextmenu: function(player) { player.contextMenu.remove(); },
isContextMenuEnabled: true, enableContextMenu: function() { this.isContextMenuEnabled = true; }, disableContextMenu: function() { this.isContextMenuEnabled = false; },
contextMenuTimeout: null, startContextMenuTimer: function() { //
var t = this;
t.killContextMenuTimer();
t.contextMenuTimer = setTimeout(function() { t.hideContextMenu(); t.killContextMenuTimer(); }, 750); }, killContextMenuTimer: function() { var timer = this.contextMenuTimer;
//
if (timer != null) { clearTimeout(timer); delete timer; timer = null; } },
hideContextMenu: function() { this.contextMenu.hide(); },
renderContextMenu: function(x,y) {
// alway re-render the items so that things like "turn fullscreen on" and "turn fullscreen off" are always written correctly var t = this, html = , items = t.options.contextMenuItems;
for (var i=0, il=items.length; i<il; i++) {
if (items[i].isSeparator) {
html += '';} else {
var rendered = items[i].render(t);
// render can return null if the item doesn't need to be used at the moment if (rendered != null) {
html += ' ';} } }
// position and show the context menu t.contextMenu .empty() .append($(html)) .css({top:y, left:x}) .show();
// bind events t.contextMenu.find('.mejs-contextmenu-item').each(function() {
// which one is this? var $dom = $(this), itemIndex = parseInt( $dom.data('itemindex'), 10 ), item = t.options.contextMenuItems[itemIndex];
// bind extra functionality? if (typeof item.show != 'undefined') item.show( $dom , t);
// bind click action $dom.click(function() { // perform click action if (typeof item.click != 'undefined') item.click(t);
// close t.contextMenu.hide(); }); });
// stop the controls from hiding setTimeout(function() { t.killControlsTimer('rev3'); }, 100);
} });
})(mejs.$); /**
* Postroll plugin */
(function($) {
$.extend(mejs.MepDefaults, { postrollCloseText: mejs.i18n.t('Close') });
// Postroll $.extend(MediaElementPlayer.prototype, { buildpostroll: function(player, controls, layers, media) { var t = this, postrollLink = t.container.find('link[rel="postroll"]').attr('href');
if (typeof postrollLink !== 'undefined') { player.postroll =
$('t.media.addEventListener('ended', function (e) { $.ajax({ dataType: 'html', url: postrollLink, success: function (data, textStatus) { layers.find('.mejs-postroll-layer-content').html(data); } }); player.postroll.show(); }, false); } } });
})(mejs.$);