app/assets/javascripts/mediaelement_rails/mediaelement.js in mediaelement_rails-0.5.1 vs app/assets/javascripts/mediaelement_rails/mediaelement.js in mediaelement_rails-0.7.0

- old
+ new

@@ -5,20 +5,21 @@ * * 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-2012, John Dyer (http://j.hn) +* Copyright 2010-2013, John Dyer (http://j.hn) * License: MIT * */ // Namespace var mejs = mejs || {}; // version number -mejs.version = '2.11.0'; +mejs.version = '2.13.2'; + // player number (for missing, same id attr) mejs.meIndex = 0; // media types accepted by plugins mejs.plugins = { @@ -35,11 +36,10 @@ vimeo: [ {version: null, types: ['video/vimeo', 'video/x-vimeo']} ] }; - /* Utility methods */ mejs.Utility = { encodeUrl: function(url) { @@ -55,31 +55,51 @@ }, getScriptPath: function(scriptNames) { var i = 0, j, - path = '', - name = '', - script, + 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++) { - script = scripts[i].src; + 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++) { - name = scriptNames[j]; - if (script.indexOf(name) > -1) { - path = script.substring(0, script.indexOf(name)); + testname = scriptNames[j]; + filenamePos = scriptFilename.indexOf(testname); + if (filenamePos > -1) { + codePath = scriptPath; break; } } - if (path !== '') { + + // if we found a path, then break and return it + if (codePath !== '') { break; } } - return path; + + // send the best path back + return codePath; }, secondsToTimeCode: function(time, forceHours, showFrameCount, fps) { //add framecount if (typeof showFrameCount == 'undefined') { showFrameCount=false; @@ -295,17 +315,18 @@ 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.isIE = (nav.appName.toLowerCase().indexOf("microsoft") != -1); + 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.isFirefox = (ua.match(/firefox/gi) !== null); t.isWebkit = (ua.match(/webkit/gi) !== null); - t.isGecko = (ua.match(/gecko/gi) !== null) && !t.isWebkit; + t.isGecko = (ua.match(/gecko/gi) !== null) && !t.isWebkit && !t.isIE; t.isOpera = (ua.match(/opera/gi) !== null); - t.hasTouch = ('ontouchstart' in window); + t.hasTouch = ('ontouchstart' in window); // && window.ontouchstart != null); // this breaks iOS 7 // borrowed from Modernizr t.svg = !! document.createElementNS && !! document.createElementNS('http://www.w3.org/2000/svg','svg').createSVGRect; @@ -314,56 +335,93 @@ v = document.createElement(html5Elements[i]); } t.supportsMediaTag = (typeof v.canPlayType !== 'undefined' || t.isBustedAndroid); + // Fix for IE9 on Windows 7N / Windows 7KN (Media Player not installer) + try{ + v.canPlayType("video/mp4"); + }catch(e){ + t.supportsMediaTag = false; + } + // detect native JavaScript fullscreen (Safari/Firefox only, Chrome still fails) // iOS t.hasSemiNativeFullScreen = (typeof v.webkitEnterFullscreen !== 'undefined'); - // Webkit/firefox + // 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.hasTrueNativeFullScreen = (t.hasWebkitNativeFullScreen || t.hasMozNativeFullScreen || t.hasMsNativeFullScreen); t.nativeFullScreenEnabled = t.hasTrueNativeFullScreen; + + // Enabled? if (t.hasMozNativeFullScreen) { - t.nativeFullScreenEnabled = v.mozFullScreenEnabled; + t.nativeFullScreenEnabled = document.mozFullScreenEnabled; + } else if (t.hasMsNativeFullScreen) { + t.nativeFullScreenEnabled = document.msFullscreenEnabled; } - - if (this.isChrome) { + if (t.isChrome) { t.hasSemiNativeFullScreen = false; } if (t.hasTrueNativeFullScreen) { - t.fullScreenEventName = (t.hasWebkitNativeFullScreen) ? 'webkitfullscreenchange' : 'mozfullscreenchange'; + t.fullScreenEventName = ''; + if (t.hasWebkitNativeFullScreen) { + t.fullScreenEventName = 'webkitfullscreenchange'; + + } else if (t.hasMozNativeFullScreen) { + t.fullScreenEventName = 'mozfullscreenchange'; + + } else if (t.hasMsNativeFullScreen) { + t.fullScreenEventName = 'MSFullscreenChange'; + } t.isFullScreen = function() { if (v.mozRequestFullScreen) { return d.mozFullScreen; + } else if (v.webkitRequestFullScreen) { return d.webkitIsFullScreen; + + } else if (v.hasMsNativeFullScreen) { + return d.msFullscreenElement !== null; } } t.requestFullScreen = function(el) { if (t.hasWebkitNativeFullScreen) { el.webkitRequestFullScreen(); + } else if (t.hasMozNativeFullScreen) { el.mozRequestFullScreen(); + + } else if (t.hasMsNativeFullScreen) { + el.msRequestFullscreen(); + } } t.cancelFullScreen = function() { if (t.hasWebkitNativeFullScreen) { document.webkitCancelFullScreen(); + } else if (t.hasMozNativeFullScreen) { document.mozCancelFullScreen(); + + } else if (t.hasMsNativeFullScreen) { + document.msExitFullscreen(); + } } } @@ -542,11 +600,11 @@ return ''; }, positionFullscreenButton: function(x,y,visibleAndAbove) { if (this.pluginApi != null && this.pluginApi.positionFullscreenButton) { - this.pluginApi.positionFullscreenButton(x,y,visibleAndAbove); + this.pluginApi.positionFullscreenButton(Math.floor(x),Math.floor(y),visibleAndAbove); } }, hideFullscreenButton: function() { if (this.pluginApi != null && this.pluginApi.hideFullscreenButton) { @@ -754,10 +812,14 @@ e, i, bufferedTime, pluginMediaElement = this.pluginMediaElements[id]; + if(!pluginMediaElement){ + return; + } + // fake event object to mimic real HTML media event. e = { type: eventName, target: pluginMediaElement }; @@ -798,20 +860,26 @@ mode: 'auto', // remove or reorder to change plugin priority and availability plugins: ['flash','silverlight','youtube','vimeo'], // shows debug errors on screen enablePluginDebug: false, + // use plugin for browsers that have trouble with Basic Authentication on HTTPS sites + httpsBasicAuthSite: false, // overrides the type specified, useful for dynamic instantiation type: '', // path to Flash and Silverlight plugins pluginPath: mejs.Utility.getScriptPath(['mediaelement.js','mediaelement.min.js','mediaelement-and-player.js','mediaelement-and-player.min.js']), // name of flash file flashName: 'flashmediaelement.swf', // streamer for RTMP streaming flashStreamer: '', // turns on the smoothing filter in Flash enablePluginSmoothing: false, + // enabled pseudo-streaming (seek) on .mp4 files + enablePseudoStreaming: false, + // start query parameter sent to server for pseudo-streaming + pseudoStreamingStartQueryParam: 'start', // name of silverlight file silverlightName: 'silverlightmediaelement.xap', // default if the <video width> is not specified defaultVideoWidth: 480, // default if the <video height> is not specified @@ -964,11 +1032,11 @@ }; } // test for native playback first - if (supportsMediaTag && (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'native')) { + 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'); @@ -1073,11 +1141,11 @@ } }, getTypeFromFile: function(url) { url = url.split('?')[0]; - var ext = url.substring(url.lastIndexOf('.') + 1); + var ext = url.substring(url.lastIndexOf('.') + 1).toLowerCase(); return (/(mp4|m4v|ogg|ogv|webm|webmv|flv|wmv|mpeg|mov)/gi.test(ext) ? 'video' : 'audio') + '/' + this.getTypeFromExtension(ext); }, getTypeFromExtension: function(ext) { @@ -1108,13 +1176,17 @@ try { errorContainer.style.width = htmlMediaElement.width + 'px'; errorContainer.style.height = htmlMediaElement.height + 'px'; } catch (e) {} - errorContainer.innerHTML = (poster !== '') ? - '<a href="' + playback.url + '"><img src="' + poster + '" width="100%" height="100%" /></a>' : - '<a href="' + playback.url + '"><span>' + mejs.i18n.t('Download File') + '</span></a>'; + if (options.customError) { + errorContainer.innerHTML = options.customError; + } else { + errorContainer.innerHTML = (poster !== '') ? + '<a href="' + playback.url + '"><img src="' + poster + '" width="100%" height="100%" /></a>' : + '<a href="' + playback.url + '"><span>' + mejs.i18n.t('Download File') + '</span></a>'; + } htmlMediaElement.parentNode.insertBefore(errorContainer, htmlMediaElement); htmlMediaElement.style.display = 'none'; options.error(htmlMediaElement); @@ -1152,12 +1224,12 @@ } node = node.parentNode; } if (playback.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; + 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); @@ -1190,11 +1262,12 @@ 'preload=' + preload, 'width=' + width, 'startvolume=' + options.startVolume, 'timerrate=' + options.timerRate, 'flashstreamer=' + options.flashStreamer, - 'height=' + height]; + 'height=' + height, + 'pseudostreamstart=' + options.pseudoStreamingStartQueryParam]; if (playback.url !== null) { if (playback.method == 'flash') { initVars.push('file=' + mejs.Utility.encodeUrl(playback.url)); } else { @@ -1205,10 +1278,13 @@ 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); @@ -1240,10 +1316,11 @@ '<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" />' + +'<param name="scale" value="default" />' + '</object>'; } else { container.innerHTML = @@ -1258,10 +1335,11 @@ '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': @@ -1287,11 +1365,11 @@ break; // DEMO Code. Does NOT work. case 'vimeo': - //console.log('vimeoid'); + // pluginMediaElement.vimeoid = playback.url.substr(playback.url.lastIndexOf('/')+1); container.innerHTML ='<iframe src="http://player.vimeo.com/video/' + pluginMediaElement.vimeoid + '?portrait=0&byline=0&title=0" width="' + width +'" height="' + height +'" frameborder="0" class="mejs-shim"></iframe>'; @@ -1308,10 +1386,12 @@ break; } // hide original element htmlMediaElement.style.display = 'none'; + // prevent browser from autoplaying when using a plugin + htmlMediaElement.removeAttribute('autoplay'); // FYI: options.success will be fired by the MediaPluginBridge return pluginMediaElement; }, @@ -1373,11 +1453,11 @@ isIframeStarted: false, isIframeLoaded: false, loadIframeApi: function() { if (!this.isIframeStarted) { var tag = document.createElement('script'); - tag.src = "http://www.youtube.com/player_api"; + tag.src = "//www.youtube.com/player_api"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); this.isIframeStarted = true; } }, @@ -1491,11 +1571,11 @@ '<param name="wmode" value="transparent">' + '</object>'; */ var specialIEContainer, - youtubeUrl = 'http://www.youtube.com/apiplayer?enablejsapi=1&amp;playerapiid=' + settings.pluginId + '&amp;version=3&amp;autoplay=0&amp;controls=0&amp;modestbranding=1&loop=0'; + youtubeUrl = '//www.youtube.com/apiplayer?enablejsapi=1&amp;playerapiid=' + settings.pluginId + '&amp;version=3&amp;autoplay=0&amp;controls=0&amp;modestbranding=1&loop=0'; if (mejs.MediaFeatures.isIE) { specialIEContainer = document.createElement('div'); settings.container.appendChild(specialIEContainer); @@ -1590,81 +1670,74 @@ window.mejs = mejs; window.MediaElement = mejs.MediaElement; /*! - * Adds Internationalization and localization to objects. + * Adds Internationalization and localization to mediaelement. * - * What is the concept beyond i18n? - * http://en.wikipedia.org/wiki/Internationalization_and_localization + * This file does not contain translations, you have to add the manually. + * The schema is always the same: me-i18n-locale-[ISO_639-1 Code].js * + * Examples are provided both for german and chinese translation. * - * This file both i18n methods and locale which is used to translate - * strings into other languages. * - * Default translations are not available, you have to add them - * through locale objects which are named exactly as the langcode - * they stand for. The default language is always english (en). + * What is the concept beyond i18n? + * http://en.wikipedia.org/wiki/Internationalization_and_localization * + * What langcode should i use? + * http://en.wikipedia.org/wiki/ISO_639-1 * - * Wrapper built to be able to attach the i18n object to - * other objects without changing more than one line. * + * License? * - * LICENSE: - * * The i18n file uses methods from the Drupal project (drupal.js): * - i18n.methods.t() (modified) * - i18n.methods.checkPlain() (full copy) - * - i18n.methods.formatString() (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) * - * @see - * me-i18n-locale.js * * @params - * - $ - zepto || jQuery .. * - context - document, iframe .. * - exports - CommonJS, window .. * */ -;(function($, context, exports, undefined) { +;(function(context, exports, undefined) { "use strict"; var i18n = { "locale": { + "language" : '', "strings" : {} }, "methods" : {} }; // start i18n /** - * Get the current browser's language - * - * @see: i18n.methods.t() + * Get language, fallback to browser's language if empty */ - i18n.locale.getLanguage = function () { - return { - "language" : navigator.language - }; + i18n.getLanguage = function () { + var language = i18n.locale.language || window.navigator.userLanguage || window.navigator.language; + // convert to iso 639-1 (2-letters, lower case) + return language.substr(0, 2).toLowerCase(); }; - /** - * Store the language the locale object was initialized with - */ - i18n.locale.INIT_LANGUAGE = i18n.locale.getLanguage(); + // 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, @@ -1683,115 +1756,81 @@ } return str; }; /** - * Replace placeholders with sanitized values in a string. - * - * @param str - * A string with placeholders. - * @param args - * An object of replacements pairs to make. Incidences of any key in this - * array are replaced with the corresponding value. Based on the first - * character of the key, the value is escaped and/or themed: - * - !variable: inserted as is - * - @variable: escape plain text to HTML (i18n.methods.checkPlain) - * - %variable: escape text and theme as a placeholder for user-submitted - * content (checkPlain + <em class="placeholder" > ) - * - * @see i18n.methods.t() - */ - i18n.methods.formatString = function(str, args) { - // Transform arguments before inserting them. - for (var key in args) { - switch (key.charAt(0)) { - // Escaped only. - case '@': - args[key] = i18n.methods.checkPlain(args[key]); - break; - // Pass-through. - case '!': - break; - // Escaped and placeholder. - case '%': - default: - args[key] = '<em class="placeholder">' + i18n.methods.checkPlain(args[key]) + '</em>'; - break; - } - str = str.replace(key, args[key]); - } - return str; - }; - - /** * Translate strings to the page language or a given language. * - * See the documentation of the server-side t() function for further details. * * @param str * A string containing the English string to translate. - * @param args - * An object of replacements pairs to make after translation. Incidences - * of any key in this array are replaced with the corresponding value. - * See i18n.methods.formatString(). * * @param options * - 'context' (defaults to the default context): The context the source string * belongs to. * * @return - * The translated string. + * The translated string, escaped via i18n.methods.checkPlain() */ - i18n.methods.t = function (str, args, options) { + 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]; } - if (args) { - str = i18n.methods.formatString(str, args); - } - return str; + return i18n.methods.checkPlain(str); }; /** * Wrapper for i18n.methods.t() * * @see i18n.methods.t() * @throws InvalidArgumentException */ - i18n.t = function(str, args, options) { + i18n.t = function(str, options) { if (typeof str === 'string' && str.length > 0) { - // check every time due languge can change for + // check every time due language can change for // different reasons (translation, lang switcher ..) - var lang = i18n.locale.getLanguage(); + var language = i18n.getLanguage(); options = options || { - "context" : lang.language + "context" : language }; - return i18n.methods.t(str, args, options); + 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; -}(jQuery, document, mejs)); +}(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)); + /*! * This is a i18n.locale language object. * - *<de> German translation by Tim Latz, latz.tim@gmail.com + * German translation by Tim Latz, latz.tim@gmail.com * * @author * Tim Latz (latz.tim@gmail.com) * * @see @@ -1802,13 +1841,44 @@ */ ;(function(exports, undefined) { "use strict"; - exports.de = { - "Fullscreen" : "Vollbild", - "Go Fullscreen" : "Vollbild an", - "Turn off Fullscreen" : "Vollbild aus", - "Close" : "Schließen" - }; + if (typeof exports.de === 'undefined') { + exports.de = { + "Fullscreen" : "Vollbild", + "Go Fullscreen" : "Vollbild an", + "Turn off Fullscreen" : "Vollbild aus", + "Close" : "Schließen" + }; + } }(mejs.i18n.locale.strings)); +/*! + * This is a i18n.locale language object. + * + * Traditional chinese translation by Tim Latz, latz.tim@gmail.com + * + * @author + * Tim Latz (latz.tim@gmail.com) + * + * @see + * me-i18n.js + * + * @params + * - exports - CommonJS, window .. + */ +;(function(exports, undefined) { + + "use strict"; + + if (typeof exports.zh === 'undefined') { + exports.zh = { + "Fullscreen" : "全螢幕", + "Go Fullscreen" : "全屏模式", + "Turn off Fullscreen" : "退出全屏模式", + "Close" : "關閉" + }; + } + +}(mejs.i18n.locale.strings)); +