/*! * Media Element * HTML5 and Shim * http://mediaelementjs.com/ * * Creates a JavaScript object that mimics HTML5 media object * for browsers that don't understand HTML5 or can't play the provided codec * Can also play MP4 (H.264), Ogg, WebM, FLV, WMV, WMA, ACC, and MP3 * * Copyright 2010, John Dyer * Dual licensed under the MIT or GPL Version 2 licenses. * * Version: 1.1.2 */ // Namespace var html5 = html5 || {}; // player number (for missing, same id attr) html5.meIndex = 0; // media types accepted by plugins html5.plugins = { silverlight: [ {version: [3,0], types: ['video/mp4','video/m4v','video/mov','video/wmv','audio/wma','audio/mp4','audio/m4a','audio/mp3']} ], flash: [ {version: [9,0,124], types: ['video/mp4','video/m4v','video/mov','video/flv','audio/flv','audio/mp3','audio/m4a','audio/mp3']} //,{version: [11,0], types: ['video/webm'} // for future reference ] }; // Core detector, plugins are added below html5.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], d, i, ax; // Firefox, Webkit, Opera if (typeof(this.nav.plugins) != 'undefined' && typeof this.nav.plugins[pluginName] == 'object') { d = this.nav.plugins[pluginName].description; if (d && !(typeof this.nav.mimeTypes != 'undefined' && this.nav.mimeTypes[mimeType] && !this.nav.mimeTypes[mimeType].enabledPlugin)) { version = d.replace(pluginName, '').replace(/^\s+/,'').replace(/\sr/gi,'.').split('.'); for (i=0; i element for fullscreen detection for (i=0; ix'; return el.firstChild.href; }, getScriptPath: function(scriptNames) { var i = 0, j, path = '', name = '', script, scripts = document.getElementsByTagName('script'); for (; i < scripts.length; i++) { script = scripts[i].src; for (j = 0; j < scriptNames.length; j++) { name = scriptNames[j]; if (script.indexOf(name) > -1) { path = script.substring(0, script.indexOf(name)); break; } } if (path !== '') { break; } } return path; }, secondsToTimeCode: function(seconds) { seconds = Math.round(seconds); var minutes = Math.floor(seconds / 60); minutes = (minutes >= 10) ? minutes : "0" + minutes; seconds = Math.floor(seconds % 60); seconds = (seconds >= 10) ? seconds : "0" + seconds; return minutes + ":" + seconds; } }; /* extension methods to or object to bring it into parity with PluginMediaElement (see below) */ html5.HtmlMediaElement = { pluginType: 'native', setCurrentTime: function (time) { this.currentTime = time; }, setMuted: function (muted) { this.muted = muted; }, setVolume: function (volume) { this.volume = volume; }, // 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.src = url; } else { var i, media; for (i=0; i element by calling Flash's External Interface or Silverlights [ScriptableMember] */ html5.PluginMediaElement = function (pluginid, pluginType) { this.id = pluginid; this.pluginType = pluginType; this.events = {}; }; // 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 html5.PluginMediaElement.prototype = { // special pluginElement: null, pluginType: '', // not implemented :( playbackRate: -1, defaultPlaybackRate: -1, seekable: [], played: [], // HTML5 read-only properties paused: true, ended: false, seeking: false, duration: 0, // HTML5 get/set properties, but only set (updated by event handlers) muted: false, volume: 1, currentTime: 0, // HTML5 methods play: function () { this.pluginApi.playMedia(); this.paused = false; }, load: function () { this.pluginApi.loadMedia(); this.paused = false; }, pause: function () { this.pluginApi.pauseMedia(); this.paused = true; }, canPlayType: function(type) { var i, j, pluginInfo, pluginVersions = html5.plugins[this.pluginType]; for (i=0; i events and properties html5.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.SilverlightApp; break; } if (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 = false; // 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 */ html5.MediaElementDefaults = { // shows debug errors on screen enablePluginDebug: false, // remove or reorder to change plugin priority plugins: ['silverlight', 'flash'], // specify to force MediaElement into a mode type: '', // path to Flash and Silverlight plugins pluginPath: html5.Utility.getScriptPath(['mediaelement.js','mediaelement.min.js','mediaelement-and-player.js','mediaelement-and-player.min.js']), // name of flash file flashName: 'flashmediaelement.swf', // name of silverlight file silverlightName: 'silverlightmediaelement.xap', // default if the is not specified defaultVideoWidth: 480, // default if the is not specified defaultVideoHeight: 270, // overrides pluginWidth: -1, // overrides 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 or element and returns either the native element or a Flash/Silverlight version that mimics HTML5 MediaElement */ html5.MediaElement = function (el, o) { html5.HtmlMediaElementShim.create(el,o); }; html5.HtmlMediaElementShim = { create: function(el, o) { var options = html5.MediaElementDefaults, htmlMediaElement = (typeof(el) == 'string') ? document.getElementById(el) : el, isVideo = (htmlMediaElement.tagName.toLowerCase() == 'video'), supportsMediaTag = (typeof(htmlMediaElement.canPlayType) != 'undefined'), playback = {method:'', url:''}, poster = htmlMediaElement.getAttribute('poster'), autoplay = htmlMediaElement.getAttribute('autoplay'), prop; // extend options for (prop in o) { options[prop] = o[prop]; } // check for real poster poster = (poster == 'undefined' || poster === null) ? '' : poster; // test for HTML5 and plugin capabilities playback = this.determinePlayback(htmlMediaElement, options, isVideo, supportsMediaTag); if (playback.method == 'native') { // add methods to native HTMLMediaElement this.updateNative( htmlMediaElement, options); } else if (playback.method !== '') { // create plugin to mimic HTMLMediaElement this.createPlugin( htmlMediaElement, options, isVideo, playback.method, (playback.url !== null) ? html5.Utility.absolutizeUrl(playback.url).replace('&','%26') : '', poster, autoplay); } else { // boo, no HTML5, no Flash, no Silverlight. this.createErrorMessage( htmlMediaElement, options, (playback.url !== null) ? html5.Utility.absolutizeUrl(playback.url) : '', poster ); } }, determinePlayback: function(htmlMediaElement, options, isVideo, supportsMediaTag) { var mediaFiles = [], i, j, k, l, n, url, type, result = { method: '', url: ''}, src = htmlMediaElement.getAttribute('src'), pluginName, pluginVersions, pluginInfo; // STEP 1: Get Files from or // supplied type overrides all HTML if (typeof (options.type) != 'undefined' && options.type !== '') { mediaFiles.push({type:options.type, url:null}); // test for src attribute first } else if (src != 'undefined' && src !== null) { url = htmlMediaElement.getAttribute('src'); type = this.checkType(url, htmlMediaElement.getAttribute('type'), isVideo); mediaFiles.push({type:type, url:url}); // then test for elements } else { // test 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') { url = n.getAttribute('src'); type = this.checkType(url, n.getAttribute('type'), isVideo); mediaFiles.push({type:type, url:url}); } } } // STEP 2: Test for playback method // test for native playback first if (supportsMediaTag) { for (i=0; i' : 'Download File'; htmlMediaElement.parentNode.insertBefore(errorContainer, htmlMediaElement); htmlMediaElement.style.display = 'none'; options.error(htmlMediaElement); }, createPlugin:function(htmlMediaElement, options, isVideo, pluginType, mediaUrl, poster, autoplay) { var width = 1, height = 1, pluginid = 'me_' + pluginType + '_' + (html5.meIndex++), pluginMediaElement = new html5.PluginMediaElement(pluginid, pluginType), container = document.createElement('div'), initVars; 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; html5.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, 'poster=' + poster, 'isvideo=' + isVideo.toString(), 'autoplay=' + autoplay, 'width=' + width, 'timerrate=' + options.timerRate, 'height=' + height]; if (mediaUrl !== null) { initVars.push('file=' + mediaUrl); } if (options.enablePluginDebug) { initVars.push('debug=true'); } switch (pluginType) { case 'silverlight': container.innerHTML = '' + '' + '' + '' + '' + '' + '' + ''; break; case 'flash': if (html5.MediaFeatures.isIE) { container.outerHTML = '' + '' + '' + '' + '' + '' + '' + '' + ''; } else { container.innerHTML = ''; } break; } // hide original element htmlMediaElement.style.display = 'none'; // FYI: options.success will be fired by the MediaPluginBridge }, updateNative: function(htmlMediaElement, options) { // add methods to video object to bring it into parity with Flash Object for (var m in html5.HtmlMediaElement) { htmlMediaElement[m] = html5.HtmlMediaElement[m]; } // fire success code options.success(htmlMediaElement, htmlMediaElement); } }; window.html5 = html5; window.MediaElement = html5.MediaElement;