Mercurial > hg > env-test-daniele
view johndyer-mediaelement-13fa20a/src/js/me-shim.js @ 0:032bc65ebafc
added core components
author | George Fazekas <gyorgy.fazekas@eecs.qmul.ac.uk> |
---|---|
date | Wed, 06 Mar 2013 15:45:48 +0000 |
parents | |
children |
line wrap: on
line source
// 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; }, // when Flash/Silverlight is ready, it calls out to this method initPlugin: function (id) { var pluginMediaElement = this.pluginMediaElements[id], htmlMediaElement = this.htmlMediaElements[id]; // 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]; pluginMediaElement.ended = false; pluginMediaElement.paused = true; // 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 // 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'], // shows debug errors on screen enablePluginDebug: 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', // turns on the smoothing filter in Flash enablePluginSmoothing: false, // 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, // 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, 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 = htmlMediaElement.getAttribute('src'), 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]; } // is this a true HTML5 media element if (isMediaTag) { isVideo = (htmlMediaElement.tagName.toLowerCase() == 'video'); } else { // fake source from <a href=""></a> src = htmlMediaElement.getAttribute('href'); } // clean up attributes src = (src == 'undefined' || src == '' || src === null) ? 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); if (playback.method == 'native') { // second fix for android if (mejs.MediaFeatures.isBustedAndroid) { htmlMediaElement.src = playback.url; htmlMediaElement.addEventListener('click', function() { htmlMediaElement.play(); }, true); } // add methods to native HTMLMediaElement return this.updateNative( playback.htmlMediaElement, options, autoplay, preload, playback); } else if (playback.method !== '') { // create plugin to mimic HTMLMediaElement return this.createPlugin( htmlMediaElement, options, playback.method, (playback.url !== null) ? mejs.Utility.absolutizeUrl(playback.url) : '', poster, autoplay, preload, controls); } else { // boo, no HTML5, no Flash, no Silverlight. this.createErrorMessage( htmlMediaElement, options, (playback.url !== null) ? mejs.Utility.absolutizeUrl(playback.url) : '', poster ); } }, videoRegExp: /(mp4|m4v|ogg|ogv|webm|flv|wmv|mpeg)/gi, 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; // 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')); mediaFiles.push({type:type, url:src}); } } } // 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 === 'native')) { if (!isMediaTag) { var tagName = 'video'; if (mediaFiles.length > 0 && mediaFiles[0].url !== null && this.getTypeFromFile(mediaFiles[0].url).indexOf('audio') > -1) { tagName = 'audio'; result.isVideo = false; } // create a real HTML5 Media Element dummy = document.createElement(tagName); 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') { return result; } } // if native playback didn't work, then test plugins if (options.mode === 'auto' || 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 if (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; } } } } } } } // what if there's nothing to play? just grab the first available if (result.method === '') { 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) { var ext = url.substring(url.lastIndexOf('.') + 1); return (this.videoRegExp.test(ext) ? 'video' : 'audio') + '/' + ext; }, createErrorMessage: function(htmlMediaElement, options, downloadUrl, poster) { var errorContainer = document.createElement('div'); errorContainer.className = 'me-cannotplay'; try { errorContainer.style.width = htmlMediaElement.width + 'px'; errorContainer.style.height = htmlMediaElement.height + 'px'; } catch (e) {} errorContainer.innerHTML = (poster !== '') ? '<a href="' + downloadUrl + '"><img src="' + poster + '" /></a>' : '<a href="' + downloadUrl + '"><span>Download File</span></a>'; htmlMediaElement.parentNode.insertBefore(errorContainer, htmlMediaElement); htmlMediaElement.style.display = 'none'; options.error(htmlMediaElement); }, createPlugin:function(htmlMediaElement, options, isVideo, pluginType, mediaUrl, poster, autoplay, preload, controls) { var width = 1, height = 1, pluginid = 'me_' + pluginType + '_' + (mejs.meIndex++), pluginMediaElement = new mejs.PluginMediaElement(pluginid, pluginType, mediaUrl), container = document.createElement('div'), specialIEContainer, node, initVars; // check for placement inside a <p> tag (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 (isVideo) { width = (options.videoWidth > 0) ? options.videoWidth : (htmlMediaElement.getAttribute('width') !== null) ? htmlMediaElement.getAttribute('width') : options.defaultVideoWidth; height = (options.videoHeight > 0) ? options.videoHeight : (htmlMediaElement.getAttribute('height') !== null) ? htmlMediaElement.getAttribute('height') : options.defaultVideoHeight; } 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'; htmlMediaElement.parentNode.insertBefore(container, htmlMediaElement); // flash/silverlight vars initVars = [ 'id=' + pluginid, 'isvideo=' + ((isVideo) ? "true" : "false"), 'autoplay=' + ((autoplay) ? "true" : "false"), 'preload=' + preload, 'width=' + width, 'startvolume=' + options.startVolume, 'timerrate=' + options.timerRate, 'height=' + height]; if (mediaUrl !== null) { if (pluginType == 'flash') { initVars.push('file=' + mejs.Utility.encodeUrl(mediaUrl)); } else { initVars.push('file=' + mediaUrl); } } if (options.enablePluginDebug) { initVars.push('debug=true'); } if (options.enablePluginSmoothing) { initVars.push('smoothing=true'); } if (controls) { initVars.push('controls=true'); // shows controls in the plugin if desired } switch (pluginType) { case 'silverlight': container.innerHTML = '<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" id="' + pluginid + '" name="' + pluginid + '" width="' + width + '" height="' + height + '">' + '<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="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' + 'id="' + pluginid + '" width="' + width + '" height="' + height + '">' + '<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="http://www.macromedia.com/go/getflashplayer" ' + 'src="' + options.pluginPath + options.flashName + '" ' + 'flashvars="' + initVars.join('&') + '" ' + 'width="' + width + '" ' + 'height="' + height + '"></embed>'; } break; } // hide original element htmlMediaElement.style.display = 'none'; // FYI: options.success will be fired by the MediaPluginBridge return pluginMediaElement; }, updateNative: function(htmlMediaElement, options, autoplay, preload, playback) { // add methods to video object to bring it into parity with Flash Object for (var 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; } }; window.mejs = mejs; window.MediaElement = mejs.MediaElement;