aboutsummaryrefslogtreecommitdiff
path: root/assets/mejs/mediaelementplayer.js
diff options
context:
space:
mode:
authorMaZderMind2015-02-27 19:21:25 +0100
committerMaZderMind2015-02-27 19:21:25 +0100
commited8b20bc6cb03a8f3521b169e8778d8ede23ea26 (patch)
treed8061b885dedf3baaab476c056d9b28fa3f3c8d2 /assets/mejs/mediaelementplayer.js
parent18031079f5ca57cdac6aa4ab4c236182329bc1f7 (diff)
upgrade to newer mejs - happy to kick out flash \o/
Diffstat (limited to 'assets/mejs/mediaelementplayer.js')
-rw-r--r--assets/mejs/mediaelementplayer.js3560
1 files changed, 3560 insertions, 0 deletions
diff --git a/assets/mejs/mediaelementplayer.js b/assets/mejs/mediaelementplayer.js
new file mode 100644
index 0000000..28147ab
--- /dev/null
+++ b/assets/mejs/mediaelementplayer.js
@@ -0,0 +1,3560 @@
+/*!
+ *
+ * MediaElementPlayer
+ * http://mediaelementjs.com/
+ *
+ * Creates a controller bar for HTML5 <video> add <audio> tags
+ * using jQuery and MediaElement.js (HTML5 Flash/Silverlight wrapper)
+ *
+ * Copyright 2010-2013, John Dyer (http://j.hn/)
+ * License: MIT
+ *
+ */
+if (typeof jQuery != 'undefined') {
+ mejs.$ = jQuery;
+} else if (typeof ender != 'undefined') {
+ mejs.$ = ender;
+}
+(function ($) {
+
+ // default player values
+ mejs.MepDefaults = {
+ // url to poster (to fix iOS 3.x)
+ poster: '',
+ // When the video is ended, we can show the poster.
+ showPosterWhenEnded: false,
+ // default if the <video width> is not specified
+ defaultVideoWidth: 480,
+ // default if the <video height> is not specified
+ defaultVideoHeight: 270,
+ // if set, overrides <video width>
+ videoWidth: -1,
+ // if set, overrides <video height>
+ videoHeight: -1,
+ // default if the user doesn't specify
+ defaultAudioWidth: 400,
+ // default if the user doesn't specify
+ defaultAudioHeight: 30,
+
+ // default amount to move back when back key is pressed
+ defaultSeekBackwardInterval: function(media) {
+ return (media.duration * 0.05);
+ },
+ // default amount to move forward when forward key is pressed
+ defaultSeekForwardInterval: function(media) {
+ return (media.duration * 0.05);
+ },
+
+ // set dimensions via JS instead of CSS
+ setDimensions: true,
+
+ // width of audio player
+ audioWidth: -1,
+ // height of audio player
+ audioHeight: -1,
+ // initial volume when the player starts (overrided by user cookie)
+ startVolume: 0.8,
+ // useful for <audio> player loops
+ loop: false,
+ // rewind to beginning when media ends
+ autoRewind: true,
+ // resize to media dimensions
+ enableAutosize: true,
+ // forces the hour marker (##:00:00)
+ alwaysShowHours: false,
+
+ // show framecount in timecode (##:00:00:00)
+ showTimecodeFrameCount: false,
+ // used when showTimecodeFrameCount is set to true
+ framesPerSecond: 25,
+
+ // automatically calculate the width of the progress bar based on the sizes of other elements
+ autosizeProgress : true,
+ // Hide controls when playing and mouse is not over the video
+ alwaysShowControls: false,
+ // Display the video control
+ hideVideoControlsOnLoad: false,
+ // Enable click video element to toggle play/pause
+ clickToPlayPause: true,
+ // force iPad's native controls
+ iPadUseNativeControls: false,
+ // force iPhone's native controls
+ iPhoneUseNativeControls: false,
+ // force Android's native controls
+ AndroidUseNativeControls: false,
+ // features to show
+ features: ['playpause','current','progress','duration','tracks','volume','fullscreen'],
+ // only for dynamic
+ isVideo: true,
+
+ // turns keyboard support on and off for this instance
+ enableKeyboard: true,
+
+ // whenthis player starts, it will pause other players
+ pauseOtherPlayers: true,
+
+ // array of keyboard actions such as play pause
+ keyActions: [
+ {
+ keys: [
+ 32, // SPACE
+ 179 // GOOGLE play/pause button
+ ],
+ action: function(player, media) {
+ if (media.paused || media.ended) {
+ player.play();
+ } else {
+ player.pause();
+ }
+ }
+ },
+ {
+ keys: [38], // UP
+ action: function(player, media) {
+ player.container.find('.mejs-volume-slider').css('display','block');
+ if (player.isVideo) {
+ player.showControls();
+ player.startControlsTimer();
+ }
+
+ var newVolume = Math.min(media.volume + 0.1, 1);
+ media.setVolume(newVolume);
+ }
+ },
+ {
+ keys: [40], // DOWN
+ action: function(player, media) {
+ player.container.find('.mejs-volume-slider').css('display','block');
+ if (player.isVideo) {
+ player.showControls();
+ player.startControlsTimer();
+ }
+
+ var newVolume = Math.max(media.volume - 0.1, 0);
+ media.setVolume(newVolume);
+ }
+ },
+ {
+ keys: [
+ 37, // LEFT
+ 227 // Google TV rewind
+ ],
+ action: function(player, media) {
+ if (!isNaN(media.duration) && media.duration > 0) {
+ if (player.isVideo) {
+ player.showControls();
+ player.startControlsTimer();
+ }
+
+ // 5%
+ var newTime = Math.max(media.currentTime - player.options.defaultSeekBackwardInterval(media), 0);
+ media.setCurrentTime(newTime);
+ }
+ }
+ },
+ {
+ keys: [
+ 39, // RIGHT
+ 228 // Google TV forward
+ ],
+ action: function(player, media) {
+ if (!isNaN(media.duration) && media.duration > 0) {
+ if (player.isVideo) {
+ player.showControls();
+ player.startControlsTimer();
+ }
+
+ // 5%
+ var newTime = Math.min(media.currentTime + player.options.defaultSeekForwardInterval(media), media.duration);
+ media.setCurrentTime(newTime);
+ }
+ }
+ },
+ {
+ keys: [70], // F
+ action: function(player, media) {
+ if (typeof player.enterFullScreen != 'undefined') {
+ if (player.isFullScreen) {
+ player.exitFullScreen();
+ } else {
+ player.enterFullScreen();
+ }
+ }
+ }
+ },
+ {
+ keys: [77], // M
+ action: function(player, media) {
+ player.container.find('.mejs-volume-slider').css('display','block');
+ if (player.isVideo) {
+ player.showControls();
+ player.startControlsTimer();
+ }
+ if (player.media.muted) {
+ player.setMuted(false);
+ } else {
+ player.setMuted(true);
+ }
+ }
+ }
+ ]
+ };
+
+ mejs.mepIndex = 0;
+
+ mejs.players = {};
+
+ // wraps a MediaElement object in player controls
+ mejs.MediaElementPlayer = function(node, o) {
+ // enforce object, even without "new" (via John Resig)
+ if ( !(this instanceof mejs.MediaElementPlayer) ) {
+ return new mejs.MediaElementPlayer(node, o);
+ }
+
+ var t = this;
+
+ // these will be reset after the MediaElement.success fires
+ t.$media = t.$node = $(node);
+ t.node = t.media = t.$media[0];
+
+ // check for existing player
+ if (typeof t.node.player != 'undefined') {
+ return t.node.player;
+ } else {
+ // attach player to DOM node for reference
+ t.node.player = t;
+ }
+
+
+ // try to get options from data-mejsoptions
+ if (typeof o == 'undefined') {
+ o = t.$node.data('mejsoptions');
+ }
+
+ // extend default options
+ t.options = $.extend({},mejs.MepDefaults,o);
+
+ // unique ID
+ t.id = 'mep_' + mejs.mepIndex++;
+
+ // add to player array (for focus events)
+ mejs.players[t.id] = t;
+
+ // start up
+ t.init();
+
+ return t;
+ };
+
+ // actual player
+ mejs.MediaElementPlayer.prototype = {
+
+ hasFocus: false,
+
+ controlsAreVisible: true,
+
+ init: function() {
+
+ var
+ t = this,
+ mf = mejs.MediaFeatures,
+ // options for MediaElement (shim)
+ meOptions = $.extend(true, {}, t.options, {
+ success: function(media, domNode) { t.meReady(media, domNode); },
+ error: function(e) { t.handleError(e);}
+ }),
+ tagName = t.media.tagName.toLowerCase();
+
+ t.isDynamic = (tagName !== 'audio' && tagName !== 'video');
+
+ if (t.isDynamic) {
+ // get video from src or href?
+ t.isVideo = t.options.isVideo;
+ } else {
+ t.isVideo = (tagName !== 'audio' && t.options.isVideo);
+ }
+
+ // use native controls in iPad, iPhone, and Android
+ if ((mf.isiPad && t.options.iPadUseNativeControls) || (mf.isiPhone && t.options.iPhoneUseNativeControls)) {
+
+ // add controls and stop
+ t.$media.attr('controls', 'controls');
+
+ // attempt to fix iOS 3 bug
+ //t.$media.removeAttr('poster');
+ // no Issue found on iOS3 -ttroxell
+
+ // override Apple's autoplay override for iPads
+ if (mf.isiPad && t.media.getAttribute('autoplay') !== null) {
+ t.play();
+ }
+
+ } else if (mf.isAndroid && t.options.AndroidUseNativeControls) {
+
+ // leave default player
+
+ } else {
+
+ // DESKTOP: use MediaElementPlayer controls
+
+ // remove native controls
+ t.$media.removeAttr('controls');
+ var videoPlayerTitle = t.isVideo ?
+ mejs.i18n.t('Video Player') : mejs.i18n.t('Audio Player');
+ // insert description for screen readers
+ $('<span class="mejs-offscreen">' + videoPlayerTitle + '</span>').insertBefore(t.$media);
+ // build container
+ t.container =
+ $('<div id="' + t.id + '" class="mejs-container ' + (mejs.MediaFeatures.svg ? 'svg' : 'no-svg') +
+ '" tabindex="0" role="application" aria-label="' + videoPlayerTitle + '">'+
+ '<div class="mejs-inner">'+
+ '<div class="mejs-mediaelement"></div>'+
+ '<div class="mejs-layers"></div>'+
+ '<div class="mejs-controls"></div>'+
+ '<div class="mejs-clear"></div>'+
+ '</div>' +
+ '</div>')
+ .addClass(t.$media[0].className)
+ .insertBefore(t.$media)
+ .focus(function ( e ) {
+ if( !t.controlsAreVisible ) {
+ t.showControls(true);
+ var playButton = t.container.find('.mejs-playpause-button > button');
+ playButton.focus();
+ }
+ });
+
+ // add classes for user and content
+ t.container.addClass(
+ (mf.isAndroid ? 'mejs-android ' : '') +
+ (mf.isiOS ? 'mejs-ios ' : '') +
+ (mf.isiPad ? 'mejs-ipad ' : '') +
+ (mf.isiPhone ? 'mejs-iphone ' : '') +
+ (t.isVideo ? 'mejs-video ' : 'mejs-audio ')
+ );
+
+
+ // move the <video/video> tag into the right spot
+ if (mf.isiOS) {
+
+ // sadly, you can't move nodes in iOS, so we have to destroy and recreate it!
+ var $newMedia = t.$media.clone();
+
+ t.container.find('.mejs-mediaelement').append($newMedia);
+
+ t.$media.remove();
+ t.$node = t.$media = $newMedia;
+ t.node = t.media = $newMedia[0];
+
+ } else {
+
+ // normal way of moving it into place (doesn't work on iOS)
+ t.container.find('.mejs-mediaelement').append(t.$media);
+ }
+
+ // find parts
+ t.controls = t.container.find('.mejs-controls');
+ t.layers = t.container.find('.mejs-layers');
+
+ // determine the size
+
+ /* size priority:
+ (1) videoWidth (forced),
+ (2) style="width;height;"
+ (3) width attribute,
+ (4) defaultVideoWidth (for unspecified cases)
+ */
+
+ var tagType = (t.isVideo ? 'video' : 'audio'),
+ capsTagName = tagType.substring(0,1).toUpperCase() + tagType.substring(1);
+
+
+
+ if (t.options[tagType + 'Width'] > 0 || t.options[tagType + 'Width'].toString().indexOf('%') > -1) {
+ t.width = t.options[tagType + 'Width'];
+ } else if (t.media.style.width !== '' && t.media.style.width !== null) {
+ t.width = t.media.style.width;
+ } else if (t.media.getAttribute('width') !== null) {
+ t.width = t.$media.attr('width');
+ } else {
+ t.width = t.options['default' + capsTagName + 'Width'];
+ }
+
+ if (t.options[tagType + 'Height'] > 0 || t.options[tagType + 'Height'].toString().indexOf('%') > -1) {
+ t.height = t.options[tagType + 'Height'];
+ } else if (t.media.style.height !== '' && t.media.style.height !== null) {
+ t.height = t.media.style.height;
+ } else if (t.$media[0].getAttribute('height') !== null) {
+ t.height = t.$media.attr('height');
+ } else {
+ t.height = t.options['default' + capsTagName + 'Height'];
+ }
+
+ // set the size, while we wait for the plugins to load below
+ t.setPlayerSize(t.width, t.height);
+
+ // create MediaElementShim
+ meOptions.pluginWidth = t.width;
+ meOptions.pluginHeight = t.height;
+ }
+
+ // create MediaElement shim
+ mejs.MediaElement(t.$media[0], meOptions);
+
+ if (typeof(t.container) != 'undefined' && t.controlsAreVisible){
+ // controls are shown when loaded
+ t.container.trigger('controlsshown');
+ }
+ },
+
+ showControls: function(doAnimation) {
+ var t = this;
+
+ doAnimation = typeof doAnimation == 'undefined' || doAnimation;
+
+ if (t.controlsAreVisible)
+ return;
+
+ if (doAnimation) {
+ t.controls
+ .css('visibility','visible')
+ .stop(true, true).fadeIn(200, function() {
+ t.controlsAreVisible = true;
+ t.container.trigger('controlsshown');
+ });
+
+ // any additional controls people might add and want to hide
+ t.container.find('.mejs-control')
+ .css('visibility','visible')
+ .stop(true, true).fadeIn(200, function() {t.controlsAreVisible = true;});
+
+ } else {
+ t.controls
+ .css('visibility','visible')
+ .css('display','block');
+
+ // any additional controls people might add and want to hide
+ t.container.find('.mejs-control')
+ .css('visibility','visible')
+ .css('display','block');
+
+ t.controlsAreVisible = true;
+ t.container.trigger('controlsshown');
+ }
+
+ t.setControlsSize();
+
+ },
+
+ hideControls: function(doAnimation) {
+ var t = this;
+
+ doAnimation = typeof doAnimation == 'undefined' || doAnimation;
+
+ if (!t.controlsAreVisible || t.options.alwaysShowControls || t.keyboardAction)
+ return;
+
+ if (doAnimation) {
+ // fade out main controls
+ t.controls.stop(true, true).fadeOut(200, function() {
+ $(this)
+ .css('visibility','hidden')
+ .css('display','block');
+
+ t.controlsAreVisible = false;
+ t.container.trigger('controlshidden');
+ });
+
+ // any additional controls people might add and want to hide
+ t.container.find('.mejs-control').stop(true, true).fadeOut(200, function() {
+ $(this)
+ .css('visibility','hidden')
+ .css('display','block');
+ });
+ } else {
+
+ // hide main controls
+ t.controls
+ .css('visibility','hidden')
+ .css('display','block');
+
+ // hide others
+ t.container.find('.mejs-control')
+ .css('visibility','hidden')
+ .css('display','block');
+
+ t.controlsAreVisible = false;
+ t.container.trigger('controlshidden');
+ }
+ },
+
+ controlsTimer: null,
+
+ startControlsTimer: function(timeout) {
+
+ var t = this;
+
+ timeout = typeof timeout != 'undefined' ? timeout : 1500;
+
+ t.killControlsTimer('start');
+
+ t.controlsTimer = setTimeout(function() {
+ //
+ t.hideControls();
+ t.killControlsTimer('hide');
+ }, timeout);
+ },
+
+ killControlsTimer: function(src) {
+
+ var t = this;
+
+ if (t.controlsTimer !== null) {
+ clearTimeout(t.controlsTimer);
+ delete t.controlsTimer;
+ t.controlsTimer = null;
+ }
+ },
+
+ controlsEnabled: true,
+
+ disableControls: function() {
+ var t= this;
+
+ t.killControlsTimer();
+ t.hideControls(false);
+ this.controlsEnabled = false;
+ },
+
+ enableControls: function() {
+ var t= this;
+
+ t.showControls(false);
+
+ t.controlsEnabled = true;
+ },
+
+
+ // Sets up all controls and events
+ meReady: function(media, domNode) {
+
+
+ var t = this,
+ mf = mejs.MediaFeatures,
+ autoplayAttr = domNode.getAttribute('autoplay'),
+ autoplay = !(typeof autoplayAttr == 'undefined' || autoplayAttr === null || autoplayAttr === 'false'),
+ featureIndex,
+ feature;
+
+ // make sure it can't create itself again if a plugin reloads
+ if (t.created) {
+ return;
+ } else {
+ t.created = true;
+ }
+
+ t.media = media;
+ t.domNode = domNode;
+
+ if (!(mf.isAndroid && t.options.AndroidUseNativeControls) && !(mf.isiPad && t.options.iPadUseNativeControls) && !(mf.isiPhone && t.options.iPhoneUseNativeControls)) {
+
+ // two built in features
+ t.buildposter(t, t.controls, t.layers, t.media);
+ t.buildkeyboard(t, t.controls, t.layers, t.media);
+ t.buildoverlays(t, t.controls, t.layers, t.media);
+
+ // grab for use by features
+ t.findTracks();
+
+ // add user-defined features/controls
+ for (featureIndex in t.options.features) {
+ feature = t.options.features[featureIndex];
+ if (t['build' + feature]) {
+ try {
+ t['build' + feature](t, t.controls, t.layers, t.media);
+ } catch (e) {
+ // TODO: report control error
+ //throw e;
+
+
+ }
+ }
+ }
+
+ t.container.trigger('controlsready');
+
+ // reset all layers and controls
+ t.setPlayerSize(t.width, t.height);
+ t.setControlsSize();
+
+
+ // controls fade
+ if (t.isVideo) {
+
+ if (mejs.MediaFeatures.hasTouch) {
+
+ // for touch devices (iOS, Android)
+ // show/hide without animation on touch
+
+ t.$media.bind('touchstart', function() {
+
+
+ // toggle controls
+ if (t.controlsAreVisible) {
+ t.hideControls(false);
+ } else {
+ if (t.controlsEnabled) {
+ t.showControls(false);
+ }
+ }
+ });
+
+ } else {
+
+ // create callback here since it needs access to current
+ // MediaElement object
+ t.clickToPlayPauseCallback = function() {
+ //
+
+ if (t.options.clickToPlayPause) {
+ if (t.media.paused) {
+ t.play();
+ } else {
+ t.pause();
+ }
+ }
+ };
+
+ // click to play/pause
+ t.media.addEventListener('click', t.clickToPlayPauseCallback, false);
+
+ // show/hide controls
+ t.container
+ .bind('mouseenter mouseover', function () {
+ if (t.controlsEnabled) {
+ if (!t.options.alwaysShowControls ) {
+ t.killControlsTimer('enter');
+ t.showControls();
+ t.startControlsTimer(2500);
+ }
+ }
+ })
+ .bind('mousemove', function() {
+ if (t.controlsEnabled) {
+ if (!t.controlsAreVisible) {
+ t.showControls();
+ }
+ if (!t.options.alwaysShowControls) {
+ t.startControlsTimer(2500);
+ }
+ }
+ })
+ .bind('mouseleave', function () {
+ if (t.controlsEnabled) {
+ if (!t.media.paused && !t.options.alwaysShowControls) {
+ t.startControlsTimer(1000);
+ }
+ }
+ });
+ }
+
+ if(t.options.hideVideoControlsOnLoad) {
+ t.hideControls(false);
+ }
+
+ // check for autoplay
+ if (autoplay && !t.options.alwaysShowControls) {
+ t.hideControls();
+ }
+
+ // resizer
+ if (t.options.enableAutosize) {
+ t.media.addEventListener('loadedmetadata', function(e) {
+ // if the <video height> was not set and the options.videoHeight was not set
+ // then resize to the real dimensions
+ if (t.options.videoHeight <= 0 && t.domNode.getAttribute('height') === null && !isNaN(e.target.videoHeight)) {
+ t.setPlayerSize(e.target.videoWidth, e.target.videoHeight);
+ t.setControlsSize();
+ t.media.setVideoSize(e.target.videoWidth, e.target.videoHeight);
+ }
+ }, false);
+ }
+ }
+
+ // EVENTS
+
+ // FOCUS: when a video starts playing, it takes focus from other players (possibily pausing them)
+ media.addEventListener('play', function() {
+ var playerIndex;
+
+ // go through all other players
+ for (playerIndex in mejs.players) {
+ var p = mejs.players[playerIndex];
+ if (p.id != t.id && t.options.pauseOtherPlayers && !p.paused && !p.ended) {
+ p.pause();
+ }
+ p.hasFocus = false;
+ }
+
+ t.hasFocus = true;
+ },false);
+
+
+ // ended for all
+ t.media.addEventListener('ended', function (e) {
+ if(t.options.autoRewind) {
+ try{
+ t.media.setCurrentTime(0);
+ // Fixing an Android stock browser bug, where "seeked" isn't fired correctly after ending the video and jumping to the beginning
+ window.setTimeout(function(){
+ $(t.container).find('.mejs-overlay-loading').parent().hide();
+ }, 20);
+ } catch (exp) {
+
+ }
+ }
+ t.media.pause();
+
+ if (t.setProgressRail) {
+ t.setProgressRail();
+ }
+ if (t.setCurrentRail) {
+ t.setCurrentRail();
+ }
+
+ if (t.options.loop) {
+ t.play();
+ } else if (!t.options.alwaysShowControls && t.controlsEnabled) {
+ t.showControls();
+ }
+ }, false);
+
+ // resize on the first play
+ t.media.addEventListener('loadedmetadata', function(e) {
+ if (t.updateDuration) {
+ t.updateDuration();
+ }
+ if (t.updateCurrent) {
+ t.updateCurrent();
+ }
+
+ if (!t.isFullScreen) {
+ t.setPlayerSize(t.width, t.height);
+ t.setControlsSize();
+ }
+ }, false);
+
+ t.container.focusout(function (e) {
+ if( e.relatedTarget ) { //FF is working on supporting focusout https://bugzilla.mozilla.org/show_bug.cgi?id=687787
+ var $target = $(e.relatedTarget);
+ if (t.keyboardAction && $target.parents('.mejs-container').length === 0) {
+ t.keyboardAction = false;
+ t.hideControls(true);
+ }
+ }
+ });
+
+ // webkit has trouble doing this without a delay
+ setTimeout(function () {
+ t.setPlayerSize(t.width, t.height);
+ t.setControlsSize();
+ }, 50);
+
+ // adjust controls whenever window sizes (used to be in fullscreen only)
+ t.globalBind('resize', function() {
+
+ // don't resize for fullscreen mode
+ if ( !(t.isFullScreen || (mejs.MediaFeatures.hasTrueNativeFullScreen && document.webkitIsFullScreen)) ) {
+ t.setPlayerSize(t.width, t.height);
+ }
+
+ // always adjust controls
+ t.setControlsSize();
+ });
+
+ // This is a work-around for a bug in the YouTube iFrame player, which means
+ // we can't use the play() API for the initial playback on iOS or Android;
+ // user has to start playback directly by tapping on the iFrame.
+ if (t.media.pluginType == 'youtube' && ( mf.isiOS || mf.isAndroid ) ) {
+ t.container.find('.mejs-overlay-play').hide();
+ }
+ }
+
+ // force autoplay for HTML5
+ if (autoplay && media.pluginType == 'native') {
+ t.play();
+ }
+
+
+ if (t.options.success) {
+
+ if (typeof t.options.success == 'string') {
+ window[t.options.success](t.media, t.domNode, t);
+ } else {
+ t.options.success(t.media, t.domNode, t);
+ }
+ }
+ },
+
+ handleError: function(e) {
+ var t = this;
+
+ t.controls.hide();
+
+ // Tell user that the file cannot be played
+ if (t.options.error) {
+ t.options.error(e);
+ }
+ },
+
+ setPlayerSize: function(width,height) {
+ var t = this;
+
+ if( !t.options.setDimensions ) {
+ return false;
+ }
+
+ if (typeof width != 'undefined') {
+ t.width = width;
+ }
+
+ if (typeof height != 'undefined') {
+ t.height = height;
+ }
+
+ // detect 100% mode - use currentStyle for IE since css() doesn't return percentages
+ if (t.height.toString().indexOf('%') > 0 || t.$node.css('max-width') === '100%' || (t.$node[0].currentStyle && t.$node[0].currentStyle.maxWidth === '100%')) {
+
+ // do we have the native dimensions yet?
+ var nativeWidth = (function() {
+ if (t.isVideo) {
+ if (t.media.videoWidth && t.media.videoWidth > 0) {
+ return t.media.videoWidth;
+ } else if (t.media.getAttribute('width') !== null) {
+ return t.media.getAttribute('width');
+ } else {
+ return t.options.defaultVideoWidth;
+ }
+ } else {
+ return t.options.defaultAudioWidth;
+ }
+ })();
+
+ var nativeHeight = (function() {
+ if (t.isVideo) {
+ if (t.media.videoHeight && t.media.videoHeight > 0) {
+ return t.media.videoHeight;
+ } else if (t.media.getAttribute('height') !== null) {
+ return t.media.getAttribute('height');
+ } else {
+ return t.options.defaultVideoHeight;
+ }
+ } else {
+ return t.options.defaultAudioHeight;
+ }
+ })();
+
+ var
+ parentWidth = t.container.parent().closest(':visible').width(),
+ parentHeight = t.container.parent().closest(':visible').height(),
+ newHeight = t.isVideo || !t.options.autosizeProgress ? parseInt(parentWidth * nativeHeight/nativeWidth, 10) : nativeHeight;
+
+ // When we use percent, the newHeight can't be calculated so we get the container height
+ if (isNaN(newHeight)) {
+ newHeight = parentHeight;
+ }
+
+ if (t.container.parent()[0].tagName.toLowerCase() === 'body') { // && t.container.siblings().count == 0) {
+ parentWidth = $(window).width();
+ newHeight = $(window).height();
+ }
+
+ if ( newHeight && parentWidth ) {
+
+ // set outer container size
+ t.container
+ .width(parentWidth)
+ .height(newHeight);
+
+ // set native <video> or <audio> and shims
+ t.$media.add(t.container.find('.mejs-shim'))
+ .width('100%')
+ .height('100%');
+
+ // if shim is ready, send the size to the embeded plugin
+ if (t.isVideo) {
+ if (t.media.setVideoSize) {
+ t.media.setVideoSize(parentWidth, newHeight);
+ }
+ }
+
+ // set the layers
+ t.layers.children('.mejs-layer')
+ .width('100%')
+ .height('100%');
+ }
+
+
+ } else {
+
+ t.container
+ .width(t.width)
+ .height(t.height);
+
+ t.layers.children('.mejs-layer')
+ .width(t.width)
+ .height(t.height);
+
+ }
+
+ // special case for big play button so it doesn't go over the controls area
+ var playLayer = t.layers.find('.mejs-overlay-play'),
+ playButton = playLayer.find('.mejs-overlay-button');
+
+ playLayer.height(t.container.height() - t.controls.height());
+ playButton.css('margin-top', '-' + (playButton.height()/2 - t.controls.height()/2).toString() + 'px' );
+
+ },
+
+ setControlsSize: function() {
+ var t = this,
+ usedWidth = 0,
+ railWidth = 0,
+ rail = t.controls.find('.mejs-time-rail'),
+ total = t.controls.find('.mejs-time-total'),
+ current = t.controls.find('.mejs-time-current'),
+ loaded = t.controls.find('.mejs-time-loaded'),
+ others = rail.siblings(),
+ lastControl = others.last(),
+ lastControlPosition = null;
+
+ // skip calculation if hidden
+ if (!t.container.is(':visible') || !rail.length || !rail.is(':visible')) {
+ return;
+ }
+
+
+ // allow the size to come from custom CSS
+ if (t.options && !t.options.autosizeProgress) {
+ // Also, frontends devs can be more flexible
+ // due the opportunity of absolute positioning.
+ railWidth = parseInt(rail.css('width'), 10);
+ }
+
+ // attempt to autosize
+ if (railWidth === 0 || !railWidth) {
+
+ // find the size of all the other controls besides the rail
+ others.each(function() {
+ var $this = $(this);
+ if ($this.css('position') != 'absolute' && $this.is(':visible')) {
+ usedWidth += $(this).outerWidth(true);
+ }
+ });
+
+ // fit the rail into the remaining space
+ railWidth = t.controls.width() - usedWidth - (rail.outerWidth(true) - rail.width());
+ }
+
+ // resize the rail,
+ // but then check if the last control (say, the fullscreen button) got pushed down
+ // this often happens when zoomed
+ do {
+ // outer area
+ rail.width(railWidth);
+ // dark space
+ total.width(railWidth - (total.outerWidth(true) - total.width()));
+
+ if (lastControl.css('position') != 'absolute') {
+ lastControlPosition = lastControl.position();
+ railWidth--;
+ }
+ } while (lastControlPosition !== null && lastControlPosition.top > 0 && railWidth > 0);
+
+ if (t.setProgressRail)
+ t.setProgressRail();
+ if (t.setCurrentRail)
+ t.setCurrentRail();
+ },
+
+
+ buildposter: function(player, controls, layers, media) {
+ var t = this,
+ poster =
+ $('<div class="mejs-poster mejs-layer">' +
+ '</div>')
+ .appendTo(layers),
+ posterUrl = player.$media.attr('poster');
+
+ // prioriy goes to option (this is useful if you need to support iOS 3.x (iOS completely fails with poster)
+ if (player.options.poster !== '') {
+ posterUrl = player.options.poster;
+ }
+
+ // second, try the real poster
+ if ( posterUrl ) {
+ t.setPoster(posterUrl);
+ } else {
+ poster.hide();
+ }
+
+ media.addEventListener('play',function() {
+ poster.hide();
+ }, false);
+
+ if(player.options.showPosterWhenEnded && player.options.autoRewind){
+ media.addEventListener('ended',function() {
+ poster.show();
+ }, false);
+ }
+ },
+
+ setPoster: function(url) {
+ var t = this,
+ posterDiv = t.container.find('.mejs-poster'),
+ posterImg = posterDiv.find('img');
+
+ if (posterImg.length === 0) {
+ posterImg = $('<img width="100%" height="100%" />').appendTo(posterDiv);
+ }
+
+ posterImg.attr('src', url);
+ posterDiv.css({'background-image' : 'url(' + url + ')'});
+ },
+
+ buildoverlays: function(player, controls, layers, media) {
+ var t = this;
+ if (!player.isVideo)
+ return;
+
+ var
+ loading =
+ $('<div class="mejs-overlay mejs-layer">'+
+ '<div class="mejs-overlay-loading"><span></span></div>'+
+ '</div>')
+ .hide() // start out hidden
+ .appendTo(layers),
+ error =
+ $('<div class="mejs-overlay mejs-layer">'+
+ '<div class="mejs-overlay-error"></div>'+
+ '</div>')
+ .hide() // start out hidden
+ .appendTo(layers),
+ // this needs to come last so it's on top
+ bigPlay =
+ $('<div class="mejs-overlay mejs-layer mejs-overlay-play">'+
+ '<div class="mejs-overlay-button"></div>'+
+ '</div>')
+ .appendTo(layers)
+ .bind('click', function() { // Removed 'touchstart' due issues on Samsung Android devices where a tap on bigPlay started and immediately stopped the video
+ if (t.options.clickToPlayPause) {
+ if (media.paused) {
+ media.play();
+ }
+ }
+ });
+
+ /*
+ if (mejs.MediaFeatures.isiOS || mejs.MediaFeatures.isAndroid) {
+ bigPlay.remove();
+ loading.remove();
+ }
+ */
+
+
+ // show/hide big play button
+ media.addEventListener('play',function() {
+ bigPlay.hide();
+ loading.hide();
+ controls.find('.mejs-time-buffering').hide();
+ error.hide();
+ }, false);
+
+ media.addEventListener('playing', function() {
+ bigPlay.hide();
+ loading.hide();
+ controls.find('.mejs-time-buffering').hide();
+ error.hide();
+ }, false);
+
+ media.addEventListener('seeking', function() {
+ loading.show();
+ controls.find('.mejs-time-buffering').show();
+ }, false);
+
+ media.addEventListener('seeked', function() {
+ loading.hide();
+ controls.find('.mejs-time-buffering').hide();
+ }, false);
+
+ media.addEventListener('pause',function() {
+ if (!mejs.MediaFeatures.isiPhone) {
+ bigPlay.show();
+ }
+ }, false);
+
+ media.addEventListener('waiting', function() {
+ loading.show();
+ controls.find('.mejs-time-buffering').show();
+ }, false);
+
+
+ // show/hide loading
+ media.addEventListener('loadeddata',function() {
+ // for some reason Chrome is firing this event
+ //if (mejs.MediaFeatures.isChrome && media.getAttribute && media.getAttribute('preload') === 'none')
+ // return;
+
+ loading.show();
+ controls.find('.mejs-time-buffering').show();
+ // Firing the 'canplay' event after a timeout which isn't getting fired on some Android 4.1 devices (https://github.com/johndyer/mediaelement/issues/1305)
+ if (mejs.MediaFeatures.isAndroid) {
+ media.canplayTimeout = window.setTimeout(
+ function() {
+ if (document.createEvent) {
+ var evt = document.createEvent('HTMLEvents');
+ evt.initEvent('canplay', true, true);
+ return media.dispatchEvent(evt);
+ }
+ }, 300
+ );
+ }
+ }, false);
+ media.addEventListener('canplay',function() {
+ loading.hide();
+ controls.find('.mejs-time-buffering').hide();
+ clearTimeout(media.canplayTimeout); // Clear timeout inside 'loadeddata' to prevent 'canplay' to fire twice
+ }, false);
+
+ // error handling
+ media.addEventListener('error',function() {
+ loading.hide();
+ controls.find('.mejs-time-buffering').hide();
+ error.show();
+ error.find('mejs-overlay-error').html("Error loading this resource");
+ }, false);
+
+ media.addEventListener('keydown', function(e) {
+ t.onkeydown(player, media, e);
+ }, false);
+ },
+
+ buildkeyboard: function(player, controls, layers, media) {
+
+ var t = this;
+
+ t.container.keydown(function () {
+ t.keyboardAction = true;
+ });
+
+ // listen for key presses
+ t.globalBind('keydown', function(e) {
+ return t.onkeydown(player, media, e);
+ });
+
+
+ // check if someone clicked outside a player region, then kill its focus
+ t.globalBind('click', function(event) {
+ player.hasFocus = $(event.target).closest('.mejs-container').length !== 0;
+ });
+
+ },
+ onkeydown: function(player, media, e) {
+ if (player.hasFocus && player.options.enableKeyboard) {
+ // find a matching key
+ for (var i = 0, il = player.options.keyActions.length; i < il; i++) {
+ var keyAction = player.options.keyActions[i];
+
+ for (var j = 0, jl = keyAction.keys.length; j < jl; j++) {
+ if (e.keyCode == keyAction.keys[j]) {
+ if (typeof(e.preventDefault) == "function") e.preventDefault();
+ keyAction.action(player, media, e.keyCode);
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ },
+
+ findTracks: function() {
+ var t = this,
+ tracktags = t.$media.find('track');
+
+ // store for use by plugins
+ t.tracks = [];
+ tracktags.each(function(index, track) {
+
+ track = $(track);
+
+ t.tracks.push({
+ srclang: (track.attr('srclang')) ? track.attr('srclang').toLowerCase() : '',
+ src: track.attr('src'),
+ kind: track.attr('kind'),
+ label: track.attr('label') || '',
+ entries: [],
+ isLoaded: false
+ });
+ });
+ },
+ changeSkin: function(className) {
+ this.container[0].className = 'mejs-container ' + className;
+ this.setPlayerSize(this.width, this.height);
+ this.setControlsSize();
+ },
+ play: function() {
+ this.load();
+ this.media.play();
+ },
+ pause: function() {
+ try {
+ this.media.pause();
+ } catch (e) {}
+ },
+ load: function() {
+ if (!this.isLoaded) {
+ this.media.load();
+ }
+
+ this.isLoaded = true;
+ },
+ setMuted: function(muted) {
+ this.media.setMuted(muted);
+ },
+ setCurrentTime: function(time) {
+ this.media.setCurrentTime(time);
+ },
+ getCurrentTime: function() {
+ return this.media.currentTime;
+ },
+ setVolume: function(volume) {
+ this.media.setVolume(volume);
+ },
+ getVolume: function() {
+ return this.media.volume;
+ },
+ setSrc: function(src) {
+ this.media.setSrc(src);
+ },
+ remove: function() {
+ var t = this, featureIndex, feature;
+
+ // invoke features cleanup
+ for (featureIndex in t.options.features) {
+ feature = t.options.features[featureIndex];
+ if (t['clean' + feature]) {
+ try {
+ t['clean' + feature](t);
+ } catch (e) {
+ // TODO: report control error
+ //throw e;
+ //
+ //
+ }
+ }
+ }
+
+ // grab video and put it back in place
+ if (!t.isDynamic) {
+ t.$media.prop('controls', true);
+ // detach events from the video
+ // TODO: detach event listeners better than this;
+ // also detach ONLY the events attached by this plugin!
+ t.$node.clone().insertBefore(t.container).show();
+ t.$node.remove();
+ } else {
+ t.$node.insertBefore(t.container);
+ }
+
+ if (t.media.pluginType !== 'native') {
+ t.media.remove();
+ }
+
+ // Remove the player from the mejs.players object so that pauseOtherPlayers doesn't blow up when trying to pause a non existance flash api.
+ delete mejs.players[t.id];
+
+ if (typeof t.container == 'object') {
+ t.container.remove();
+ }
+ t.globalUnbind();
+ delete t.node.player;
+ }
+ };
+
+ (function(){
+ var rwindow = /^((after|before)print|(before)?unload|hashchange|message|o(ff|n)line|page(hide|show)|popstate|resize|storage)\b/;
+
+ function splitEvents(events, id) {
+ // add player ID as an event namespace so it's easier to unbind them all later
+ var ret = {d: [], w: []};
+ $.each((events || '').split(' '), function(k, v){
+ var eventname = v + '.' + id;
+ if (eventname.indexOf('.') === 0) {
+ ret.d.push(eventname);
+ ret.w.push(eventname);
+ }
+ else {
+ ret[rwindow.test(v) ? 'w' : 'd'].push(eventname);
+ }
+ });
+ ret.d = ret.d.join(' ');
+ ret.w = ret.w.join(' ');
+ return ret;
+ }
+
+ mejs.MediaElementPlayer.prototype.globalBind = function(events, data, callback) {
+ var t = this;
+ events = splitEvents(events, t.id);
+ if (events.d) $(document).bind(events.d, data, callback);
+ if (events.w) $(window).bind(events.w, data, callback);
+ };
+
+ mejs.MediaElementPlayer.prototype.globalUnbind = function(events, callback) {
+ var t = this;
+ events = splitEvents(events, t.id);
+ if (events.d) $(document).unbind(events.d, callback);
+ if (events.w) $(window).unbind(events.w, callback);
+ };
+ })();
+
+ // turn into jQuery plugin
+ if (typeof $ != 'undefined') {
+ $.fn.mediaelementplayer = function (options) {
+ if (options === false) {
+ this.each(function () {
+ var player = $(this).data('mediaelementplayer');
+ if (player) {
+ player.remove();
+ }
+ $(this).removeData('mediaelementplayer');
+ });
+ }
+ else {
+ this.each(function () {
+ $(this).data('mediaelementplayer', new mejs.MediaElementPlayer(this, options));
+ });
+ }
+ return this;
+ };
+
+
+ $(document).ready(function() {
+ // auto enable using JSON attribute
+ $('.mejs-player').mediaelementplayer();
+ });
+ }
+
+ // push out to window
+ window.MediaElementPlayer = mejs.MediaElementPlayer;
+
+})(mejs.$);
+
+(function($) {
+
+ $.extend(mejs.MepDefaults, {
+ playText: mejs.i18n.t('Play'),
+ pauseText: mejs.i18n.t('Pause')
+ });
+
+ // PLAY/pause BUTTON
+ $.extend(MediaElementPlayer.prototype, {
+ buildplaypause: function(player, controls, layers, media) {
+ var
+ t = this,
+ op = t.options,
+ play =
+ $('<div class="mejs-button mejs-playpause-button mejs-play" >' +
+ '<button type="button" aria-controls="' + t.id + '" title="' + op.playText + '" aria-label="' + op.playText + '"></button>' +
+ '</div>')
+ .appendTo(controls)
+ .click(function(e) {
+ e.preventDefault();
+
+ if (media.paused) {
+ media.play();
+ } else {
+ media.pause();
+ }
+
+ return false;
+ }),
+ play_btn = play.find('button');
+
+
+ function togglePlayPause(which) {
+ if ('play' === which) {
+ play.removeClass('mejs-play').addClass('mejs-pause');
+ play_btn.attr({
+ 'title': op.pauseText,
+ 'aria-label': op.pauseText
+ });
+ } else {
+ play.removeClass('mejs-pause').addClass('mejs-play');
+ play_btn.attr({
+ 'title': op.playText,
+ 'aria-label': op.playText
+ });
+ }
+ };
+ togglePlayPause('pse');
+
+
+ media.addEventListener('play',function() {
+ togglePlayPause('play');
+ }, false);
+ media.addEventListener('playing',function() {
+ togglePlayPause('play');
+ }, false);
+
+
+ media.addEventListener('pause',function() {
+ togglePlayPause('pse');
+ }, false);
+ media.addEventListener('paused',function() {
+ togglePlayPause('pse');
+ }, false);
+ }
+ });
+
+})(mejs.$);
+
+(function($) {
+
+ $.extend(mejs.MepDefaults, {
+ stopText: 'Stop'
+ });
+
+ // STOP BUTTON
+ $.extend(MediaElementPlayer.prototype, {
+ buildstop: function(player, controls, layers, media) {
+ var t = this,
+ stop =
+ $('<div class="mejs-button mejs-stop-button mejs-stop">' +
+ '<button type="button" aria-controls="' + t.id + '" title="' + t.options.stopText + '" aria-label="' + t.options.stopText + '"></button>' +
+ '</div>')
+ .appendTo(controls)
+ .click(function() {
+ if (!media.paused) {
+ media.pause();
+ }
+ if (media.currentTime > 0) {
+ media.setCurrentTime(0);
+ media.pause();
+ controls.find('.mejs-time-current').width('0px');
+ controls.find('.mejs-time-handle').css('left', '0px');
+ controls.find('.mejs-time-float-current').html( mejs.Utility.secondsToTimeCode(0) );
+ controls.find('.mejs-currenttime').html( mejs.Utility.secondsToTimeCode(0) );
+ layers.find('.mejs-poster').show();
+ }
+ });
+ }
+ });
+
+})(mejs.$);
+
+(function($) {
+
+ $.extend(mejs.MepDefaults, {
+ progessHelpText: mejs.i18n.t(
+ 'Use Left/Right Arrow keys to advance one second, Up/Down arrows to advance ten seconds.')
+ });
+
+ // progress/loaded bar
+ $.extend(MediaElementPlayer.prototype, {
+ buildprogress: function(player, controls, layers, media) {
+
+ $('<div class="mejs-time-rail">' +
+ '<a href="javascript:void(0);" class="mejs-time-total mejs-time-slider">' +
+ '<span class="mejs-offscreen">' + this.options.progessHelpText + '</span>' +
+ '<span class="mejs-time-buffering"></span>' +
+ '<span class="mejs-time-loaded"></span>' +
+ '<span class="mejs-time-current"></span>' +
+ '<span class="mejs-time-handle"></span>' +
+ '<span class="mejs-time-float">' +
+ '<span class="mejs-time-float-current">00:00</span>' +
+ '<span class="mejs-time-float-corner"></span>' +
+ '</span>' +
+ '</a>' +
+ '</div>')
+ .appendTo(controls);
+ controls.find('.mejs-time-buffering').hide();
+
+ var
+ t = this,
+ total = controls.find('.mejs-time-total'),
+ loaded = controls.find('.mejs-time-loaded'),
+ current = controls.find('.mejs-time-current'),
+ handle = controls.find('.mejs-time-handle'),
+ timefloat = controls.find('.mejs-time-float'),
+ timefloatcurrent = controls.find('.mejs-time-float-current'),
+ slider = controls.find('.mejs-time-slider'),
+ handleMouseMove = function (e) {
+
+ var offset = total.offset(),
+ width = total.outerWidth(true),
+ percentage = 0,
+ newTime = 0,
+ pos = 0,
+ x;
+
+ // mouse or touch position relative to the object
+ if (e.originalEvent.changedTouches) {
+ x = e.originalEvent.changedTouches[0].pageX;
+ }else{
+ x = e.pageX;
+ }
+
+ if (media.duration) {
+ if (x < offset.left) {
+ x = offset.left;
+ } else if (x > width + offset.left) {
+ x = width + offset.left;
+ }
+
+ pos = x - offset.left;
+ percentage = (pos / width);
+ newTime = (percentage <= 0.02) ? 0 : percentage * media.duration;
+
+ // seek to where the mouse is
+ if (mouseIsDown && newTime !== media.currentTime) {
+ media.setCurrentTime(newTime);
+ }
+
+ // position floating time box
+ if (!mejs.MediaFeatures.hasTouch) {
+ timefloat.css('left', pos);
+ timefloatcurrent.html( mejs.Utility.secondsToTimeCode(newTime) );
+ timefloat.show();
+ }
+ }
+ },
+ mouseIsDown = false,
+ mouseIsOver = false,
+ lastKeyPressTime = 0,
+ startedPaused = false,
+ autoRewindInitial = player.options.autoRewind;
+ // Accessibility for slider
+ var updateSlider = function (e) {
+
+ var seconds = media.currentTime,
+ timeSliderText = mejs.i18n.t('Time Slider'),
+ time = mejs.Utility.secondsToTimeCode(seconds),
+ duration = media.duration;
+
+ slider.attr({
+ 'aria-label': timeSliderText,
+ 'aria-valuemin': 0,
+ 'aria-valuemax': duration,
+ 'aria-valuenow': seconds,
+ 'aria-valuetext': time,
+ 'role': 'slider',
+ 'tabindex': 0
+ });
+
+ };
+
+ var restartPlayer = function () {
+ var now = new Date();
+ if (now - lastKeyPressTime >= 1000) {
+ media.play();
+ }
+ };
+
+ slider.bind('focus', function (e) {
+ player.options.autoRewind = false;
+ });
+
+ slider.bind('blur', function (e) {
+ player.options.autoRewind = autoRewindInitial;
+ });
+
+ slider.bind('keydown', function (e) {
+
+ if ((new Date() - lastKeyPressTime) >= 1000) {
+ startedPaused = media.paused;
+ }
+
+ var keyCode = e.keyCode,
+ duration = media.duration,
+ seekTime = media.currentTime;
+
+ switch (keyCode) {
+ case 37: // left
+ seekTime -= 1;
+ break;
+ case 39: // Right
+ seekTime += 1;
+ break;
+ case 38: // Up
+ seekTime += Math.floor(duration * 0.1);
+ break;
+ case 40: // Down
+ seekTime -= Math.floor(duration * 0.1);
+ break;
+ case 36: // Home
+ seekTime = 0;
+ break;
+ case 35: // end
+ seekTime = duration;
+ break;
+ case 10: // enter
+ media.paused ? media.play() : media.pause();
+ return;
+ case 13: // space
+ media.paused ? media.play() : media.pause();
+ return;
+ default:
+ return;
+ }
+
+ seekTime = seekTime < 0 ? 0 : (seekTime >= duration ? duration : Math.floor(seekTime));
+ lastKeyPressTime = new Date();
+ if (!startedPaused) {
+ media.pause();
+ }
+
+ if (seekTime < media.duration && !startedPaused) {
+ setTimeout(restartPlayer, 1100);
+ }
+
+ media.setCurrentTime(seekTime);
+
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ });
+
+
+ // handle clicks
+ //controls.find('.mejs-time-rail').delegate('span', 'click', handleMouseMove);
+ total
+ .bind('mousedown touchstart', function (e) {
+ // only handle left clicks or touch
+ if (e.which === 1 || e.which === 0) {
+ mouseIsDown = true;
+ handleMouseMove(e);
+ t.globalBind('mousemove.dur touchmove.dur', function(e) {
+ handleMouseMove(e);
+ });
+ t.globalBind('mouseup.dur touchend.dur', function (e) {
+ mouseIsDown = false;
+ timefloat.hide();
+ t.globalUnbind('.dur');
+ });
+ }
+ })
+ .bind('mouseenter', function(e) {
+ mouseIsOver = true;
+ t.globalBind('mousemove.dur', function(e) {
+ handleMouseMove(e);
+ });
+ if (!mejs.MediaFeatures.hasTouch) {
+ timefloat.show();
+ }
+ })
+ .bind('mouseleave',function(e) {
+ mouseIsOver = false;
+ if (!mouseIsDown) {
+ t.globalUnbind('.dur');
+ timefloat.hide();
+ }
+ });
+
+ // loading
+ media.addEventListener('progress', function (e) {
+ player.setProgressRail(e);
+ player.setCurrentRail(e);
+ }, false);
+
+ // current time
+ media.addEventListener('timeupdate', function(e) {
+ player.setProgressRail(e);
+ player.setCurrentRail(e);
+ updateSlider(e);
+ }, false);
+
+
+ // store for later use
+ t.loaded = loaded;
+ t.total = total;
+ t.current = current;
+ t.handle = handle;
+ },
+ setProgressRail: function(e) {
+
+ var
+ t = this,
+ target = (e !== undefined) ? e.target : t.media,
+ percent = null;
+
+ // newest HTML5 spec has buffered array (FF4, Webkit)
+ if (target && target.buffered && target.buffered.length > 0 && target.buffered.end && target.duration) {
+ // TODO: account for a real array with multiple values (only Firefox 4 has this so far)
+ percent = target.buffered.end(0) / target.duration;
+ }
+ // Some browsers (e.g., FF3.6 and Safari 5) cannot calculate target.bufferered.end()
+ // to be anything other than 0. If the byte count is available we use this instead.
+ // Browsers that support the else if do not seem to have the bufferedBytes value and
+ // should skip to there. Tested in Safari 5, Webkit head, FF3.6, Chrome 6, IE 7/8.
+ else if (target && target.bytesTotal !== undefined && target.bytesTotal > 0 && target.bufferedBytes !== undefined) {
+ percent = target.bufferedBytes / target.bytesTotal;
+ }
+ // Firefox 3 with an Ogg file seems to go this way
+ else if (e && e.lengthComputable && e.total !== 0) {
+ percent = e.loaded / e.total;
+ }
+
+ // finally update the progress bar
+ if (percent !== null) {
+ percent = Math.min(1, Math.max(0, percent));
+ // update loaded bar
+ if (t.loaded && t.total) {
+ t.loaded.width(t.total.width() * percent);
+ }
+ }
+ },
+ setCurrentRail: function() {
+
+ var t = this;
+
+ if (t.media.currentTime !== undefined && t.media.duration) {
+
+ // update bar and handle
+ if (t.total && t.handle) {
+ var
+ newWidth = Math.round(t.total.width() * t.media.currentTime / t.media.duration),
+ handlePos = newWidth - Math.round(t.handle.outerWidth(true) / 2);
+
+ t.current.width(newWidth);
+ t.handle.css('left', handlePos);
+ }
+ }
+
+ }
+ });
+})(mejs.$);
+(function($) {
+
+ // options
+ $.extend(mejs.MepDefaults, {
+ duration: -1,
+ timeAndDurationSeparator: '<span> | </span>'
+ });
+
+
+ // current and duration 00:00 / 00:00
+ $.extend(MediaElementPlayer.prototype, {
+ buildcurrent: function(player, controls, layers, media) {
+ var t = this;
+
+ $('<div class="mejs-time" role="timer" aria-live="off">' +
+ '<span class="mejs-currenttime">' +
+ (player.options.alwaysShowHours ? '00:' : '') +
+ (player.options.showTimecodeFrameCount? '00:00:00':'00:00') +
+ '</span>'+
+ '</div>')
+ .appendTo(controls);
+
+ t.currenttime = t.controls.find('.mejs-currenttime');
+
+ media.addEventListener('timeupdate',function() {
+ player.updateCurrent();
+ }, false);
+ },
+
+
+ buildduration: function(player, controls, layers, media) {
+ var t = this;
+
+ if (controls.children().last().find('.mejs-currenttime').length > 0) {
+ $(t.options.timeAndDurationSeparator +
+ '<span class="mejs-duration">' +
+ (t.options.duration > 0 ?
+ mejs.Utility.secondsToTimeCode(t.options.duration, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25) :
+ ((player.options.alwaysShowHours ? '00:' : '') + (player.options.showTimecodeFrameCount? '00:00:00':'00:00'))
+ ) +
+ '</span>')
+ .appendTo(controls.find('.mejs-time'));
+ } else {
+
+ // add class to current time
+ controls.find('.mejs-currenttime').parent().addClass('mejs-currenttime-container');
+
+ $('<div class="mejs-time mejs-duration-container">'+
+ '<span class="mejs-duration">' +
+ (t.options.duration > 0 ?
+ mejs.Utility.secondsToTimeCode(t.options.duration, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25) :
+ ((player.options.alwaysShowHours ? '00:' : '') + (player.options.showTimecodeFrameCount? '00:00:00':'00:00'))
+ ) +
+ '</span>' +
+ '</div>')
+ .appendTo(controls);
+ }
+
+ t.durationD = t.controls.find('.mejs-duration');
+
+ media.addEventListener('timeupdate',function() {
+ player.updateDuration();
+ }, false);
+ },
+
+ updateCurrent: function() {
+ var t = this;
+
+ if (t.currenttime) {
+ t.currenttime.html(mejs.Utility.secondsToTimeCode(t.media.currentTime, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25));
+ }
+ },
+
+ updateDuration: function() {
+ var t = this;
+
+ //Toggle the long video class if the video is longer than an hour.
+ t.container.toggleClass("mejs-long-video", t.media.duration > 3600);
+
+ if (t.durationD && (t.options.duration > 0 || t.media.duration)) {
+ t.durationD.html(mejs.Utility.secondsToTimeCode(t.options.duration > 0 ? t.options.duration : t.media.duration, t.options.alwaysShowHours, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25));
+ }
+ }
+ });
+
+})(mejs.$);
+
+(function($) {
+
+ $.extend(mejs.MepDefaults, {
+ muteText: mejs.i18n.t('Mute Toggle'),
+ allyVolumeControlText: mejs.i18n.t('Use Up/Down Arrow keys to increase or decrease volume.'),
+ hideVolumeOnTouchDevices: true,
+
+ audioVolume: 'horizontal',
+ videoVolume: 'vertical'
+ });
+
+ $.extend(MediaElementPlayer.prototype, {
+ buildvolume: function(player, controls, layers, media) {
+
+ // Android and iOS don't support volume controls
+ if ((mejs.MediaFeatures.isAndroid || mejs.MediaFeatures.isiOS) && this.options.hideVolumeOnTouchDevices)
+ return;
+
+ var t = this,
+ mode = (t.isVideo) ? t.options.videoVolume : t.options.audioVolume,
+ mute = (mode == 'horizontal') ?
+
+ // horizontal version
+ $('<div class="mejs-button mejs-volume-button mejs-mute">' +
+ '<button type="button" aria-controls="' + t.id +
+ '" title="' + t.options.muteText +
+ '" aria-label="' + t.options.muteText +
+ '"></button>'+
+ '</div>' +
+ '<a href="javascript:void(0);" class="mejs-horizontal-volume-slider">' + // outer background
+ '<span class="mejs-offscreen">' + t.options.allyVolumeControlText + '</span>' +
+ '<div class="mejs-horizontal-volume-total"></div>'+ // line background
+ '<div class="mejs-horizontal-volume-current"></div>'+ // current volume
+ '<div class="mejs-horizontal-volume-handle"></div>'+ // handle
+ '</a>'
+ )
+ .appendTo(controls) :
+
+ // vertical version
+ $('<div class="mejs-button mejs-volume-button mejs-mute">'+
+ '<button type="button" aria-controls="' + t.id +
+ '" title="' + t.options.muteText +
+ '" aria-label="' + t.options.muteText +
+ '"></button>'+
+ '<a href="javascript:void(0);" class="mejs-volume-slider">'+ // outer background
+ '<span class="mejs-offscreen">' + t.options.allyVolumeControlText + '</span>' +
+ '<div class="mejs-volume-total"></div>'+ // line background
+ '<div class="mejs-volume-current"></div>'+ // current volume
+ '<div class="mejs-volume-handle"></div>'+ // handle
+ '</a>'+
+ '</div>')
+ .appendTo(controls),
+ volumeSlider = t.container.find('.mejs-volume-slider, .mejs-horizontal-volume-slider'),
+ volumeTotal = t.container.find('.mejs-volume-total, .mejs-horizontal-volume-total'),
+ volumeCurrent = t.container.find('.mejs-volume-current, .mejs-horizontal-volume-current'),
+ volumeHandle = t.container.find('.mejs-volume-handle, .mejs-horizontal-volume-handle'),
+
+ positionVolumeHandle = function(volume, secondTry) {
+
+ if (!volumeSlider.is(':visible') && typeof secondTry == 'undefined') {
+ volumeSlider.show();
+ positionVolumeHandle(volume, true);
+ volumeSlider.hide();
+ return;
+ }
+
+ // correct to 0-1
+ volume = Math.max(0,volume);
+ volume = Math.min(volume,1);
+
+ // ajust mute button style
+ if (volume === 0) {
+ mute.removeClass('mejs-mute').addClass('mejs-unmute');
+ } else {
+ mute.removeClass('mejs-unmute').addClass('mejs-mute');
+ }
+
+ // top/left of full size volume slider background
+ var totalPosition = volumeTotal.position();
+ // position slider
+ if (mode == 'vertical') {
+ var
+ // height of the full size volume slider background
+ totalHeight = volumeTotal.height(),
+
+ // the new top position based on the current volume
+ // 70% volume on 100px height == top:30px
+ newTop = totalHeight - (totalHeight * volume);
+
+ // handle
+ volumeHandle.css('top', Math.round(totalPosition.top + newTop - (volumeHandle.height() / 2)));
+
+ // show the current visibility
+ volumeCurrent.height(totalHeight - newTop );
+ volumeCurrent.css('top', totalPosition.top + newTop);
+ } else {
+ var
+ // height of the full size volume slider background
+ totalWidth = volumeTotal.width(),
+
+ // the new left position based on the current volume
+ newLeft = totalWidth * volume;
+
+ // handle
+ volumeHandle.css('left', Math.round(totalPosition.left + newLeft - (volumeHandle.width() / 2)));
+
+ // rezize the current part of the volume bar
+ volumeCurrent.width( Math.round(newLeft) );
+ }
+ },
+ handleVolumeMove = function(e) {
+
+ var volume = null,
+ totalOffset = volumeTotal.offset();
+
+ // calculate the new volume based on the moust position
+ if (mode === 'vertical') {
+
+ var
+ railHeight = volumeTotal.height(),
+ totalTop = parseInt(volumeTotal.css('top').replace(/px/,''),10),
+ newY = e.pageY - totalOffset.top;
+
+ volume = (railHeight - newY) / railHeight;
+
+ // the controls just hide themselves (usually when mouse moves too far up)
+ if (totalOffset.top === 0 || totalOffset.left === 0) {
+ return;
+ }
+
+ } else {
+ var
+ railWidth = volumeTotal.width(),
+ newX = e.pageX - totalOffset.left;
+
+ volume = newX / railWidth;
+ }
+
+ // ensure the volume isn't outside 0-1
+ volume = Math.max(0,volume);
+ volume = Math.min(volume,1);
+
+ // position the slider and handle
+ positionVolumeHandle(volume);
+
+ // set the media object (this will trigger the volumechanged event)
+ if (volume === 0) {
+ media.setMuted(true);
+ } else {
+ media.setMuted(false);
+ }
+ media.setVolume(volume);
+ },
+ mouseIsDown = false,
+ mouseIsOver = false;
+
+ // SLIDER
+
+ mute
+ .hover(function() {
+ volumeSlider.show();
+ mouseIsOver = true;
+ }, function() {
+ mouseIsOver = false;
+
+ if (!mouseIsDown && mode == 'vertical') {
+ volumeSlider.hide();
+ }
+ });
+
+ var updateVolumeSlider = function (e) {
+
+ var volume = Math.floor(media.volume*100);
+
+ volumeSlider.attr({
+ 'aria-label': mejs.i18n.t('volumeSlider'),
+ 'aria-valuemin': 0,
+ 'aria-valuemax': 100,
+ 'aria-valuenow': volume,
+ 'aria-valuetext': volume+'%',
+ 'role': 'slider',
+ 'tabindex': 0
+ });
+
+ };
+
+ volumeSlider
+ .bind('mouseover', function() {
+ mouseIsOver = true;
+ })
+ .bind('mousedown', function (e) {
+ handleVolumeMove(e);
+ t.globalBind('mousemove.vol', function(e) {
+ handleVolumeMove(e);
+ });
+ t.globalBind('mouseup.vol', function () {
+ mouseIsDown = false;
+ t.globalUnbind('.vol');
+
+ if (!mouseIsOver && mode == 'vertical') {
+ volumeSlider.hide();
+ }
+ });
+ mouseIsDown = true;
+
+ return false;
+ })
+ .bind('keydown', function (e) {
+ var keyCode = e.keyCode;
+ var volume = media.volume;
+ switch (keyCode) {
+ case 38: // Up
+ volume += 0.1;
+ break;
+ case 40: // Down
+ volume = volume - 0.1;
+ break;
+ default:
+ return true;
+ }
+
+ mouseIsDown = false;
+ positionVolumeHandle(volume);
+ media.setVolume(volume);
+ return false;
+ })
+ .bind('blur', function () {
+ volumeSlider.hide();
+ });
+
+ // MUTE button
+ mute.find('button').click(function() {
+ media.setMuted( !media.muted );
+ });
+
+ //Keyboard input
+ mute.find('button').bind('focus', function () {
+ volumeSlider.show();
+ });
+
+ // listen for volume change events from other sources
+ media.addEventListener('volumechange', function(e) {
+ if (!mouseIsDown) {
+ if (media.muted) {
+ positionVolumeHandle(0);
+ mute.removeClass('mejs-mute').addClass('mejs-unmute');
+ } else {
+ positionVolumeHandle(media.volume);
+ mute.removeClass('mejs-unmute').addClass('mejs-mute');
+ }
+ }
+ updateVolumeSlider(e);
+ }, false);
+
+ if (t.container.is(':visible')) {
+ // set initial volume
+ positionVolumeHandle(player.options.startVolume);
+
+ // mutes the media and sets the volume icon muted if the initial volume is set to 0
+ if (player.options.startVolume === 0) {
+ media.setMuted(true);
+ }
+
+ // shim gets the startvolume as a parameter, but we have to set it on the native <video> and <audio> elements
+ if (media.pluginType === 'native') {
+ media.setVolume(player.options.startVolume);
+ }
+ }
+ }
+ });
+
+})(mejs.$);
+(function($) {
+
+ $.extend(mejs.MepDefaults, {
+ usePluginFullScreen: true,
+ newWindowCallback: function() { return '';},
+ fullscreenText: mejs.i18n.t('Fullscreen')
+ });
+
+ $.extend(MediaElementPlayer.prototype, {
+
+ isFullScreen: false,
+
+ isNativeFullScreen: false,
+
+ isInIframe: false,
+
+ buildfullscreen: function(player, controls, layers, media) {
+
+ if (!player.isVideo)
+ return;
+
+ player.isInIframe = (window.location != window.parent.location);
+
+ // native events
+ if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
+
+ // chrome doesn't alays fire this in an iframe
+ var func = function(e) {
+ if (player.isFullScreen) {
+ if (mejs.MediaFeatures.isFullScreen()) {
+ player.isNativeFullScreen = true;
+ // reset the controls once we are fully in full screen
+ player.setControlsSize();
+ } else {
+ player.isNativeFullScreen = false;
+ // when a user presses ESC
+ // make sure to put the player back into place
+ player.exitFullScreen();
+ }
+ }
+ };
+
+ player.globalBind(mejs.MediaFeatures.fullScreenEventName, func);
+ }
+
+ var t = this,
+ normalHeight = 0,
+ normalWidth = 0,
+ container = player.container,
+ fullscreenBtn =
+ $('<div class="mejs-button mejs-fullscreen-button">' +
+ '<button type="button" aria-controls="' + t.id + '" title="' + t.options.fullscreenText + '" aria-label="' + t.options.fullscreenText + '"></button>' +
+ '</div>')
+ .appendTo(controls);
+
+ if (t.media.pluginType === 'native' || (!t.options.usePluginFullScreen && !mejs.MediaFeatures.isFirefox)) {
+
+ fullscreenBtn.click(function() {
+ var isFullScreen = (mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || player.isFullScreen;
+
+ if (isFullScreen) {
+ player.exitFullScreen();
+ } else {
+ player.enterFullScreen();
+ }
+ });
+
+ } else {
+
+ var hideTimeout = null,
+ supportsPointerEvents = (function() {
+ // TAKEN FROM MODERNIZR
+ var element = document.createElement('x'),
+ documentElement = document.documentElement,
+ getComputedStyle = window.getComputedStyle,
+ supports;
+ if(!('pointerEvents' in element.style)){
+ return false;
+ }
+ element.style.pointerEvents = 'auto';
+ element.style.pointerEvents = 'x';
+ documentElement.appendChild(element);
+ supports = getComputedStyle &&
+ getComputedStyle(element, '').pointerEvents === 'auto';
+ documentElement.removeChild(element);
+ return !!supports;
+ })();
+
+ //
+
+ if (supportsPointerEvents && !mejs.MediaFeatures.isOpera) { // opera doesn't allow this :(
+
+ // allows clicking through the fullscreen button and controls down directly to Flash
+
+ /*
+ When a user puts his mouse over the fullscreen button, the controls are disabled
+ So we put a div over the video and another one on iether side of the fullscreen button
+ that caputre mouse movement
+ and restore the controls once the mouse moves outside of the fullscreen button
+ */
+
+ var fullscreenIsDisabled = false,
+ restoreControls = function() {
+ if (fullscreenIsDisabled) {
+ // hide the hovers
+ for (var i in hoverDivs) {
+ hoverDivs[i].hide();
+ }
+
+ // restore the control bar
+ fullscreenBtn.css('pointer-events', '');
+ t.controls.css('pointer-events', '');
+
+ // prevent clicks from pausing video
+ t.media.removeEventListener('click', t.clickToPlayPauseCallback);
+
+ // store for later
+ fullscreenIsDisabled = false;
+ }
+ },
+ hoverDivs = {},
+ hoverDivNames = ['top', 'left', 'right', 'bottom'],
+ i, len,
+ positionHoverDivs = function() {
+ var fullScreenBtnOffsetLeft = fullscreenBtn.offset().left - t.container.offset().left,
+ fullScreenBtnOffsetTop = fullscreenBtn.offset().top - t.container.offset().top,
+ fullScreenBtnWidth = fullscreenBtn.outerWidth(true),
+ fullScreenBtnHeight = fullscreenBtn.outerHeight(true),
+ containerWidth = t.container.width(),
+ containerHeight = t.container.height();
+
+ for (i in hoverDivs) {
+ hoverDivs[i].css({position: 'absolute', top: 0, left: 0}); //, backgroundColor: '#f00'});
+ }
+
+ // over video, but not controls
+ hoverDivs['top']
+ .width( containerWidth )
+ .height( fullScreenBtnOffsetTop );
+
+ // over controls, but not the fullscreen button
+ hoverDivs['left']
+ .width( fullScreenBtnOffsetLeft )
+ .height( fullScreenBtnHeight )
+ .css({top: fullScreenBtnOffsetTop});
+
+ // after the fullscreen button
+ hoverDivs['right']
+ .width( containerWidth - fullScreenBtnOffsetLeft - fullScreenBtnWidth )
+ .height( fullScreenBtnHeight )
+ .css({top: fullScreenBtnOffsetTop,
+ left: fullScreenBtnOffsetLeft + fullScreenBtnWidth});
+
+ // under the fullscreen button
+ hoverDivs['bottom']
+ .width( containerWidth )
+ .height( containerHeight - fullScreenBtnHeight - fullScreenBtnOffsetTop )
+ .css({top: fullScreenBtnOffsetTop + fullScreenBtnHeight});
+ };
+
+ t.globalBind('resize', function() {
+ positionHoverDivs();
+ });
+
+ for (i = 0, len = hoverDivNames.length; i < len; i++) {
+ hoverDivs[hoverDivNames[i]] = $('<div class="mejs-fullscreen-hover" />').appendTo(t.container).mouseover(restoreControls).hide();
+ }
+
+ // on hover, kill the fullscreen button's HTML handling, allowing clicks down to Flash
+ fullscreenBtn.on('mouseover',function() {
+
+ if (!t.isFullScreen) {
+
+ var buttonPos = fullscreenBtn.offset(),
+ containerPos = player.container.offset();
+
+ // move the button in Flash into place
+ media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, false);
+
+ // allows click through
+ fullscreenBtn.css('pointer-events', 'none');
+ t.controls.css('pointer-events', 'none');
+
+ // restore click-to-play
+ t.media.addEventListener('click', t.clickToPlayPauseCallback);
+
+ // show the divs that will restore things
+ for (i in hoverDivs) {
+ hoverDivs[i].show();
+ }
+
+ positionHoverDivs();
+
+ fullscreenIsDisabled = true;
+ }
+
+ });
+
+ // restore controls anytime the user enters or leaves fullscreen
+ media.addEventListener('fullscreenchange', function(e) {
+ t.isFullScreen = !t.isFullScreen;
+ // don't allow plugin click to pause video - messes with
+ // plugin's controls
+ if (t.isFullScreen) {
+ t.media.removeEventListener('click', t.clickToPlayPauseCallback);
+ } else {
+ t.media.addEventListener('click', t.clickToPlayPauseCallback);
+ }
+ restoreControls();
+ });
+
+
+ // the mouseout event doesn't work on the fullscren button, because we already killed the pointer-events
+ // so we use the document.mousemove event to restore controls when the mouse moves outside the fullscreen button
+
+ t.globalBind('mousemove', function(e) {
+
+ // if the mouse is anywhere but the fullsceen button, then restore it all
+ if (fullscreenIsDisabled) {
+
+ var fullscreenBtnPos = fullscreenBtn.offset();
+
+
+ if (e.pageY < fullscreenBtnPos.top || e.pageY > fullscreenBtnPos.top + fullscreenBtn.outerHeight(true) ||
+ e.pageX < fullscreenBtnPos.left || e.pageX > fullscreenBtnPos.left + fullscreenBtn.outerWidth(true)
+ ) {
+
+ fullscreenBtn.css('pointer-events', '');
+ t.controls.css('pointer-events', '');
+
+ fullscreenIsDisabled = false;
+ }
+ }
+ });
+
+
+
+ } else {
+
+ // the hover state will show the fullscreen button in Flash to hover up and click
+
+ fullscreenBtn
+ .on('mouseover', function() {
+
+ if (hideTimeout !== null) {
+ clearTimeout(hideTimeout);
+ delete hideTimeout;
+ }
+
+ var buttonPos = fullscreenBtn.offset(),
+ containerPos = player.container.offset();
+
+ media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, true);
+
+ })
+ .on('mouseout', function() {
+
+ if (hideTimeout !== null) {
+ clearTimeout(hideTimeout);
+ delete hideTimeout;
+ }
+
+ hideTimeout = setTimeout(function() {
+ media.hideFullscreenButton();
+ }, 1500);
+
+
+ });
+ }
+ }
+
+ player.fullscreenBtn = fullscreenBtn;
+
+ t.globalBind('keydown',function (e) {
+ if (((mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || t.isFullScreen) && e.keyCode == 27) {
+ player.exitFullScreen();
+ }
+ });
+
+ },
+
+ cleanfullscreen: function(player) {
+ player.exitFullScreen();
+ },
+
+ containerSizeTimeout: null,
+
+ enterFullScreen: function() {
+
+ var t = this;
+
+ // firefox+flash can't adjust plugin sizes without resetting :(
+ if (t.media.pluginType !== 'native' && (mejs.MediaFeatures.isFirefox || t.options.usePluginFullScreen)) {
+ //t.media.setFullscreen(true);
+ //player.isFullScreen = true;
+ return;
+ }
+
+ // set it to not show scroll bars so 100% will work
+ $(document.documentElement).addClass('mejs-fullscreen');
+
+ // store sizing
+ normalHeight = t.container.height();
+ normalWidth = t.container.width();
+
+ // attempt to do true fullscreen (Safari 5.1 and Firefox Nightly only for now)
+ if (t.media.pluginType === 'native') {
+ if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
+
+ mejs.MediaFeatures.requestFullScreen(t.container[0]);
+ //return;
+
+ if (t.isInIframe) {
+ // sometimes exiting from fullscreen doesn't work
+ // notably in Chrome <iframe>. Fixed in version 17
+ setTimeout(function checkFullscreen() {
+
+ if (t.isNativeFullScreen) {
+ var zoomMultiplier = window["devicePixelRatio"] || 1;
+ // Use a percent error margin since devicePixelRatio is a float and not exact.
+ var percentErrorMargin = 0.002; // 0.2%
+ var windowWidth = zoomMultiplier * $(window).width();
+ var screenWidth = screen.width;
+ var absDiff = Math.abs(screenWidth - windowWidth);
+ var marginError = screenWidth * percentErrorMargin;
+
+ // check if the video is suddenly not really fullscreen
+ if (absDiff > marginError) {
+ // manually exit
+ t.exitFullScreen();
+ } else {
+ // test again
+ setTimeout(checkFullscreen, 500);
+ }
+ }
+
+
+ }, 500);
+ }
+
+ } else if (mejs.MediaFeatures.hasSemiNativeFullScreen) {
+ t.media.webkitEnterFullscreen();
+ return;
+ }
+ }
+
+ // check for iframe launch
+ if (t.isInIframe) {
+ var url = t.options.newWindowCallback(this);
+
+
+ if (url !== '') {
+
+ // launch immediately
+ if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
+ t.pause();
+ window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
+ return;
+ } else {
+ setTimeout(function() {
+ if (!t.isNativeFullScreen) {
+ t.pause();
+ window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
+ }
+ }, 250);
+ }
+ }
+
+ }
+
+ // full window code
+
+
+
+ // make full size
+ t.container
+ .addClass('mejs-container-fullscreen')
+ .width('100%')
+ .height('100%');
+ //.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});
+
+ // Only needed for safari 5.1 native full screen, can cause display issues elsewhere
+ // Actually, it seems to be needed for IE8, too
+ //if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
+ t.containerSizeTimeout = setTimeout(function() {
+ t.container.css({width: '100%', height: '100%'});
+ t.setControlsSize();
+ }, 500);
+ //}
+
+ if (t.media.pluginType === 'native') {
+ t.$media
+ .width('100%')
+ .height('100%');
+ } else {
+ t.container.find('.mejs-shim')
+ .width('100%')
+ .height('100%');
+
+ //if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
+ t.media.setVideoSize($(window).width(),$(window).height());
+ //}
+ }
+
+ t.layers.children('div')
+ .width('100%')
+ .height('100%');
+
+ if (t.fullscreenBtn) {
+ t.fullscreenBtn
+ .removeClass('mejs-fullscreen')
+ .addClass('mejs-unfullscreen');
+ }
+
+ t.setControlsSize();
+ t.isFullScreen = true;
+
+ t.container.find('.mejs-captions-text').css('font-size', screen.width / t.width * 1.00 * 100 + '%');
+ t.container.find('.mejs-captions-position').css('bottom', '45px');
+ },
+
+ exitFullScreen: function() {
+
+ var t = this;
+
+ // Prevent container from attempting to stretch a second time
+ clearTimeout(t.containerSizeTimeout);
+
+ // firefox can't adjust plugins
+ if (t.media.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) {
+ t.media.setFullscreen(false);
+ //player.isFullScreen = false;
+ return;
+ }
+
+ // come outo of native fullscreen
+ if (mejs.MediaFeatures.hasTrueNativeFullScreen && (mejs.MediaFeatures.isFullScreen() || t.isFullScreen)) {
+ mejs.MediaFeatures.cancelFullScreen();
+ }
+
+ // restore scroll bars to document
+ $(document.documentElement).removeClass('mejs-fullscreen');
+
+ t.container
+ .removeClass('mejs-container-fullscreen')
+ .width(normalWidth)
+ .height(normalHeight);
+ //.css({position: '', left: '', top: '', right: '', bottom: '', overflow: 'inherit', width: normalWidth + 'px', height: normalHeight + 'px', 'z-index': 1});
+
+ if (t.media.pluginType === 'native') {
+ t.$media
+ .width(normalWidth)
+ .height(normalHeight);
+ } else {
+ t.container.find('.mejs-shim')
+ .width(normalWidth)
+ .height(normalHeight);
+
+ t.media.setVideoSize(normalWidth, normalHeight);
+ }
+
+ t.layers.children('div')
+ .width(normalWidth)
+ .height(normalHeight);
+
+ t.fullscreenBtn
+ .removeClass('mejs-unfullscreen')
+ .addClass('mejs-fullscreen');
+
+ t.setControlsSize();
+ t.isFullScreen = false;
+
+ t.container.find('.mejs-captions-text').css('font-size','');
+ t.container.find('.mejs-captions-position').css('bottom', '');
+ }
+ });
+
+})(mejs.$);
+
+(function($) {
+
+ // Speed
+ $.extend(mejs.MepDefaults, {
+
+ speeds: ['2.00', '1.50', '1.25', '1.00', '0.75'],
+
+ defaultSpeed: '1.00',
+
+ speedChar: 'x'
+
+ });
+
+ $.extend(MediaElementPlayer.prototype, {
+
+ buildspeed: function(player, controls, layers, media) {
+ var t = this;
+
+ if (t.media.pluginType == 'native') {
+ var
+ speedButton = null,
+ speedSelector = null,
+ playbackSpeed = null,
+ html = '<div class="mejs-button mejs-speed-button">' +
+ '<button type="button">' + t.options.defaultSpeed + t.options.speedChar + '</button>' +
+ '<div class="mejs-speed-selector">' +
+ '<ul>';
+
+ if ($.inArray(t.options.defaultSpeed, t.options.speeds) === -1) {
+ t.options.speeds.push(t.options.defaultSpeed);
+ }
+
+ t.options.speeds.sort(function(a, b) {
+ return parseFloat(b) - parseFloat(a);
+ });
+
+ for (var i = 0, il = t.options.speeds.length; i<il; i++) {
+ html += '<li>' +
+ '<input type="radio" name="speed" ' +
+ 'value="' + t.options.speeds[i] + '" ' +
+ 'id="' + t.options.speeds[i] + '" ' +
+ (t.options.speeds[i] == t.options.defaultSpeed ? ' checked' : '') +
+ ' />' +
+ '<label for="' + t.options.speeds[i] + '" ' +
+ (t.options.speeds[i] == t.options.defaultSpeed ? ' class="mejs-speed-selected"' : '') +
+ '>' + t.options.speeds[i] + t.options.speedChar + '</label>' +
+ '</li>';
+ }
+ html += '</ul></div></div>';
+
+ speedButton = $(html).appendTo(controls);
+ speedSelector = speedButton.find('.mejs-speed-selector');
+
+ playbackspeed = t.options.defaultSpeed;
+
+ speedSelector
+ .on('click', 'input[type="radio"]', function() {
+ var newSpeed = $(this).attr('value');
+ playbackspeed = newSpeed;
+ media.playbackRate = parseFloat(newSpeed);
+ speedButton.find('button').html('test' + newSpeed + t.options.speedChar);
+ speedButton.find('.mejs-speed-selected').removeClass('mejs-speed-selected');
+ speedButton.find('input[type="radio"]:checked').next().addClass('mejs-speed-selected');
+ });
+
+ speedSelector
+ .height(
+ speedButton.find('.mejs-speed-selector ul').outerHeight(true) +
+ speedButton.find('.mejs-speed-translations').outerHeight(true))
+ .css('top', (-1 * speedSelector.height()) + 'px');
+ }
+ }
+ });
+
+})(mejs.$);
+
+(function($) {
+
+ // add extra default options
+ $.extend(mejs.MepDefaults, {
+ // this will automatically turn on a <track>
+ startLanguage: '',
+
+ tracksText: mejs.i18n.t('Captions/Subtitles'),
+
+ // option to remove the [cc] button when no <track kind="subtitles"> are present
+ hideCaptionsButtonWhenEmpty: true,
+
+ // If true and we only have one track, change captions to popup
+ toggleCaptionsButtonWhenOnlyOne: false,
+
+ // #id or .class
+ slidesSelector: ''
+ });
+
+ $.extend(MediaElementPlayer.prototype, {
+
+ hasChapters: false,
+
+ buildtracks: function(player, controls, layers, media) {
+ if (player.tracks.length === 0)
+ return;
+
+ var t = this,
+ i,
+ options = '';
+
+ if (t.domNode.textTracks) { // if browser will do native captions, prefer mejs captions, loop through tracks and hide
+ for (i = t.domNode.textTracks.length - 1; i >= 0; i--) {
+ t.domNode.textTracks[i].mode = "hidden";
+ }
+ }
+ player.chapters =
+ $('<div class="mejs-chapters mejs-layer"></div>')
+ .prependTo(layers).hide();
+ player.captions =
+ $('<div class="mejs-captions-layer mejs-layer"><div class="mejs-captions-position mejs-captions-position-hover" role="log" aria-live="assertive" aria-atomic="false"><span class="mejs-captions-text"></span></div></div>')
+ .prependTo(layers).hide();
+ player.captionsText = player.captions.find('.mejs-captions-text');
+ player.captionsButton =
+ $('<div class="mejs-button mejs-captions-button">'+
+ '<button type="button" aria-controls="' + t.id + '" title="' + t.options.tracksText + '" aria-label="' + t.options.tracksText + '"></button>'+
+ '<div class="mejs-captions-selector">'+
+ '<ul>'+
+ '<li>'+
+ '<input type="radio" name="' + player.id + '_captions" id="' + player.id + '_captions_none" value="none" checked="checked" />' +
+ '<label for="' + player.id + '_captions_none">' + mejs.i18n.t('None') +'</label>'+
+ '</li>' +
+ '</ul>'+
+ '</div>'+
+ '</div>')
+ .appendTo(controls);
+
+
+ var subtitleCount = 0;
+ for (i=0; i<player.tracks.length; i++) {
+ if (player.tracks[i].kind == 'subtitles') {
+ subtitleCount++;
+ }
+ }
+
+ // if only one language then just make the button a toggle
+ if (t.options.toggleCaptionsButtonWhenOnlyOne && subtitleCount == 1){
+ // click
+ player.captionsButton.on('click',function() {
+ if (player.selectedTrack === null) {
+ lang = player.tracks[0].srclang;
+ } else {
+ lang = 'none';
+ }
+ player.setTrack(lang);
+ });
+ } else {
+ // hover or keyboard focus
+ player.captionsButton.on( 'mouseenter focusin', function() {
+ $(this).find('.mejs-captions-selector').css('visibility','visible');
+ })
+
+ // handle clicks to the language radio buttons
+ .on('click','input[type=radio]',function() {
+ lang = this.value;
+ player.setTrack(lang);
+ });
+
+ player.captionsButton.on( 'mouseleave focusout', function() {
+ $(this).find(".mejs-captions-selector").css("visibility","hidden");
+ });
+
+ }
+
+ if (!player.options.alwaysShowControls) {
+ // move with controls
+ player.container
+ .bind('controlsshown', function () {
+ // push captions above controls
+ player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
+
+ })
+ .bind('controlshidden', function () {
+ if (!media.paused) {
+ // move back to normal place
+ player.container.find('.mejs-captions-position').removeClass('mejs-captions-position-hover');
+ }
+ });
+ } else {
+ player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
+ }
+
+ player.trackToLoad = -1;
+ player.selectedTrack = null;
+ player.isLoadingTrack = false;
+
+ // add to list
+ for (i=0; i<player.tracks.length; i++) {
+ if (player.tracks[i].kind == 'subtitles') {
+ player.addTrackButton(player.tracks[i].srclang, player.tracks[i].label);
+ }
+ }
+
+ // start loading tracks
+ player.loadNextTrack();
+
+ media.addEventListener('timeupdate',function(e) {
+ player.displayCaptions();
+ }, false);
+
+ if (player.options.slidesSelector !== '') {
+ player.slidesContainer = $(player.options.slidesSelector);
+
+ media.addEventListener('timeupdate',function(e) {
+ player.displaySlides();
+ }, false);
+
+ }
+
+ media.addEventListener('loadedmetadata', function(e) {
+ player.displayChapters();
+ }, false);
+
+ player.container.hover(
+ function () {
+ // chapters
+ if (player.hasChapters) {
+ player.chapters.css('visibility','visible');
+ player.chapters.fadeIn(200).height(player.chapters.find('.mejs-chapter').outerHeight());
+ }
+ },
+ function () {
+ if (player.hasChapters && !media.paused) {
+ player.chapters.fadeOut(200, function() {
+ $(this).css('visibility','hidden');
+ $(this).css('display','block');
+ });
+ }
+ });
+
+ // check for autoplay
+ if (player.node.getAttribute('autoplay') !== null) {
+ player.chapters.css('visibility','hidden');
+ }
+ },
+
+ setTrack: function(lang){
+
+ var t = this,
+ i;
+
+ if (lang == 'none') {
+ t.selectedTrack = null;
+ t.captionsButton.removeClass('mejs-captions-enabled');
+ } else {
+ for (i=0; i<t.tracks.length; i++) {
+ if (t.tracks[i].srclang == lang) {
+ if (t.selectedTrack === null)
+ t.captionsButton.addClass('mejs-captions-enabled');
+ t.selectedTrack = t.tracks[i];
+ t.captions.attr('lang', t.selectedTrack.srclang);
+ t.displayCaptions();
+ break;
+ }
+ }
+ }
+ },
+
+ loadNextTrack: function() {
+ var t = this;
+
+ t.trackToLoad++;
+ if (t.trackToLoad < t.tracks.length) {
+ t.isLoadingTrack = true;
+ t.loadTrack(t.trackToLoad);
+ } else {
+ // add done?
+ t.isLoadingTrack = false;
+
+ t.checkForTracks();
+ }
+ },
+
+ loadTrack: function(index){
+ var
+ t = this,
+ track = t.tracks[index],
+ after = function() {
+
+ track.isLoaded = true;
+
+ // create button
+ //t.addTrackButton(track.srclang);
+ t.enableTrackButton(track.srclang, track.label);
+
+ t.loadNextTrack();
+
+ };
+
+
+ $.ajax({
+ url: track.src,
+ dataType: "text",
+ success: function(d) {
+
+ // parse the loaded file
+ if (typeof d == "string" && (/<tt\s+xml/ig).exec(d)) {
+ track.entries = mejs.TrackFormatParser.dfxp.parse(d);
+ } else {
+ track.entries = mejs.TrackFormatParser.webvtt.parse(d);
+ }
+
+ after();
+
+ if (track.kind == 'chapters') {
+ t.media.addEventListener('play', function(e) {
+ if (t.media.duration > 0) {
+ t.displayChapters(track);
+ }
+ }, false);
+ }
+
+ if (track.kind == 'slides') {
+ t.setupSlides(track);
+ }
+ },
+ error: function() {
+ t.loadNextTrack();
+ }
+ });
+ },
+
+ enableTrackButton: function(lang, label) {
+ var t = this;
+
+ if (label === '') {
+ label = mejs.language.codes[lang] || lang;
+ }
+
+ t.captionsButton
+ .find('input[value=' + lang + ']')
+ .prop('disabled',false)
+ .siblings('label')
+ .html( label );
+
+ // auto select
+ if (t.options.startLanguage == lang) {
+ $('#' + t.id + '_captions_' + lang).prop('checked', true).trigger('click');
+ }
+
+ t.adjustLanguageBox();
+ },
+
+ addTrackButton: function(lang, label) {
+ var t = this;
+ if (label === '') {
+ label = mejs.language.codes[lang] || lang;
+ }
+
+ t.captionsButton.find('ul').append(
+ $('<li>'+
+ '<input type="radio" name="' + t.id + '_captions" id="' + t.id + '_captions_' + lang + '" value="' + lang + '" disabled="disabled" />' +
+ '<label for="' + t.id + '_captions_' + lang + '">' + label + ' (loading)' + '</label>'+
+ '</li>')
+ );
+
+ t.adjustLanguageBox();
+
+ // remove this from the dropdownlist (if it exists)
+ t.container.find('.mejs-captions-translations option[value=' + lang + ']').remove();
+ },
+
+ adjustLanguageBox:function() {
+ var t = this;
+ // adjust the size of the outer box
+ t.captionsButton.find('.mejs-captions-selector').height(
+ t.captionsButton.find('.mejs-captions-selector ul').outerHeight(true) +
+ t.captionsButton.find('.mejs-captions-translations').outerHeight(true)
+ );
+ },
+
+ checkForTracks: function() {
+ var
+ t = this,
+ hasSubtitles = false;
+
+ // check if any subtitles
+ if (t.options.hideCaptionsButtonWhenEmpty) {
+ for (i=0; i<t.tracks.length; i++) {
+ if (t.tracks[i].kind == 'subtitles') {
+ hasSubtitles = true;
+ break;
+ }
+ }
+
+ if (!hasSubtitles) {
+ t.captionsButton.hide();
+ t.setControlsSize();
+ }
+ }
+ },
+
+ displayCaptions: function() {
+
+ if (typeof this.tracks == 'undefined')
+ return;
+
+ var
+ t = this,
+ i,
+ track = t.selectedTrack;
+
+ if (track !== null && track.isLoaded) {
+ for (i=0; i<track.entries.times.length; i++) {
+ if (t.media.currentTime >= track.entries.times[i].start && t.media.currentTime <= track.entries.times[i].stop) {
+ // Set the line before the timecode as a class so the cue can be targeted if needed
+ t.captionsText.html(track.entries.text[i]).attr('class', 'mejs-captions-text ' + (track.entries.times[i].identifier || ''));
+ t.captions.show().height(0);
+ return; // exit out if one is visible;
+ }
+ }
+ t.captions.hide();
+ } else {
+ t.captions.hide();
+ }
+ },
+
+ setupSlides: function(track) {
+ var t = this;
+
+ t.slides = track;
+ t.slides.entries.imgs = [t.slides.entries.text.length];
+ t.showSlide(0);
+
+ },
+
+ showSlide: function(index) {
+ if (typeof this.tracks == 'undefined' || typeof this.slidesContainer == 'undefined') {
+ return;
+ }
+
+ var t = this,
+ url = t.slides.entries.text[index],
+ img = t.slides.entries.imgs[index];
+
+ if (typeof img == 'undefined' || typeof img.fadeIn == 'undefined') {
+
+ t.slides.entries.imgs[index] = img = $('<img src="' + url + '">')
+ .on('load', function() {
+ img.appendTo(t.slidesContainer)
+ .hide()
+ .fadeIn()
+ .siblings(':visible')
+ .fadeOut();
+
+ });
+
+ } else {
+
+ if (!img.is(':visible') && !img.is(':animated')) {
+
+ //
+
+ img.fadeIn()
+ .siblings(':visible')
+ .fadeOut();
+ }
+ }
+
+ },
+
+ displaySlides: function() {
+
+ if (typeof this.slides == 'undefined')
+ return;
+
+ var
+ t = this,
+ slides = t.slides,
+ i;
+
+ for (i=0; i<slides.entries.times.length; i++) {
+ if (t.media.currentTime >= slides.entries.times[i].start && t.media.currentTime <= slides.entries.times[i].stop){
+
+ t.showSlide(i);
+
+ return; // exit out if one is visible;
+ }
+ }
+ },
+
+ displayChapters: function() {
+ var
+ t = this,
+ i;
+
+ for (i=0; i<t.tracks.length; i++) {
+ if (t.tracks[i].kind == 'chapters' && t.tracks[i].isLoaded) {
+ t.drawChapters(t.tracks[i]);
+ t.hasChapters = true;
+ break;
+ }
+ }
+ },
+
+ drawChapters: function(chapters) {
+ var
+ t = this,
+ i,
+ dur,
+ //width,
+ //left,
+ percent = 0,
+ usedPercent = 0;
+
+ t.chapters.empty();
+
+ for (i=0; i<chapters.entries.times.length; i++) {
+ dur = chapters.entries.times[i].stop - chapters.entries.times[i].start;
+ percent = Math.floor(dur / t.media.duration * 100);
+ if (percent + usedPercent > 100 || // too large
+ i == chapters.entries.times.length-1 && percent + usedPercent < 100) // not going to fill it in
+ {
+ percent = 100 - usedPercent;
+ }
+ //width = Math.floor(t.width * dur / t.media.duration);
+ //left = Math.floor(t.width * chapters.entries.times[i].start / t.media.duration);
+ //if (left + width > t.width) {
+ // width = t.width - left;
+ //}
+
+ t.chapters.append( $(
+ '<div class="mejs-chapter" rel="' + chapters.entries.times[i].start + '" style="left: ' + usedPercent.toString() + '%;width: ' + percent.toString() + '%;">' +
+ '<div class="mejs-chapter-block' + ((i==chapters.entries.times.length-1) ? ' mejs-chapter-block-last' : '') + '">' +
+ '<span class="ch-title">' + chapters.entries.text[i] + '</span>' +
+ '<span class="ch-time">' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].start) + '&ndash;' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].stop) + '</span>' +
+ '</div>' +
+ '</div>'));
+ usedPercent += percent;
+ }
+
+ t.chapters.find('div.mejs-chapter').click(function() {
+ t.media.setCurrentTime( parseFloat( $(this).attr('rel') ) );
+ if (t.media.paused) {
+ t.media.play();
+ }
+ });
+
+ t.chapters.show();
+ }
+ });
+
+
+
+ mejs.language = {
+ codes: {
+ af:'Afrikaans',
+ sq:'Albanian',
+ ar:'Arabic',
+ be:'Belarusian',
+ bg:'Bulgarian',
+ ca:'Catalan',
+ zh:'Chinese',
+ 'zh-cn':'Chinese Simplified',
+ 'zh-tw':'Chinese Traditional',
+ hr:'Croatian',
+ cs:'Czech',
+ da:'Danish',
+ nl:'Dutch',
+ en:'English',
+ et:'Estonian',
+ fl:'Filipino',
+ fi:'Finnish',
+ fr:'French',
+ gl:'Galician',
+ de:'German',
+ el:'Greek',
+ ht:'Haitian Creole',
+ iw:'Hebrew',
+ hi:'Hindi',
+ hu:'Hungarian',
+ is:'Icelandic',
+ id:'Indonesian',
+ ga:'Irish',
+ it:'Italian',
+ ja:'Japanese',
+ ko:'Korean',
+ lv:'Latvian',
+ lt:'Lithuanian',
+ mk:'Macedonian',
+ ms:'Malay',
+ mt:'Maltese',
+ no:'Norwegian',
+ fa:'Persian',
+ pl:'Polish',
+ pt:'Portuguese',
+ // 'pt-pt':'Portuguese (Portugal)',
+ ro:'Romanian',
+ ru:'Russian',
+ sr:'Serbian',
+ sk:'Slovak',
+ sl:'Slovenian',
+ es:'Spanish',
+ sw:'Swahili',
+ sv:'Swedish',
+ tl:'Tagalog',
+ th:'Thai',
+ tr:'Turkish',
+ uk:'Ukrainian',
+ vi:'Vietnamese',
+ cy:'Welsh',
+ yi:'Yiddish'
+ }
+ };
+
+ /*
+ Parses WebVTT format which should be formatted as
+ ================================
+ WEBVTT
+
+ 1
+ 00:00:01,1 --> 00:00:05,000
+ A line of text
+
+ 2
+ 00:01:15,1 --> 00:02:05,000
+ A second line of text
+
+ ===============================
+
+ Adapted from: http://www.delphiki.com/html5/playr
+ */
+ mejs.TrackFormatParser = {
+ webvtt: {
+ pattern_timecode: /^((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{1,3})?) --\> ((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{3})?)(.*)$/,
+
+ parse: function(trackText) {
+ var
+ i = 0,
+ lines = mejs.TrackFormatParser.split2(trackText, /\r?\n/),
+ entries = {text:[], times:[]},
+ timecode,
+ text,
+ identifier;
+ for(; i<lines.length; i++) {
+ timecode = this.pattern_timecode.exec(lines[i]);
+
+ if (timecode && i<lines.length) {
+ if ((i - 1) >= 0 && lines[i - 1] !== '') {
+ identifier = lines[i - 1];
+ }
+ i++;
+ // grab all the (possibly multi-line) text that follows
+ text = lines[i];
+ i++;
+ while(lines[i] !== '' && i<lines.length){
+ text = text + '\n' + lines[i];
+ i++;
+ }
+ text = $.trim(text).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>");
+ // Text is in a different array so I can use .join
+ entries.text.push(text);
+ entries.times.push(
+ {
+ identifier: identifier,
+ start: (mejs.Utility.convertSMPTEtoSeconds(timecode[1]) === 0) ? 0.200 : mejs.Utility.convertSMPTEtoSeconds(timecode[1]),
+ stop: mejs.Utility.convertSMPTEtoSeconds(timecode[3]),
+ settings: timecode[5]
+ });
+ }
+ identifier = '';
+ }
+ return entries;
+ }
+ },
+ // Thanks to Justin Capella: https://github.com/johndyer/mediaelement/pull/420
+ dfxp: {
+ parse: function(trackText) {
+ trackText = $(trackText).filter("tt");
+ var
+ i = 0,
+ container = trackText.children("div").eq(0),
+ lines = container.find("p"),
+ styleNode = trackText.find("#" + container.attr("style")),
+ styles,
+ begin,
+ end,
+ text,
+ entries = {text:[], times:[]};
+
+
+ if (styleNode.length) {
+ var attributes = styleNode.removeAttr("id").get(0).attributes;
+ if (attributes.length) {
+ styles = {};
+ for (i = 0; i < attributes.length; i++) {
+ styles[attributes[i].name.split(":")[1]] = attributes[i].value;
+ }
+ }
+ }
+
+ for(i = 0; i<lines.length; i++) {
+ var style;
+ var _temp_times = {
+ start: null,
+ stop: null,
+ style: null
+ };
+ if (lines.eq(i).attr("begin")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("begin"));
+ if (!_temp_times.start && lines.eq(i-1).attr("end")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i-1).attr("end"));
+ if (lines.eq(i).attr("end")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("end"));
+ if (!_temp_times.stop && lines.eq(i+1).attr("begin")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i+1).attr("begin"));
+ if (styles) {
+ style = "";
+ for (var _style in styles) {
+ style += _style + ":" + styles[_style] + ";";
+ }
+ }
+ if (style) _temp_times.style = style;
+ if (_temp_times.start === 0) _temp_times.start = 0.200;
+ entries.times.push(_temp_times);
+ text = $.trim(lines.eq(i).html()).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>");
+ entries.text.push(text);
+ if (entries.times.start === 0) entries.times.start = 2;
+ }
+ return entries;
+ }
+ },
+ split2: function (text, regex) {
+ // normal version for compliant browsers
+ // see below for IE fix
+ return text.split(regex);
+ }
+ };
+
+ // test for browsers with bad String.split method.
+ if ('x\n\ny'.split(/\n/gi).length != 3) {
+ // add super slow IE8 and below version
+ mejs.TrackFormatParser.split2 = function(text, regex) {
+ var
+ parts = [],
+ chunk = '',
+ i;
+
+ for (i=0; i<text.length; i++) {
+ chunk += text.substring(i,i+1);
+ if (regex.test(chunk)) {
+ parts.push(chunk.replace(regex, ''));
+ chunk = '';
+ }
+ }
+ parts.push(chunk);
+ return parts;
+ };
+ }
+
+})(mejs.$);
+
+/*
+* ContextMenu Plugin
+*
+*
+*/
+
+(function($) {
+
+$.extend(mejs.MepDefaults,
+ { 'contextMenuItems': [
+ // demo of a fullscreen option
+ {
+ render: function(player) {
+
+ // check for fullscreen plugin
+ if (typeof player.enterFullScreen == 'undefined')
+ return null;
+
+ if (player.isFullScreen) {
+ return mejs.i18n.t('Turn off Fullscreen');
+ } else {
+ return mejs.i18n.t('Go Fullscreen');
+ }
+ },
+ click: function(player) {
+ if (player.isFullScreen) {
+ player.exitFullScreen();
+ } else {
+ player.enterFullScreen();
+ }
+ }
+ }
+ ,
+ // demo of a mute/unmute button
+ {
+ render: function(player) {
+ if (player.media.muted) {
+ return mejs.i18n.t('Unmute');
+ } else {
+ return mejs.i18n.t('Mute');
+ }
+ },
+ click: function(player) {
+ if (player.media.muted) {
+ player.setMuted(false);
+ } else {
+ player.setMuted(true);
+ }
+ }
+ },
+ // separator
+ {
+ isSeparator: true
+ }
+ ,
+ // demo of simple download video
+ {
+ render: function(player) {
+ return mejs.i18n.t('Download Video');
+ },
+ click: function(player) {
+ window.location.href = player.media.currentSrc;
+ }
+ }
+ ]}
+);
+
+
+ $.extend(MediaElementPlayer.prototype, {
+ buildcontextmenu: function(player, controls, layers, media) {
+
+ // create context menu
+ player.contextMenu = $('<div class="mejs-contextmenu"></div>')
+ .appendTo($('body'))
+ .hide();
+
+ // create events for showing context menu
+ player.container.bind('contextmenu', function(e) {
+ if (player.isContextMenuEnabled) {
+ e.preventDefault();
+ player.renderContextMenu(e.clientX-1, e.clientY-1);
+ return false;
+ }
+ });
+ player.container.bind('click', function() {
+ player.contextMenu.hide();
+ });
+ player.contextMenu.bind('mouseleave', function() {
+
+ //
+ player.startContextMenuTimer();
+
+ });
+ },
+
+ cleancontextmenu: function(player) {
+ player.contextMenu.remove();
+ },
+
+ isContextMenuEnabled: true,
+ enableContextMenu: function() {
+ this.isContextMenuEnabled = true;
+ },
+ disableContextMenu: function() {
+ this.isContextMenuEnabled = false;
+ },
+
+ contextMenuTimeout: null,
+ startContextMenuTimer: function() {
+ //
+
+ var t = this;
+
+ t.killContextMenuTimer();
+
+ t.contextMenuTimer = setTimeout(function() {
+ t.hideContextMenu();
+ t.killContextMenuTimer();
+ }, 750);
+ },
+ killContextMenuTimer: function() {
+ var timer = this.contextMenuTimer;
+
+ //
+
+ if (timer != null) {
+ clearTimeout(timer);
+ delete timer;
+ timer = null;
+ }
+ },
+
+ hideContextMenu: function() {
+ this.contextMenu.hide();
+ },
+
+ renderContextMenu: function(x,y) {
+
+ // alway re-render the items so that things like "turn fullscreen on" and "turn fullscreen off" are always written correctly
+ var t = this,
+ html = '',
+ items = t.options.contextMenuItems;
+
+ for (var i=0, il=items.length; i<il; i++) {
+
+ if (items[i].isSeparator) {
+ html += '<div class="mejs-contextmenu-separator"></div>';
+ } else {
+
+ var rendered = items[i].render(t);
+
+ // render can return null if the item doesn't need to be used at the moment
+ if (rendered != null) {
+ html += '<div class="mejs-contextmenu-item" data-itemindex="' + i + '" id="element-' + (Math.random()*1000000) + '">' + rendered + '</div>';
+ }
+ }
+ }
+
+ // position and show the context menu
+ t.contextMenu
+ .empty()
+ .append($(html))
+ .css({top:y, left:x})
+ .show();
+
+ // bind events
+ t.contextMenu.find('.mejs-contextmenu-item').each(function() {
+
+ // which one is this?
+ var $dom = $(this),
+ itemIndex = parseInt( $dom.data('itemindex'), 10 ),
+ item = t.options.contextMenuItems[itemIndex];
+
+ // bind extra functionality?
+ if (typeof item.show != 'undefined')
+ item.show( $dom , t);
+
+ // bind click action
+ $dom.click(function() {
+ // perform click action
+ if (typeof item.click != 'undefined')
+ item.click(t);
+
+ // close
+ t.contextMenu.hide();
+ });
+ });
+
+ // stop the controls from hiding
+ setTimeout(function() {
+ t.killControlsTimer('rev3');
+ }, 100);
+
+ }
+ });
+
+})(mejs.$);
+/**
+ * Postroll plugin
+ */
+(function($) {
+
+ $.extend(mejs.MepDefaults, {
+ postrollCloseText: mejs.i18n.t('Close')
+ });
+
+ // Postroll
+ $.extend(MediaElementPlayer.prototype, {
+ buildpostroll: function(player, controls, layers, media) {
+ var
+ t = this,
+ postrollLink = t.container.find('link[rel="postroll"]').attr('href');
+
+ if (typeof postrollLink !== 'undefined') {
+ player.postroll =
+ $('<div class="mejs-postroll-layer mejs-layer"><a class="mejs-postroll-close" onclick="$(this).parent().hide();return false;">' + t.options.postrollCloseText + '</a><div class="mejs-postroll-layer-content"></div></div>').prependTo(layers).hide();
+
+ t.media.addEventListener('ended', function (e) {
+ $.ajax({
+ dataType: 'html',
+ url: postrollLink,
+ success: function (data, textStatus) {
+ layers.find('.mejs-postroll-layer-content').html(data);
+ }
+ });
+ player.postroll.show();
+ }, false);
+ }
+ }
+ });
+
+})(mejs.$); \ No newline at end of file