05d4a07312
git-subtree-dir: ui git-subtree-mainline:3886c7bf2c
git-subtree-split:efc28c6644
2063 lines
60 KiB
JavaScript
Executable file
2063 lines
60 KiB
JavaScript
Executable file
/*!
|
|
*
|
|
* 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-2014, John Dyer (http://j.hn)
|
|
* License: MIT
|
|
*
|
|
*/
|
|
// Namespace
|
|
var mejs = mejs || {};
|
|
|
|
// version number
|
|
mejs.version = '2.21.2';
|
|
|
|
|
|
// 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/dailymotion', 'video/x-dailymotion', 'application/x-mpegURL']}
|
|
// '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;
|
|
},
|
|
/*
|
|
* Calculate the time format to use. We have a default format set in the
|
|
* options but it can be imcomplete. We ajust it according to the media
|
|
* duration.
|
|
*
|
|
* We support format like 'hh:mm:ss:ff'.
|
|
*/
|
|
calculateTimeFormat: function(time, options, fps) {
|
|
if (time < 0) {
|
|
time = 0;
|
|
}
|
|
|
|
if(typeof fps == 'undefined') {
|
|
fps = 25;
|
|
}
|
|
|
|
var format = options.timeFormat,
|
|
firstChar = format[0],
|
|
firstTwoPlaces = (format[1] == format[0]),
|
|
separatorIndex = firstTwoPlaces? 2: 1,
|
|
separator = ':',
|
|
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)),
|
|
lis = [
|
|
[frames, 'f'],
|
|
[seconds, 's'],
|
|
[minutes, 'm'],
|
|
[hours, 'h']
|
|
];
|
|
|
|
// Try to get the separator from the format
|
|
if (format.length < separatorIndex) {
|
|
separator = format[separatorIndex];
|
|
}
|
|
|
|
var required = false;
|
|
|
|
for (var i=0, len=lis.length; i < len; i++) {
|
|
if (format.indexOf(lis[i][1]) !== -1) {
|
|
required=true;
|
|
}
|
|
else if (required) {
|
|
var hasNextValue = false;
|
|
for (var j=i; j < len; j++) {
|
|
if (lis[j][0] > 0) {
|
|
hasNextValue = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (! hasNextValue) {
|
|
break;
|
|
}
|
|
|
|
if (!firstTwoPlaces) {
|
|
format = firstChar + format;
|
|
}
|
|
format = lis[i][1] + separator + format;
|
|
if (firstTwoPlaces) {
|
|
format = lis[i][1] + format;
|
|
}
|
|
firstChar = lis[i][1];
|
|
}
|
|
}
|
|
options.currentTimeFormat = format;
|
|
},
|
|
/*
|
|
* Prefix the given number by zero if it is lower than 10.
|
|
*/
|
|
twoDigitsString: function(n) {
|
|
if (n < 10) {
|
|
return '0' + n;
|
|
}
|
|
return String(n);
|
|
},
|
|
secondsToTimeCode: function(time, options) {
|
|
if (time < 0) {
|
|
time = 0;
|
|
}
|
|
|
|
// Maintain backward compatibility with method signature before v2.18.
|
|
if (typeof options !== 'object') {
|
|
var format = 'm:ss';
|
|
format = arguments[1] ? 'hh:mm:ss' : format; // forceHours
|
|
format = arguments[2] ? format + ':ff' : format; // showFrameCount
|
|
|
|
options = {
|
|
currentTimeFormat: format,
|
|
framesPerSecond: arguments[3] || 25
|
|
};
|
|
}
|
|
|
|
var fps = options.framesPerSecond;
|
|
if(typeof fps === 'undefined') {
|
|
fps = 25;
|
|
}
|
|
|
|
var format = options.currentTimeFormat,
|
|
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));
|
|
lis = [
|
|
[frames, 'f'],
|
|
[seconds, 's'],
|
|
[minutes, 'm'],
|
|
[hours, 'h']
|
|
];
|
|
|
|
var res = format;
|
|
for (i=0,len=lis.length; i < len; i++) {
|
|
res = res.replace(lis[i][1]+lis[i][1], this.twoDigitsString(lis[i][0]));
|
|
res = res.replace(lis[i][1], lis[i][0]);
|
|
}
|
|
return res;
|
|
},
|
|
|
|
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);
|
|
}
|
|
},
|
|
determineScheme: function(url) {
|
|
if (url && url.indexOf("://") != -1) {
|
|
return url.substr(0, url.indexOf("://")+3);
|
|
}
|
|
return "//"; // let user agent figure this out
|
|
}
|
|
};
|
|
|
|
|
|
// 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().indexOf("microsoft") != -1 || nav.appName.toLowerCase().match(/trident/gi) !== null);
|
|
t.isChrome = (ua.match(/chrome/gi) !== null);
|
|
t.isChromium = (ua.match(/chromium/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); // this breaks iOS 7
|
|
|
|
// Borrowed from `Modernizr.svgasimg`, sources:
|
|
// - https://github.com/Modernizr/Modernizr/issues/687
|
|
// - https://github.com/Modernizr/Modernizr/pull/1209/files
|
|
t.svgAsImg = !!document.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#Image', '1.1');
|
|
|
|
// 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;
|
|
}
|
|
|
|
t.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;
|
|
})();
|
|
|
|
|
|
// Older versions of Firefox can't move plugins around without it resetting,
|
|
t.hasFirefoxPluginMovingProblem = false;
|
|
|
|
// detect native JavaScript fullscreen (Safari/Firefox only, Chrome still fails)
|
|
|
|
// iOS
|
|
t.hasiOSFullScreen = (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.hasiOSFullScreen = 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 (t.hasMozNativeFullScreen) {
|
|
return d.mozFullScreen;
|
|
|
|
} else if (t.hasWebkitNativeFullScreen) {
|
|
return d.webkitIsFullScreen;
|
|
|
|
} else if (t.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.hasiOSFullScreen && ua.match(/mac os x 10_5/i)) {
|
|
t.hasNativeFullScreen = false;
|
|
t.hasiOSFullScreen = 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.pluginType == 'vimeo') {
|
|
this.pluginApi.playVideo();
|
|
} else {
|
|
this.pluginApi.playMedia();
|
|
}
|
|
this.paused = false;
|
|
}
|
|
},
|
|
load: function () {
|
|
if (this.pluginApi != null) {
|
|
if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
|
|
} else {
|
|
this.pluginApi.loadMedia();
|
|
}
|
|
|
|
this.paused = false;
|
|
}
|
|
},
|
|
pause: function () {
|
|
if (this.pluginApi != null) {
|
|
if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
|
|
if( this.pluginApi.getPlayerState() == 1 ) {
|
|
this.pluginApi.pauseVideo();
|
|
}
|
|
} else {
|
|
this.pluginApi.pauseMedia();
|
|
}
|
|
|
|
|
|
this.paused = true;
|
|
}
|
|
},
|
|
stop: function () {
|
|
if (this.pluginApi != null) {
|
|
if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
|
|
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(media.src);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
},
|
|
setCurrentTime: function (time) {
|
|
if (this.pluginApi != null) {
|
|
if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
|
|
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({type:'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 && 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 (var i = 0; i < callbacks.length; i++) {
|
|
if (callbacks[i] === callback) {
|
|
this.events[eventName].splice(i, 1);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
dispatchEvent: function (event) {
|
|
var i,
|
|
args,
|
|
callbacks = this.events[event.type];
|
|
|
|
if (callbacks) {
|
|
for (i = 0; i < callbacks.length; i++) {
|
|
callbacks[i].apply(this, [event]);
|
|
}
|
|
}
|
|
},
|
|
// 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);
|
|
}
|
|
};
|
|
|
|
/*
|
|
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: '',
|
|
// set to 'always' for CDN version
|
|
flashScriptAccess: 'sameDomain',
|
|
// 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 = {},
|
|
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 mejs.MediaElementDefaults) {
|
|
options[prop] = mejs.MediaElementDefaults[prop];
|
|
}
|
|
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) : '';
|
|
playback.scheme = mejs.Utility.determineScheme(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'), scheme: ''},
|
|
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' : '';
|
|
};
|
|
}
|
|
|
|
// special case for Chromium to specify natively supported video codecs (i.e. WebM and Theora)
|
|
if (mejs.MediaFeatures.isChromium) {
|
|
htmlMediaElement.canPlayType = function(type) {
|
|
return (type.match(/video\/(webm|ogv|ogg)/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 (mediaFiles[i].type == "video/m3u8" || 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/, '') !== ''
|
|
// special case for m4a supported by detecting mp4 support
|
|
|| htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/m4a/,'mp4')).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.toLowerCase() == pluginInfo.types[l].toLowerCase()) {
|
|
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) {
|
|
// 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(),
|
|
av = /(mp4|m4v|ogg|ogv|m3u8|webm|webmv|flv|wmv|mpeg|mov)/gi.test(ext) ? 'video/' : 'audio/';
|
|
return this.getTypeFromExtension(ext, av);
|
|
},
|
|
|
|
getTypeFromExtension: function(ext, av) {
|
|
av = av || '';
|
|
|
|
switch (ext) {
|
|
case 'mp4':
|
|
case 'm4v':
|
|
case 'm4a':
|
|
case 'f4v':
|
|
case 'f4a':
|
|
return av + 'mp4';
|
|
case 'flv':
|
|
return av + 'x-flv';
|
|
case 'webm':
|
|
case 'webma':
|
|
case 'webmv':
|
|
return av + 'webm';
|
|
case 'ogg':
|
|
case 'oga':
|
|
case 'ogv':
|
|
return av + 'ogg';
|
|
case 'm3u8':
|
|
return 'application/x-mpegurl';
|
|
case 'ts':
|
|
return av + 'mp2t';
|
|
default:
|
|
return av + ext;
|
|
}
|
|
},
|
|
|
|
createErrorMessage: function(playback, options, poster) {
|
|
var
|
|
htmlMediaElement = playback.htmlMediaElement,
|
|
errorContainer = document.createElement('div'),
|
|
errorContent = options.customError;
|
|
|
|
errorContainer.className = 'me-cannotplay';
|
|
|
|
try {
|
|
errorContainer.style.width = htmlMediaElement.width + 'px';
|
|
errorContainer.style.height = htmlMediaElement.height + 'px';
|
|
} catch (e) {}
|
|
|
|
if (!errorContent) {
|
|
errorContent = '<a href="' + playback.url + '">';
|
|
|
|
if (poster !== '') {
|
|
errorContent += '<img src="' + poster + '" width="100%" height="100%" alt="" />';
|
|
}
|
|
|
|
errorContent += '<span>' + mejs.i18n.t('Download File') + '</span></a>';
|
|
}
|
|
|
|
errorContainer.innerHTML = errorContent;
|
|
|
|
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) {
|
|
pluginMediaElement.setAttribute(attribute.name, attribute.value);
|
|
}
|
|
}
|
|
|
|
// check for placement inside a <p> tag (sometimes WYSIWYG editors do this)
|
|
node = htmlMediaElement.parentNode;
|
|
|
|
while (node !== null && node.tagName != null && node.tagName.toLowerCase() !== 'body' &&
|
|
node.parentNode != null && node.parentNode.tagName != null && node.parentNode.constructor != null && node.parentNode.constructor.name === "ShadowRoot") {
|
|
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;
|
|
|
|
// 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]);
|
|
}
|
|
|
|
if (playback.method === 'flash' || playback.method === 'silverlight') {
|
|
|
|
// 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);
|
|
}
|
|
|
|
// call from plugin
|
|
window[pluginid + '_init'] = function() {
|
|
switch (pluginMediaElement.pluginType) {
|
|
case 'flash':
|
|
pluginMediaElement.pluginElement = pluginMediaElement.pluginApi = document.getElementById(pluginid);
|
|
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);
|
|
}
|
|
}
|
|
|
|
// event call from plugin
|
|
window[pluginid + '_event'] = function(eventName, values) {
|
|
|
|
var
|
|
e,
|
|
i,
|
|
bufferedTime;
|
|
|
|
// 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);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
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 + '?' + (new Date().getTime()) + '" />' +
|
|
'<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="' + options.flashScriptAccess + '" />' +
|
|
'<param name="allowFullScreen" value="true" />' +
|
|
'<param name="scale" value="default" />' +
|
|
'</object>';
|
|
|
|
} else {
|
|
|
|
container.innerHTML =
|
|
'<embed id="' + pluginid + '" name="' + pluginid + '" ' +
|
|
'play="true" ' +
|
|
'loop="false" ' +
|
|
'quality="high" ' +
|
|
'bgcolor="#000000" ' +
|
|
'wmode="transparent" ' +
|
|
'allowScriptAccess="' + options.flashScriptAccess + '" ' +
|
|
'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 + '" ' +
|
|
'scale="default"' +
|
|
'class="mejs-shim"></embed>';
|
|
}
|
|
break;
|
|
|
|
case 'youtube':
|
|
|
|
|
|
var videoId;
|
|
// youtu.be url from share button
|
|
if (playback.url.lastIndexOf("youtu.be") != -1) {
|
|
videoId = playback.url.substr(playback.url.lastIndexOf('/')+1);
|
|
if (videoId.indexOf('?') != -1) {
|
|
videoId = videoId.substr(0, videoId.indexOf('?'));
|
|
}
|
|
}
|
|
else {
|
|
videoId = playback.url.substr(playback.url.lastIndexOf('=')+1);
|
|
}
|
|
youtubeSettings = {
|
|
container: container,
|
|
containerId: container.id,
|
|
pluginMediaElement: pluginMediaElement,
|
|
pluginId: pluginid,
|
|
videoId: videoId,
|
|
height: height,
|
|
width: width,
|
|
scheme: playback.scheme
|
|
};
|
|
|
|
// favor iframe version of YouTube
|
|
if (window.postMessage) {
|
|
mejs.YouTubeApi.enqueueIframe(youtubeSettings);
|
|
} else if (mejs.PluginDetector.hasPluginVersion('flash', [10,0,0]) ) {
|
|
mejs.YouTubeApi.createFlash(youtubeSettings, options);
|
|
}
|
|
|
|
break;
|
|
|
|
// DEMO Code. Does NOT work.
|
|
case 'vimeo':
|
|
var player_id = pluginid + "_player";
|
|
pluginMediaElement.vimeoid = playback.url.substr(playback.url.lastIndexOf('/')+1);
|
|
|
|
container.innerHTML ='<iframe src="' + playback.scheme + 'player.vimeo.com/video/' + pluginMediaElement.vimeoid + '?api=1&portrait=0&byline=0&title=0&player_id=' + player_id + '" width="' + width +'" height="' + height +'" frameborder="0" class="mejs-shim" id="' + player_id + '" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>';
|
|
if (typeof($f) == 'function') { // froogaloop available
|
|
var player = $f(container.childNodes[0]),
|
|
playerState = -1;
|
|
|
|
player.addEvent('ready', function() {
|
|
|
|
player.playVideo = function() {
|
|
player.api( 'play' );
|
|
}
|
|
player.stopVideo = function() {
|
|
player.api( 'unload' );
|
|
}
|
|
player.pauseVideo = function() {
|
|
player.api( 'pause' );
|
|
}
|
|
player.seekTo = function( seconds ) {
|
|
player.api( 'seekTo', seconds );
|
|
}
|
|
player.setVolume = function( volume ) {
|
|
player.api( 'setVolume', volume );
|
|
}
|
|
player.setMuted = function( muted ) {
|
|
if( muted ) {
|
|
player.lastVolume = player.api( 'getVolume' );
|
|
player.api( 'setVolume', 0 );
|
|
} else {
|
|
player.api( 'setVolume', player.lastVolume );
|
|
delete player.lastVolume;
|
|
}
|
|
}
|
|
// parity with YT player
|
|
player.getPlayerState = function() {
|
|
return playerState;
|
|
}
|
|
|
|
function createEvent(player, pluginMediaElement, eventName, e) {
|
|
var event = {
|
|
type: eventName,
|
|
target: pluginMediaElement
|
|
};
|
|
if (eventName == 'timeupdate') {
|
|
pluginMediaElement.currentTime = event.currentTime = e.seconds;
|
|
pluginMediaElement.duration = event.duration = e.duration;
|
|
}
|
|
pluginMediaElement.dispatchEvent(event);
|
|
}
|
|
|
|
player.addEvent('play', function() {
|
|
playerState = 1;
|
|
createEvent(player, pluginMediaElement, 'play');
|
|
createEvent(player, pluginMediaElement, 'playing');
|
|
});
|
|
|
|
player.addEvent('pause', function() {
|
|
playerState = 2;
|
|
createEvent(player, pluginMediaElement, 'pause');
|
|
});
|
|
|
|
player.addEvent('finish', function() {
|
|
playerState = 0;
|
|
createEvent(player, pluginMediaElement, 'ended');
|
|
});
|
|
|
|
player.addEvent('playProgress', function(e) {
|
|
createEvent(player, pluginMediaElement, 'timeupdate', e);
|
|
});
|
|
|
|
player.addEvent('seek', function(e) {
|
|
playerState = 3;
|
|
createEvent(player, pluginMediaElement, 'seeked', e);
|
|
});
|
|
|
|
player.addEvent('loadProgress', function(e) {
|
|
playerState = 3;
|
|
createEvent(player, pluginMediaElement, 'progress', e);
|
|
});
|
|
|
|
pluginMediaElement.pluginElement = container;
|
|
pluginMediaElement.pluginApi = player;
|
|
|
|
pluginMediaElement.success(pluginMediaElement, pluginMediaElement.pluginElement);
|
|
});
|
|
}
|
|
else {
|
|
console.warn("You need to include froogaloop for vimeo to work");
|
|
}
|
|
break;
|
|
}
|
|
// hide original element
|
|
htmlMediaElement.style.display = 'none';
|
|
// prevent browser from autoplaying when using a plugin
|
|
htmlMediaElement.removeAttribute('autoplay');
|
|
|
|
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?
|
|
*/
|
|
|
|
// YouTube Flash and Iframe API
|
|
mejs.YouTubeApi = {
|
|
isIframeStarted: false,
|
|
isIframeLoaded: false,
|
|
loadIframeApi: function(yt) {
|
|
if (!this.isIframeStarted) {
|
|
var tag = document.createElement('script');
|
|
tag.src = yt.scheme + "www.youtube.com/player_api";
|
|
var firstScriptTag = document.getElementsByTagName('script')[0];
|
|
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
|
this.isIframeStarted = true;
|
|
}
|
|
},
|
|
iframeQueue: [],
|
|
enqueueIframe: function(yt) {
|
|
|
|
if (this.isLoaded) {
|
|
this.createIframe(yt);
|
|
} else {
|
|
this.loadIframeApi(yt);
|
|
this.iframeQueue.push(yt);
|
|
}
|
|
},
|
|
createIframe: function(settings) {
|
|
|
|
var
|
|
pluginMediaElement = settings.pluginMediaElement,
|
|
player = new YT.Player(settings.containerId, {
|
|
height: settings.height,
|
|
width: settings.width,
|
|
videoId: settings.videoId,
|
|
playerVars: {controls:0,wmode:'transparent'},
|
|
events: {
|
|
'onReady': function() {
|
|
|
|
// wrapper to match
|
|
player.setVideoSize = function(width, height) {
|
|
player.setSize(width, height);
|
|
}
|
|
|
|
// hook up iframe object to MEjs
|
|
settings.pluginMediaElement.pluginApi = player;
|
|
settings.pluginMediaElement.pluginElement = document.getElementById(settings.containerId);
|
|
|
|
// init mejs
|
|
pluginMediaElement.success(pluginMediaElement, pluginMediaElement.pluginElement);
|
|
|
|
// create timer
|
|
setInterval(function() {
|
|
mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'timeupdate');
|
|
}, 250);
|
|
},
|
|
'onStateChange': function(e) {
|
|
|
|
mejs.YouTubeApi.handleStateChange(e.data, player, pluginMediaElement);
|
|
|
|
}
|
|
}
|
|
});
|
|
},
|
|
|
|
createEvent: function (player, pluginMediaElement, eventName) {
|
|
var event = {
|
|
type: eventName,
|
|
target: pluginMediaElement
|
|
};
|
|
|
|
if (player && player.getDuration) {
|
|
|
|
// time
|
|
pluginMediaElement.currentTime = event.currentTime = player.getCurrentTime();
|
|
pluginMediaElement.duration = event.duration = player.getDuration();
|
|
|
|
// state
|
|
event.paused = pluginMediaElement.paused;
|
|
event.ended = pluginMediaElement.ended;
|
|
|
|
// sound
|
|
event.muted = player.isMuted();
|
|
event.volume = player.getVolume() / 100;
|
|
|
|
// progress
|
|
event.bytesTotal = player.getVideoBytesTotal();
|
|
event.bufferedBytes = player.getVideoBytesLoaded();
|
|
|
|
// fake the W3C buffered TimeRange
|
|
var bufferedTime = event.bufferedBytes / event.bytesTotal * event.duration;
|
|
|
|
event.target.buffered = event.buffered = {
|
|
start: function(index) {
|
|
return 0;
|
|
},
|
|
end: function (index) {
|
|
return bufferedTime;
|
|
},
|
|
length: 1
|
|
};
|
|
|
|
}
|
|
|
|
// send event up the chain
|
|
pluginMediaElement.dispatchEvent(event);
|
|
},
|
|
|
|
iFrameReady: function() {
|
|
|
|
this.isLoaded = true;
|
|
this.isIframeLoaded = true;
|
|
|
|
while (this.iframeQueue.length > 0) {
|
|
var settings = this.iframeQueue.pop();
|
|
this.createIframe(settings);
|
|
}
|
|
},
|
|
|
|
// FLASH!
|
|
flashPlayers: {},
|
|
createFlash: function(settings) {
|
|
|
|
this.flashPlayers[settings.pluginId] = settings;
|
|
|
|
/*
|
|
settings.container.innerHTML =
|
|
'<object type="application/x-shockwave-flash" id="' + settings.pluginId + '" data="' + settings.scheme + 'www.youtube.com/apiplayer?enablejsapi=1&playerapiid=' + settings.pluginId + '&version=3&autoplay=0&controls=0&modestbranding=1&loop=0" ' +
|
|
'width="' + settings.width + '" height="' + settings.height + '" style="visibility: visible; " class="mejs-shim">' +
|
|
'<param name="allowScriptAccess" value="sameDomain">' +
|
|
'<param name="wmode" value="transparent">' +
|
|
'</object>';
|
|
*/
|
|
|
|
var specialIEContainer,
|
|
youtubeUrl = settings.scheme + 'www.youtube.com/apiplayer?enablejsapi=1&playerapiid=' + settings.pluginId + '&version=3&autoplay=0&controls=0&modestbranding=1&loop=0';
|
|
|
|
if (mejs.MediaFeatures.isIE) {
|
|
|
|
specialIEContainer = document.createElement('div');
|
|
settings.container.appendChild(specialIEContainer);
|
|
specialIEContainer.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="' + settings.scheme + 'download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' +
|
|
'id="' + settings.pluginId + '" width="' + settings.width + '" height="' + settings.height + '" class="mejs-shim">' +
|
|
'<param name="movie" value="' + youtubeUrl + '" />' +
|
|
'<param name="wmode" value="transparent" />' +
|
|
'<param name="allowScriptAccess" value="' + options.flashScriptAccess + '" />' +
|
|
'<param name="allowFullScreen" value="true" />' +
|
|
'</object>';
|
|
} else {
|
|
settings.container.innerHTML =
|
|
'<object type="application/x-shockwave-flash" id="' + settings.pluginId + '" data="' + youtubeUrl + '" ' +
|
|
'width="' + settings.width + '" height="' + settings.height + '" style="visibility: visible; " class="mejs-shim">' +
|
|
'<param name="allowScriptAccess" value="' + options.flashScriptAccess + '">' +
|
|
'<param name="wmode" value="transparent">' +
|
|
'</object>';
|
|
}
|
|
|
|
},
|
|
|
|
flashReady: function(id) {
|
|
var
|
|
settings = this.flashPlayers[id],
|
|
player = document.getElementById(id),
|
|
pluginMediaElement = settings.pluginMediaElement;
|
|
|
|
// hook up and return to MediaELementPlayer.success
|
|
pluginMediaElement.pluginApi =
|
|
pluginMediaElement.pluginElement = player;
|
|
|
|
settings.success(pluginMediaElement, pluginMediaElement.pluginElement);
|
|
|
|
// load the youtube video
|
|
player.cueVideoById(settings.videoId);
|
|
|
|
var callbackName = settings.containerId + '_callback';
|
|
|
|
window[callbackName] = function(e) {
|
|
mejs.YouTubeApi.handleStateChange(e, player, pluginMediaElement);
|
|
}
|
|
|
|
player.addEventListener('onStateChange', callbackName);
|
|
|
|
setInterval(function() {
|
|
mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'timeupdate');
|
|
}, 250);
|
|
|
|
mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'canplay');
|
|
},
|
|
|
|
handleStateChange: function(youTubeState, player, pluginMediaElement) {
|
|
switch (youTubeState) {
|
|
case -1: // not started
|
|
pluginMediaElement.paused = true;
|
|
pluginMediaElement.ended = true;
|
|
mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'loadedmetadata');
|
|
//createYouTubeEvent(player, pluginMediaElement, 'loadeddata');
|
|
break;
|
|
case 0:
|
|
pluginMediaElement.paused = false;
|
|
pluginMediaElement.ended = true;
|
|
mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'ended');
|
|
break;
|
|
case 1:
|
|
pluginMediaElement.paused = false;
|
|
pluginMediaElement.ended = false;
|
|
mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'play');
|
|
mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'playing');
|
|
break;
|
|
case 2:
|
|
pluginMediaElement.paused = true;
|
|
pluginMediaElement.ended = false;
|
|
mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'pause');
|
|
break;
|
|
case 3: // buffering
|
|
mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'progress');
|
|
break;
|
|
case 5:
|
|
// cued?
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
// IFRAME
|
|
window.onYouTubePlayerAPIReady = function() {
|
|
mejs.YouTubeApi.iFrameReady();
|
|
};
|
|
// FLASH
|
|
window.onYouTubePlayerReady = function(id) {
|
|
mejs.YouTubeApi.flashReady(id);
|
|
};
|
|
|
|
window.mejs = mejs;
|
|
window.MediaElement = mejs.MediaElement;
|
|
|
|
/*
|
|
* Adds Internationalization and localization to mediaelement.
|
|
*
|
|
* This file does not contain translations, you have to add them manually.
|
|
* The schema is always the same: me-i18n-locale-[IETF-language-tag].js
|
|
*
|
|
* Examples are provided both for german and chinese translation.
|
|
*
|
|
*
|
|
* What is the concept beyond i18n?
|
|
* http://en.wikipedia.org/wiki/Internationalization_and_localization
|
|
*
|
|
* What langcode should i use?
|
|
* http://en.wikipedia.org/wiki/IETF_language_tag
|
|
* https://tools.ietf.org/html/rfc5646
|
|
*
|
|
*
|
|
* License?
|
|
*
|
|
* The i18n file uses methods from the Drupal project (drupal.js):
|
|
* - i18n.methods.t() (modified)
|
|
* - i18n.methods.checkPlain() (full copy)
|
|
*
|
|
* The Drupal project is (like mediaelementjs) licensed under GPLv2.
|
|
* - http://drupal.org/licensing/faq/#q1
|
|
* - https://github.com/johndyer/mediaelement
|
|
* - http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
*
|
|
*
|
|
* @author
|
|
* Tim Latz (latz.tim@gmail.com)
|
|
*
|
|
*
|
|
* @params
|
|
* - context - document, iframe ..
|
|
* - exports - CommonJS, window ..
|
|
*
|
|
*/
|
|
;(function(context, exports, undefined) {
|
|
"use strict";
|
|
|
|
var i18n = {
|
|
"locale": {
|
|
// Ensure previous values aren't overwritten.
|
|
"language" : (exports.i18n && exports.i18n.locale.language) || '',
|
|
"strings" : (exports.i18n && exports.i18n.locale.strings) || {}
|
|
},
|
|
"ietf_lang_regex" : /^(x\-)?[a-z]{2,}(\-\w{2,})?(\-\w{2,})?$/,
|
|
"methods" : {}
|
|
};
|
|
// start i18n
|
|
|
|
|
|
/**
|
|
* Get language, fallback to browser's language if empty
|
|
*
|
|
* IETF: RFC 5646, https://tools.ietf.org/html/rfc5646
|
|
* Examples: en, zh-CN, cmn-Hans-CN, sr-Latn-RS, es-419, x-private
|
|
*/
|
|
i18n.getLanguage = function () {
|
|
var language = i18n.locale.language || window.navigator.userLanguage || window.navigator.language;
|
|
return i18n.ietf_lang_regex.exec(language) ? language : null;
|
|
|
|
//(WAS: convert to iso 639-1 (2-letters, lower case))
|
|
//return language.substr(0, 2).toLowerCase();
|
|
};
|
|
|
|
// i18n fixes for compatibility with WordPress
|
|
if ( typeof mejsL10n != 'undefined' ) {
|
|
i18n.locale.language = mejsL10n.language;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Encode special characters in a plain-text string for display as HTML.
|
|
*/
|
|
i18n.methods.checkPlain = function (str) {
|
|
var character, regex,
|
|
replace = {
|
|
'&': '&',
|
|
'"': '"',
|
|
'<': '<',
|
|
'>': '>'
|
|
};
|
|
str = String(str);
|
|
for (character in replace) {
|
|
if (replace.hasOwnProperty(character)) {
|
|
regex = new RegExp(character, 'g');
|
|
str = str.replace(regex, replace[character]);
|
|
}
|
|
}
|
|
return str;
|
|
};
|
|
|
|
/**
|
|
* Translate strings to the page language or a given language.
|
|
*
|
|
*
|
|
* @param str
|
|
* A string containing the English string to translate.
|
|
*
|
|
* @param options
|
|
* - 'context' (defaults to the default context): The context the source string
|
|
* belongs to.
|
|
*
|
|
* @return
|
|
* The translated string, escaped via i18n.methods.checkPlain()
|
|
*/
|
|
i18n.methods.t = function (str, options) {
|
|
|
|
// Fetch the localized version of the string.
|
|
if (i18n.locale.strings && i18n.locale.strings[options.context] && i18n.locale.strings[options.context][str]) {
|
|
str = i18n.locale.strings[options.context][str];
|
|
}
|
|
|
|
return i18n.methods.checkPlain(str);
|
|
};
|
|
|
|
|
|
/**
|
|
* Wrapper for i18n.methods.t()
|
|
*
|
|
* @see i18n.methods.t()
|
|
* @throws InvalidArgumentException
|
|
*/
|
|
i18n.t = function(str, options) {
|
|
|
|
if (typeof str === 'string' && str.length > 0) {
|
|
|
|
// check every time due language can change for
|
|
// different reasons (translation, lang switcher ..)
|
|
var language = i18n.getLanguage();
|
|
|
|
options = options || {
|
|
"context" : language
|
|
};
|
|
|
|
return i18n.methods.t(str, options);
|
|
}
|
|
else {
|
|
throw {
|
|
"name" : 'InvalidArgumentException',
|
|
"message" : 'First argument is either not a string or empty.'
|
|
};
|
|
}
|
|
};
|
|
|
|
// end i18n
|
|
exports.i18n = i18n;
|
|
}(document, mejs));
|
|
|
|
// i18n fixes for compatibility with WordPress
|
|
;(function(exports, undefined) {
|
|
|
|
"use strict";
|
|
|
|
if ( typeof mejsL10n != 'undefined' ) {
|
|
exports[mejsL10n.language] = mejsL10n.strings;
|
|
}
|
|
|
|
}(mejs.i18n.locale.strings));
|