app/assets/javascripts/mediaelement_rails/mediaelementplayer.js in mediaelement_rails-0.8.1 vs app/assets/javascripts/mediaelement_rails/mediaelementplayer.js in mediaelement_rails-0.8.2
- old
+ new
@@ -10,10 +10,23 @@
* License: MIT
*
*/
if (typeof jQuery != 'undefined') {
mejs.$ = jQuery;
+} else if (typeof Zepto != 'undefined') {
+ mejs.$ = Zepto;
+
+ // define `outerWidth` method which has not been realized in Zepto
+ Zepto.fn.outerWidth = function(includeMargin) {
+ var width = $(this).width();
+ if (includeMargin) {
+ width += parseInt($(this).css('margin-right'), 10);
+ width += parseInt($(this).css('margin-left'), 10);
+ }
+ return width
+ }
+
} else if (typeof ender != 'undefined') {
mejs.$ = ender;
}
(function ($) {
@@ -58,13 +71,29 @@
loop: false,
// rewind to beginning when media ends
autoRewind: true,
// resize to media dimensions
enableAutosize: true,
+
+ /*
+ * Time format to use. Default: 'mm:ss'
+ * Supported units:
+ * h: hour
+ * m: minute
+ * s: second
+ * f: frame count
+ * When using 'hh', 'mm', 'ss' or 'ff' we always display 2 digits.
+ * If you use 'h', 'm', 's' or 'f' we display 1 digit if possible.
+ *
+ * Example to display 75 seconds:
+ * Format 'mm:ss': 01:15
+ * Format 'm:ss': 1:15
+ * Format 'm:s': 1:15
+ */
+ timeFormat: '',
// 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,
@@ -100,13 +129,13 @@
32, // SPACE
179 // GOOGLE play/pause button
],
action: function(player, media) {
if (media.paused || media.ended) {
- player.play();
+ media.play();
} else {
- player.pause();
+ media.pause();
}
}
},
{
keys: [38], // UP
@@ -215,16 +244,17 @@
// these will be reset after the MediaElement.success fires
t.$media = t.$node = $(node);
t.node = t.media = t.$media[0];
+ if(!t.node) {
+ return
+ }
+
// 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') {
@@ -232,10 +262,23 @@
}
// extend default options
t.options = $.extend({},mejs.MepDefaults,o);
+ if (!t.options.timeFormat) {
+ // Generate the time format according to options
+ t.options.timeFormat = 'mm:ss';
+ if (t.options.alwaysShowHours) {
+ t.options.timeFormat = 'hh:mm:ss';
+ }
+ if (t.options.showTimecodeFrameCount) {
+ t.options.timeFormat += ':ff';
+ }
+ }
+
+ mejs.Utility.calculateTimeFormat(0, t.options, t.options.framesPerSecond || 25);
+
// unique ID
t.id = 'mep_' + mejs.mepIndex++;
// add to player array (for focus events)
mejs.players[t.id] = t;
@@ -349,10 +392,13 @@
} else {
// normal way of moving it into place (doesn't work on iOS)
t.container.find('.mejs-mediaelement').append(t.$media);
}
+
+ // needs to be assigned here, after iOS remap
+ t.node.player = t;
// find parts
t.controls = t.container.find('.mejs-controls');
t.layers = t.container.find('.mejs-layers');
@@ -741,10 +787,19 @@
if (!t.isFullScreen) {
t.setPlayerSize(t.width, t.height);
t.setControlsSize();
}
}, false);
+
+ // Only change the time format when necessary
+ var duration = null;
+ t.media.addEventListener('timeupdate',function() {
+ if (duration !== this.duration) {
+ duration = this.duration;
+ mejs.Utility.calculateTimeFormat(duration, t.options, t.options.framesPerSecond || 25);
+ }
+ }, 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) {
@@ -821,11 +876,11 @@
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%')) {
+ if (t.height.toString().indexOf('%') > 0 || (t.$node.css('max-width') !== 'none' && t.$node.css('max-width') !== 't.width') || (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) {
@@ -862,11 +917,11 @@
// 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) {
+ if (t.container.parent().length > 0 && t.container.parent()[0].tagName.toLowerCase() === 'body') { // && t.container.siblings().count == 0) {
parentWidth = $(window).width();
newHeight = $(window).height();
}
if ( newHeight && parentWidth ) {
@@ -905,27 +960,18 @@
.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
@@ -964,19 +1010,16 @@
rail.width(railWidth);
// dark space
total.width(railWidth - (total.outerWidth(true) - total.width()));
if (lastControl.css('position') != 'absolute') {
- lastControlPosition = lastControl.position();
+ lastControlPosition = lastControl.length ? lastControl.position() : null;
railWidth--;
}
} while (lastControlPosition !== null && lastControlPosition.top > 0 && railWidth > 0);
- if (t.setProgressRail)
- t.setProgressRail();
- if (t.setCurrentRail)
- t.setCurrentRail();
+ t.container.trigger('controlsresize');
},
buildposter: function(player, controls, layers, media) {
var t = this,
@@ -1013,11 +1056,11 @@
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 = $('<img width="100%" height="100%" alt="" />').appendTo(posterDiv);
}
posterImg.attr('src', url);
posterDiv.css({'background-image' : 'url(' + url + ')'});
},
@@ -1125,15 +1168,16 @@
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() {
+ media.addEventListener('error',function(e) {
+ t.handleError(e);
loading.hide();
- controls.find('.mejs-time-buffering').hide();
+ bigPlay.hide();
error.show();
- error.find('mejs-overlay-error').html("Error loading this resource");
+ error.find('.mejs-overlay-error').html("Error loading this resource");
}, false);
media.addEventListener('keydown', function(e) {
t.onkeydown(player, media, e);
}, false);
@@ -1237,10 +1281,12 @@
setSrc: function(src) {
this.media.setSrc(src);
},
remove: function() {
var t = this, featureIndex, feature;
+
+ t.container.prev('.mejs-offscreen').remove();
// invoke features cleanup
for (featureIndex in t.options.features) {
feature = t.options.features[featureIndex];
if (t['clean' + feature]) {
@@ -1282,10 +1328,19 @@
},
rebuildtracks: function(){
var t = this;
t.findTracks();
t.buildtracks(t, t.controls, t.layers, t.media);
+ },
+ resetSize: function(){
+ var t = this;
+ // webkit has trouble doing this without a delay
+ setTimeout(function () {
+ //
+ t.setPlayerSize(t.width, t.height);
+ t.setControlsSize();
+ }, 50);
}
};
(function(){
var rwindow = /^((after|before)print|(before)?unload|hashchange|message|o(ff|n)line|page(hide|show)|popstate|resize|storage)\b/;
@@ -1431,13 +1486,13 @@
});
// STOP BUTTON
$.extend(MediaElementPlayer.prototype, {
buildstop: function(player, controls, layers, media) {
- var t = this,
- stop =
- $('<div class="mejs-button mejs-stop-button mejs-stop">' +
+ var t = this;
+
+ $('<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) {
@@ -1446,12 +1501,12 @@
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) );
+ controls.find('.mejs-time-float-current').html( mejs.Utility.secondsToTimeCode(0, player.options));
+ controls.find('.mejs-currenttime').html( mejs.Utility.secondsToTimeCode(0, player.options));
layers.find('.mejs-poster').show();
}
});
}
});
@@ -1470,19 +1525,20 @@
buildprogress: function(player, controls, layers, media) {
$('<div class="mejs-time-rail">' +
'<span 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 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>' +
'</span>' +
- '</div>')
+ '</div>')
.appendTo(controls);
controls.find('.mejs-time-buffering').hide();
var
t = this,
@@ -1501,13 +1557,15 @@
newTime = 0,
pos = 0,
x;
// mouse or touch position relative to the object
- if (e.originalEvent.changedTouches) {
+ if (e.originalEvent && e.originalEvent.changedTouches) {
x = e.originalEvent.changedTouches[0].pageX;
- }else{
+ } else if (e.changedTouches) { // for Zepto
+ x = e.changedTouches[0].pageX;
+ } else {
x = e.pageX;
}
if (media.duration) {
if (x < offset.left) {
@@ -1526,11 +1584,11 @@
}
// position floating time box
if (!mejs.MediaFeatures.hasTouch) {
timefloat.css('left', pos);
- timefloatcurrent.html( mejs.Utility.secondsToTimeCode(newTime) );
+ timefloatcurrent.html( mejs.Utility.secondsToTimeCode(newTime, player.options) );
timefloat.show();
}
}
},
mouseIsDown = false,
@@ -1541,11 +1599,11 @@
// Accessibility for slider
var updateSlider = function (e) {
var seconds = media.currentTime,
timeSliderText = mejs.i18n.t('Time Slider'),
- time = mejs.Utility.secondsToTimeCode(seconds),
+ time = mejs.Utility.secondsToTimeCode(seconds, player.options),
duration = media.duration;
slider.attr({
'aria-label': timeSliderText,
'aria-valuemin': 0,
@@ -1676,10 +1734,14 @@
player.setProgressRail(e);
player.setCurrentRail(e);
updateSlider(e);
}, false);
+ t.container.on('controlsresize', function() {
+ player.setProgressRail();
+ player.setCurrentRail();
+ });
// store for later use
t.loaded = loaded;
t.total = total;
t.current = current;
@@ -1692,12 +1754,12 @@
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;
+ // account for a real array with multiple values - always read the end of the last buffer
+ percent = target.buffered.end(target.buffered.length - 1) / 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.
@@ -1736,10 +1798,11 @@
}
}
});
})(mejs.$);
+
(function($) {
// options
$.extend(mejs.MepDefaults, {
duration: -1,
@@ -1752,12 +1815,11 @@
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') +
+ mejs.Utility.secondsToTimeCode(0, player.options) +
'</span>'+
'</div>')
.appendTo(controls);
t.currenttime = t.controls.find('.mejs-currenttime');
@@ -1772,27 +1834,21 @@
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'))
- ) +
+ mejs.Utility.secondsToTimeCode(t.options.duration, t.options) +
'</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'))
- ) +
+ mejs.Utility.secondsToTimeCode(t.options.duration, t.options) +
'</span>' +
'</div>')
.appendTo(controls);
}
@@ -1805,22 +1861,22 @@
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));
+ t.currenttime.html(mejs.Utility.secondsToTimeCode(t.media.currentTime, t.options));
}
},
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));
+ t.durationD.html(mejs.Utility.secondsToTimeCode(t.options.duration > 0 ? t.options.duration : t.media.duration, t.options));
}
}
});
})(mejs.$);
@@ -1896,12 +1952,14 @@
volume = Math.min(volume,1);
// ajust mute button style
if (volume === 0) {
mute.removeClass('mejs-mute').addClass('mejs-unmute');
+ mute.children('button').attr('title', mejs.i18n.t('Unmute')).attr('aria-label', mejs.i18n.t('Unmute'));
} else {
mute.removeClass('mejs-unmute').addClass('mejs-mute');
+ mute.children('button').attr('title', mejs.i18n.t('Mute')).attr('aria-label', mejs.i18n.t('Mute'));
}
// top/left of full size volume slider background
var totalPosition = volumeTotal.position();
// position slider
@@ -1943,11 +2001,10 @@
// 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)
@@ -2076,29 +2133,29 @@
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);
- }
+
+ // 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);
+ }
+
+ t.container.on('controlsresize', function() {
+ positionVolumeHandle(media.volume);
+ });
}
});
})(mejs.$);
+
(function($) {
$.extend(mejs.MepDefaults, {
usePluginFullScreen: true,
newWindowCallback: function() { return '';},
@@ -2141,13 +2198,10 @@
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);
@@ -2373,10 +2427,13 @@
t.globalBind('keydown',function (e) {
if (((mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || t.isFullScreen) && e.keyCode == 27) {
player.exitFullScreen();
}
});
+
+ t.normalHeight = 0;
+ t.normalWidth = 0;
},
cleanfullscreen: function(player) {
player.exitFullScreen();
@@ -2397,12 +2454,12 @@
// 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();
+ t.normalHeight = t.container.height();
+ t.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) {
@@ -2413,30 +2470,41 @@
// 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;
+ 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;
+ percentErrorMargin = 0.002, // 0.2%
+ windowWidth = zoomMultiplier * $(window).width(),
+ screenWidth = screen.width,
+ // ** 13twelve
+ // Screen width is sort of useless: http://www.quirksmode.org/blog/archives/2013/11/screenwidth_is.html
+ // My rMBP ignores devicePixelRatio when returning the values, so fullscreen would always fail the "suddenly not fullscreen" test
+ // Theory: the gap between reported values should give us an indication of browser behavior with screen.width and devicePixelRatio
+ zoomedWindowWidth = zoomMultiplier * windowWidth;
+
+ if (Math.abs(screenWidth-windowWidth) > Math.abs(screenWidth-zoomedWindowWidth)) {
+ // screen.width is likely true pixels, not CSS pixels, so we need to use the zoomed window width for comparison
+ windowWidth = zoomedWindowWidth;
+ }
+ // ** / 13twelve
+ var absDiff = Math.abs(screenWidth - windowWidth),
+ 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);
+
+ }, 1000);
}
} else if (mejs.MediaFeatures.hasSemiNativeFullScreen) {
t.media.webkitEnterFullscreen();
return;
@@ -2514,10 +2582,12 @@
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');
+
+ t.container.trigger('enteredfullscreen');
},
exitFullScreen: function() {
var t = this;
@@ -2540,49 +2610,52 @@
// 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});
+ .width(t.normalWidth)
+ .height(t.normalHeight);
if (t.media.pluginType === 'native') {
t.$media
- .width(normalWidth)
- .height(normalHeight);
+ .width(t.normalWidth)
+ .height(t.normalHeight);
} else {
t.container.find('.mejs-shim')
- .width(normalWidth)
- .height(normalHeight);
+ .width(t.normalWidth)
+ .height(t.normalHeight);
- t.media.setVideoSize(normalWidth, normalHeight);
+ t.media.setVideoSize(t.normalWidth, t.normalHeight);
}
t.layers.children('div')
- .width(normalWidth)
- .height(normalHeight);
+ .width(t.normalWidth)
+ .height(t.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', '');
+
+ t.container.trigger('exitedfullscreen');
}
});
})(mejs.$);
(function($) {
// Speed
$.extend(mejs.MepDefaults, {
+ // We also support to pass object like this:
+ // [{name: 'Slow', value: '0.75'}, {name: 'Normal', value: '1.00'}, ...]
speeds: ['2.00', '1.50', '1.25', '1.00', '0.75'],
defaultSpeed: '1.00',
speedChar: 'x'
@@ -2597,57 +2670,94 @@
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);
+ inputId = null;
+
+ var speeds = [];
+ var defaultInArray = false;
+ for (var i=0, len=t.options.speeds.length; i < len; i++) {
+ var s = t.options.speeds[i];
+ if (typeof(s) === 'string'){
+ speeds.push({
+ name: s + t.options.speedChar,
+ value: s
+ });
+ if(s === t.options.defaultSpeed) {
+ defaultInArray = true;
+ }
+ }
+ else {
+ speeds.push(s);
+ if(s.value === t.options.defaultSpeed) {
+ defaultInArray = true;
+ }
+ }
}
- t.options.speeds.sort(function(a, b) {
- return parseFloat(b) - parseFloat(a);
+ if (!defaultInArray) {
+ speeds.push({
+ name: t.options.defaultSpeed + t.options.speedChar,
+ value: t.options.defaultSpeed
+ });
+ }
+
+ speeds.sort(function(a, b) {
+ return parseFloat(b.value) - parseFloat(a.value);
});
- for (var i = 0, il = t.options.speeds.length; i<il; i++) {
+ var getSpeedNameFromValue = function(value) {
+ for(i=0,len=speeds.length; i <len; i++) {
+ if (speeds[i].value === value) {
+ return speeds[i].name;
+ }
+ }
+ };
+
+ var html = '<div class="mejs-button mejs-speed-button">' +
+ '<button type="button">' + getSpeedNameFromValue(t.options.defaultSpeed) + '</button>' +
+ '<div class="mejs-speed-selector">' +
+ '<ul>';
+
+ for (i = 0, il = speeds.length; i<il; i++) {
+ inputId = t.id + '-speed-' + speeds[i].value;
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' : '') +
+ 'value="' + speeds[i].value + '" ' +
+ 'id="' + inputId + '" ' +
+ (speeds[i].value === 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>' +
+ '<label for="' + inputId + '" ' +
+ (speeds[i].value === t.options.defaultSpeed ? ' class="mejs-speed-selected"' : '') +
+ '>' + speeds[i].name + '</label>' +
'</li>';
}
html += '</ul></div></div>';
speedButton = $(html).appendTo(controls);
- speedSelector = speedButton.find('.mejs-speed-selector');
+ speedSelector = speedButton.find('.mejs-speed-selector');
- playbackspeed = t.options.defaultSpeed;
+ playbackSpeed = t.options.defaultSpeed;
speedSelector
.on('click', 'input[type="radio"]', function() {
var newSpeed = $(this).attr('value');
- playbackspeed = newSpeed;
+ playbackSpeed = newSpeed;
media.playbackRate = parseFloat(newSpeed);
- speedButton.find('button').html(newSpeed + t.options.speedChar);
+ speedButton.find('button').html(getSpeedNameFromValue(newSpeed));
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');
+ speedButton
+ .one( 'mouseenter focusin', function() {
+ speedSelector
+ .height(
+ speedButton.find('.mejs-speed-selector ul').outerHeight(true) +
+ speedButton.find('.mejs-speed-translations').outerHeight(true))
+ .css('top', (-1 * speedSelector.height()) + 'px');
+ });
}
}
});
})(mejs.$);
@@ -2659,10 +2769,14 @@
// this will automatically turn on a <track>
startLanguage: '',
tracksText: mejs.i18n.t('Captions/Subtitles'),
+ // By default, no WAI-ARIA live region - don't make a
+ // screen reader speak captions over an audio track.
+ tracksAriaLive: false,
+
// 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,
@@ -2686,12 +2800,13 @@
buildtracks: function(player, controls, layers, media) {
if (player.tracks.length === 0)
return;
var t = this,
- i,
- options = '';
+ attr = t.options.tracksAriaLive ?
+ 'role="log" aria-live="assertive" aria-atomic="false"' : '',
+ i;
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";
}
@@ -2699,11 +2814,12 @@
t.cleartracks(player, controls, layers, media);
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>')
+ $('<div class="mejs-captions-layer mejs-layer"><div class="mejs-captions-position mejs-captions-position-hover" ' +
+ attr + '><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>'+
@@ -2819,10 +2935,14 @@
$(this).css('display','block');
});
}
});
+ t.container.on('controlsresize', function() {
+ t.adjustLanguageBox();
+ });
+
// check for autoplay
if (player.node.getAttribute('autoplay') !== null) {
player.chapters.css('visibility','hidden');
}
},
@@ -2870,12 +2990,10 @@
track = t.tracks[index],
after = function() {
track.isLoaded = true;
- // create button
- //t.addTrackButton(track.srclang);
t.enableTrackButton(track.srclang, track.label);
t.loadNextTrack();
};
@@ -2906,10 +3024,11 @@
if (track.kind == 'slides') {
t.setupSlides(track);
}
},
error: function() {
+ t.removeTrackButton(track.srclang);
t.loadNextTrack();
}
});
},
@@ -2931,10 +3050,18 @@
$('#' + t.id + '_captions_' + lang).prop('checked', true).trigger('click');
}
t.adjustLanguageBox();
},
+
+ removeTrackButton: function(lang) {
+ var t = this;
+
+ t.captionsButton.find('input[value=' + lang + ']').closest('li').remove();
+
+ t.adjustLanguageBox();
+ },
addTrackButton: function(lang, label) {
var t = this;
if (label === '') {
label = mejs.language.codes[lang] || lang;
@@ -2968,11 +3095,11 @@
hasSubtitles = false;
// check if any subtitles
if (t.options.hideCaptionsButtonWhenEmpty) {
for (i=0; i<t.tracks.length; i++) {
- if (t.tracks[i].kind == 'subtitles') {
+ if (t.tracks[i].kind == 'subtitles' && t.tracks[i].isLoaded) {
hasSubtitles = true;
break;
}
}
@@ -3114,11 +3241,11 @@
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) + '–' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].stop) + '</span>' +
+ '<span class="ch-time">' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].start, t.options) + '–' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].stop, t.options) + '</span>' +
'</div>' +
'</div>'));
usedPercent += percent;
}
@@ -3264,12 +3391,10 @@
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) {
@@ -3534,9 +3659,41 @@
}
});
})(mejs.$);
+(function($) {
+ // skip back button
+
+ $.extend(mejs.MepDefaults, {
+ skipBackInterval: 30,
+ // %1 will be replaced with skipBackInterval in this string
+ skipBackText: mejs.i18n.t('Skip back %1 seconds')
+ });
+
+ $.extend(MediaElementPlayer.prototype, {
+ buildskipback: function(player, controls, layers, media) {
+ var
+ t = this,
+ // Replace %1 with skip back interval
+ backText = t.options.skipBackText.replace('%1', t.options.skipBackInterval),
+ // create the loop button
+ loop =
+ $('<div class="mejs-button mejs-skip-back-button">' +
+ '<button type="button" aria-controls="' + t.id + '" title="' + backText + '" aria-label="' + backText + '">' + t.options.skipBackInterval + '</button>' +
+ '</div>')
+ // append it to the toolbar
+ .appendTo(controls)
+ // add a click toggle event
+ .click(function() {
+ media.setCurrentTime(Math.max(media.currentTime - t.options.skipBackInterval, 0));
+ $(this).find('button').blur();
+ });
+ }
+ });
+
+})(mejs.$);
+
/**
* Postroll plugin
*/
(function($) {
\ No newline at end of file