upgrade mediaelement.js to instructure/mediaelement#1a177ed2cc
fixes: CNVS-34022 test plan: should include general improvements and bugfixes from upgrade, but no core functionality should change. - video and audio player - speed controls - quality controls - caption track controls (including upload and delete) - note: this does not yet fix all a11y issues Change-Id: Ifc0afbece6044cd04087f474c9e9c6a30caca74d Reviewed-on: https://gerrit.instructure.com/111518 Tested-by: Jenkins Reviewed-by: Ryan Shaw <ryan@instructure.com> QA-Review: Jeremy Putnam <jeremyp@instructure.com> Product-Review: Simon Williams <simon@instructure.com>
This commit is contained in:
parent
b91fed2744
commit
13e71ca726
|
@ -33,6 +33,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* Subtitile upload link */
|
||||
.mejs-captions-selector .upload-track {
|
||||
color: white;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.mejs-captions-selector .upload-track:focus {
|
||||
color: rgba(33, 248, 248, 1);
|
||||
}
|
||||
|
||||
//
|
||||
// Playback speed control is based on code from an as-yet-unmerged pull request to mediaelement.js
|
||||
// See: https://github.com/matthillman/mediaelement/commit/e9efc9473ca38c240b712a11ba4c035651c204d4
|
||||
|
@ -118,4 +128,13 @@ div.mejs-speed-button {
|
|||
background-color: rgb(200, 200, 200) !important;
|
||||
background-color: rgba(255,255,255,.4) !important;
|
||||
}
|
||||
|
||||
.mejs-controls .mejs-speed-button .mejs-speed-selector a:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.mejs-controls .mejs-speed-button .mejs-speed-selector .focus {
|
||||
background-color: rgb(200, 200, 200) !important;
|
||||
background-color: rgba(255,255,255,.4) !important;
|
||||
}
|
||||
/* End: Speed */
|
||||
|
|
|
@ -1,29 +1,43 @@
|
|||
/*
|
||||
Built by: build_media_element_js.rake
|
||||
from https://github.com/johndyer/mediaelement.git
|
||||
revision: ceeb1a7f41b2557c4f6a865cee5564c82039201d
|
||||
from https://github.com/instructure/mediaelement.git
|
||||
revision: a84fd12157c225714283bffc053ba17e1eea6c0c
|
||||
|
||||
YOU SHOULDN'T EDIT ME DIRECTLY
|
||||
*/
|
||||
.mejs-offscreen{
|
||||
/* Accessibility: hide screen reader texts (and prefer "top" for RTL languages). */
|
||||
/* Accessibility: hide screen reader texts (and prefer "top" for RTL languages). Reference: http://blog.rrwd.nl/2015/04/04/the-screen-reader-text-class-why-and-how/ */
|
||||
clip: rect(1px 1px 1px 1px); /* IE6, IE7 - no likey commas */
|
||||
clip: rect(1px, 1px, 1px, 1px); /* IE8-IE11 - we likey commas, no support for clip-path */
|
||||
clip-path: polygon(0px 0px, 0px 0px,0px 0px, 0px 0px);
|
||||
position: absolute !important;
|
||||
top: -10000px;
|
||||
left: -10000px;
|
||||
overflow: hidden;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mejs-container {
|
||||
position: relative;
|
||||
background: #000;
|
||||
font-family: Helvetica, Arial;
|
||||
font-family: "Helvetica", Arial, serif;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
text-indent: 0;
|
||||
}
|
||||
|
||||
.mejs-fill-container,.mejs-fill-container .mejs-container{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.mejs-fill-container{
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mejs-container:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.me-plugin {
|
||||
position: absolute;
|
||||
}
|
||||
|
@ -91,7 +105,6 @@
|
|||
.mejs-poster img {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.mejs-overlay {
|
||||
|
@ -111,11 +124,11 @@
|
|||
width: 100px;
|
||||
height: 100px;
|
||||
margin: -50px 0 0 -50px;
|
||||
background: url(/images/mediaelement/bigplay.svg) no-repeat;
|
||||
background: url("/images/mediaelement/bigplay.svg") no-repeat;
|
||||
}
|
||||
|
||||
.no-svg .mejs-overlay-button {
|
||||
background-image: url(/images/mediaelement/bigplay.png);
|
||||
background-image: url("/images/mediaelement/bigplay.png");
|
||||
}
|
||||
|
||||
.mejs-overlay:hover .mejs-overlay-button {
|
||||
|
@ -130,7 +143,7 @@
|
|||
height: 80px;
|
||||
margin: -40px 0 0 -40px;
|
||||
background: #333;
|
||||
background: url(/images/mediaelement/background.png);
|
||||
background: url("/images/mediaelement/background.png");
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(50,50,50,0.9)), to(rgba(0,0,0,0.9)));
|
||||
background: -webkit-linear-gradient(top, rgba(50,50,50,0.9), rgba(0,0,0,0.9));
|
||||
|
@ -144,7 +157,7 @@
|
|||
display: block;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: transparent url(/images/mediaelement/loading.gif) 50% 50% no-repeat;
|
||||
background: transparent url("/images/mediaelement/loading.gif") 50% 50% no-repeat;
|
||||
}
|
||||
|
||||
/* End: LAYERS */
|
||||
|
@ -157,7 +170,7 @@
|
|||
padding: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: url(/images/mediaelement/background.png);
|
||||
background: url("/images/mediaelement/background.png");
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(50,50,50,0.7)), to(rgba(0,0,0,0.7)));
|
||||
background: -webkit-linear-gradient(top, rgba(50,50,50,0.7), rgba(0,0,0,0.7));
|
||||
|
@ -179,7 +192,7 @@
|
|||
height: 26px;
|
||||
font-size: 11px;
|
||||
line-height: 11px;
|
||||
font-family: Helvetica, Arial;
|
||||
font-family: "Helvetica", Arial, serif;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
|
@ -195,11 +208,11 @@
|
|||
height: 16px;
|
||||
width: 16px;
|
||||
border: 0;
|
||||
background: transparent url(/images/mediaelement/controls.svg) no-repeat;
|
||||
background: transparent url("/images/mediaelement/controls.svg") no-repeat;
|
||||
}
|
||||
|
||||
.no-svg .mejs-controls .mejs-button button {
|
||||
background-image: url(/images/mediaelement/controls.png);
|
||||
background-image: url("/images/mediaelement/controls.png");
|
||||
}
|
||||
|
||||
/* :focus for accessibility */
|
||||
|
@ -428,7 +441,7 @@
|
|||
display: none;
|
||||
height: 115px;
|
||||
width: 25px;
|
||||
background: url(/images/mediaelement/background.png);
|
||||
background: url("/images/mediaelement/background.png");
|
||||
background: rgba(50, 50, 50, 0.7);
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
|
@ -564,7 +577,7 @@
|
|||
right: -51px;
|
||||
width: 85px;
|
||||
height: 100px;
|
||||
background: url(/images/mediaelement/background.png);
|
||||
background: url("/images/mediaelement/background.png");
|
||||
background: rgba(50,50,50,0.7);
|
||||
border: solid 1px transparent;
|
||||
padding: 10px 10px 0 10px;
|
||||
|
@ -574,11 +587,9 @@
|
|||
border-radius: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
.mejs-controls .mejs-captions-button:hover .mejs-captions-selector {
|
||||
visibility: visible;
|
||||
}
|
||||
*/
|
||||
|
||||
.mejs-controls .mejs-captions-button .mejs-captions-selector ul {
|
||||
margin: 0;
|
||||
|
@ -608,7 +619,7 @@
|
|||
float: left;
|
||||
padding: 4px 0 0 0;
|
||||
line-height: 15px;
|
||||
font-family: helvetica, arial;
|
||||
font-family: "Helvetica", Arial, serif;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
|
@ -621,7 +632,7 @@
|
|||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
-xborder-right: solid 1px #fff;
|
||||
border-right: solid 1px #fff;
|
||||
width: 10000px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
@ -719,10 +730,12 @@
|
|||
}
|
||||
|
||||
.mejs-captions-text {
|
||||
padding: 3px 5px;
|
||||
background: url(/images/mediaelement/background.png);
|
||||
padding: 0;
|
||||
background: url("/images/mediaelement/background.png");
|
||||
background: rgba(20, 20, 20, 0.5);
|
||||
white-space: pre-wrap;
|
||||
-webkit-box-shadow: 5px 0 0 rgba(20, 20, 20, 0.5), -5px 0 0 rgba(20, 20, 20, 0.5);
|
||||
box-shadow: 5px 0 0 rgba(20, 20, 20, 0.5), -5px 0 0 rgba(20, 20, 20, 0.5);
|
||||
}
|
||||
/* End: Track (Captions and Chapters) */
|
||||
|
||||
|
@ -790,7 +803,7 @@
|
|||
}
|
||||
|
||||
.mejs-contextmenu .mejs-contextmenu-item {
|
||||
font-family: Helvetica, Arial;
|
||||
font-family: "Helvetica", Arial, serif;
|
||||
font-size: 12px;
|
||||
padding: 4px 6px;
|
||||
cursor: pointer;
|
||||
|
@ -811,13 +824,12 @@
|
|||
}
|
||||
|
||||
.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
bottom: 26px;
|
||||
right: -10px;
|
||||
width: 130px;
|
||||
height: 100px;
|
||||
background: url(/images/mediaelement/background.png);
|
||||
background: url("/images/mediaelement/background.png");
|
||||
background: rgba(50,50,50,0.7);
|
||||
border: solid 1px transparent;
|
||||
padding: 10px;
|
||||
|
@ -855,7 +867,7 @@
|
|||
float: left;
|
||||
padding: 4px 0 0 0;
|
||||
line-height: 15px;
|
||||
font-family: helvetica, arial;
|
||||
font-family: "Helvetica", Arial, serif;
|
||||
font-size: 10px;
|
||||
}
|
||||
/* End: Source Chooser */
|
||||
|
@ -867,7 +879,7 @@
|
|||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: url(/images/mediaelement/background.png);
|
||||
background: url("/images/mediaelement/background.png");
|
||||
background: rgba(50,50,50,0.7);
|
||||
z-index: 1000;
|
||||
overflow: hidden;
|
||||
|
@ -880,7 +892,7 @@
|
|||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
background: url(/images/mediaelement/background.png);
|
||||
background: url("/images/mediaelement/background.png");
|
||||
background: rgba(50,50,50,0.7);
|
||||
color: #fff;
|
||||
padding: 4px;
|
||||
|
@ -905,13 +917,12 @@ div.mejs-speed-button {
|
|||
}
|
||||
|
||||
.mejs-controls .mejs-speed-button .mejs-speed-selector {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
top: -100px;
|
||||
left: -10px;
|
||||
width: 60px;
|
||||
height: 100px;
|
||||
background: url(/images/mediaelement/background.png);
|
||||
background: url("/images/mediaelement/background.png");
|
||||
background: rgba(50, 50, 50, 0.7);
|
||||
border: solid 1px transparent;
|
||||
padding: 0;
|
||||
|
@ -921,9 +932,6 @@ div.mejs-speed-button {
|
|||
border-radius: 0;
|
||||
}
|
||||
|
||||
.mejs-controls .mejs-speed-button:hover > .mejs-speed-selector {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.mejs-controls .mejs-speed-button .mejs-speed-selector ul li label.mejs-speed-selected {
|
||||
color: rgba(33, 248, 248, 1);
|
||||
|
@ -939,7 +947,6 @@ div.mejs-speed-button {
|
|||
|
||||
.mejs-controls .mejs-speed-button .mejs-speed-selector ul li {
|
||||
margin: 0 0 6px 0;
|
||||
padding: 0 10px;
|
||||
list-style-type: none !important;
|
||||
display: block;
|
||||
color: #fff;
|
||||
|
@ -950,16 +957,14 @@ div.mejs-speed-button {
|
|||
clear: both;
|
||||
float: left;
|
||||
margin: 3px 3px 0 5px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mejs-controls .mejs-speed-button .mejs-speed-selector ul li label {
|
||||
width: 60px;
|
||||
float: left;
|
||||
padding: 4px 0 0 0;
|
||||
line-height: 15px;
|
||||
font-family: helvetica, arial;
|
||||
font-size: 11.5px;
|
||||
font-family: "Helvetica", Arial, serif;
|
||||
font-size: 11px;
|
||||
color: white;
|
||||
margin-left: 5px;
|
||||
cursor: pointer;
|
||||
|
@ -971,11 +976,24 @@ div.mejs-speed-button {
|
|||
}
|
||||
/* End: Speed */
|
||||
|
||||
/* Start: Jump Forward */
|
||||
|
||||
.mejs-controls .mejs-button.mejs-jump-forward-button {
|
||||
background: transparent url("/images/mediaelement/jumpforward.png") no-repeat 3px 3px;
|
||||
}
|
||||
.mejs-controls .mejs-button.mejs-jump-forward-button button {
|
||||
background: transparent;
|
||||
font-size: 9px;
|
||||
line-height: normal;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* End: Jump Forward */
|
||||
|
||||
/* Start: Skip Back */
|
||||
|
||||
.mejs-controls .mejs-button.mejs-skip-back-button {
|
||||
background: transparent url(/images/mediaelement/skipback.png) no-repeat;
|
||||
background-position: 3px 3px;
|
||||
background: transparent url("/images/mediaelement/skipback.png") no-repeat 3px 3px;
|
||||
}
|
||||
.mejs-controls .mejs-button.mejs-skip-back-button button {
|
||||
background: transparent;
|
||||
|
|
|
@ -9,7 +9,7 @@ task :build_media_element_js do
|
|||
|
||||
repo_path = '../mediaelement'
|
||||
public_path = 'public'
|
||||
repo_location = "https://github.com/johndyer/mediaelement.git"
|
||||
repo_location = "https://github.com/instructure/mediaelement.git"
|
||||
|
||||
unless File.exists? repo_path
|
||||
puts "cloning repo"
|
||||
|
@ -70,7 +70,7 @@ task :build_media_element_js do
|
|||
puts "Copying CSS"
|
||||
css = File.read "#{repo_path}/src/css/mediaelementplayer.css"
|
||||
# fix urls to background images
|
||||
css = css.gsub('url(', 'url(/images/mediaelement/')
|
||||
css = css.gsub('url("', 'url("/images/mediaelement/')
|
||||
File.open("app/stylesheets/vendor/_mediaelementplayer.scss", 'w') {|f| f.write(rev_msg + css) }
|
||||
|
||||
puts 'Copying Skin Files'
|
||||
|
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
|
@ -1,89 +1,210 @@
|
|||
//
|
||||
// mep-feature-tracks.js with additional customizations
|
||||
//
|
||||
// to see the diff, run:
|
||||
//
|
||||
// upstream_url='https://raw.githubusercontent.com/instructure/mediaelement/1a177ed2cc3d51689a210d5c034c88112d5a2e42/src/js/mep-feature-sourcechooser.js'
|
||||
// diff -bu \
|
||||
// <(curl -s "${upstream_url}") \
|
||||
// public/javascripts/mediaelement/mep-feature-sourcechooser-instructure.js
|
||||
//
|
||||
// Source Chooser Plugin
|
||||
(function($) {
|
||||
|
||||
$.extend(mejs.MepDefaults, {
|
||||
sourcechooserText: 'Source Chooser'
|
||||
});
|
||||
$.extend(mejs.MepDefaults, {
|
||||
sourcechooserText: 'Source Chooser'
|
||||
});
|
||||
|
||||
$.extend(MediaElementPlayer.prototype, {
|
||||
buildsourcechooser: function(player, controls, layers, media) {
|
||||
if (!player.isVideo) { return; }
|
||||
$.extend(MediaElementPlayer.prototype, {
|
||||
sources: [],
|
||||
|
||||
var t = this;
|
||||
buildsourcechooser: function(player, controls, layers, media) {
|
||||
// INSTRUCTURE ADDED
|
||||
if (!player.isVideo) { return; }
|
||||
|
||||
player.sourcechooserButton =
|
||||
$('<div class="mejs-button mejs-sourcechooser-button">'+
|
||||
'<button type="button" aria-controls="' + t.id + '" title="' + t.options.sourcechooserText + '" aria-label="' + t.options.sourcechooserText + '"></button>'+
|
||||
'<div class="mejs-sourcechooser-selector">'+
|
||||
'<ul>'+
|
||||
'</ul>'+
|
||||
'</div>'+
|
||||
'</div>')
|
||||
.appendTo(controls)
|
||||
var t = this;
|
||||
var hoverTimeout;
|
||||
|
||||
// hover
|
||||
.hover(function() {
|
||||
$(this).find('.mejs-sourcechooser-selector').css('visibility','visible');
|
||||
}, function() {
|
||||
$(this).find('.mejs-sourcechooser-selector').css('visibility','hidden');
|
||||
})
|
||||
player.sourcechooserButton =
|
||||
$('<div class="mejs-button mejs-sourcechooser-button">'+
|
||||
'<button type="button" role="button" aria-haspopup="true" aria-controls="' + t.id + '" title="' + t.options.sourcechooserText + '" aria-label="' + t.options.sourcechooserText + '" aria-live="assertive"></button>'+
|
||||
'<div class="mejs-sourcechooser-selector mejs-offscreen" role="menu" aria-expanded="false" aria-hidden="true">'+
|
||||
'<ul>'+
|
||||
'</ul>'+
|
||||
'</div>'+
|
||||
'</div>')
|
||||
.appendTo(controls)
|
||||
|
||||
// handle clicks to the language radio buttons
|
||||
.on('click', 'input[type=radio]', function() {
|
||||
if (media.currentSrc === this.value) { return; }
|
||||
// hover
|
||||
.hover(function() {
|
||||
clearTimeout(hoverTimeout);
|
||||
player.showSourcechooserSelector();
|
||||
}, function() {
|
||||
hoverTimeout = setTimeout(function () {
|
||||
player.hideSourcechooserSelector();
|
||||
}, t.options.menuTimeoutMouseLeave);
|
||||
})
|
||||
|
||||
var src = this.value;
|
||||
var currentTime = media.currentTime;
|
||||
var wasPlaying = !media.paused;
|
||||
// keyboard menu activation
|
||||
.on('keydown', function (e) {
|
||||
var keyCode = e.keyCode;
|
||||
|
||||
$(media).one('loadedmetadata', function() {
|
||||
media.setCurrentTime(currentTime);
|
||||
});
|
||||
switch (keyCode) {
|
||||
case 32: // space
|
||||
if (!mejs.MediaFeatures.isFirefox) { // space sends the click event in Firefox
|
||||
player.showSourcechooserSelector();
|
||||
}
|
||||
$(this).find('.mejs-sourcechooser-selector')
|
||||
.find('input[type=radio]:checked').first().focus();
|
||||
break;
|
||||
case 13: // enter
|
||||
player.showSourcechooserSelector();
|
||||
$(this).find('.mejs-sourcechooser-selector')
|
||||
.find('input[type=radio]:checked').first().focus();
|
||||
break;
|
||||
case 27: // esc
|
||||
player.hideSourcechooserSelector();
|
||||
$(this).find('button').focus();
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
})
|
||||
|
||||
$(media).one('canplay', function() {
|
||||
if (wasPlaying) {
|
||||
media.play();
|
||||
}
|
||||
});
|
||||
// close menu when tabbing away
|
||||
.on('focusout', mejs.Utility.debounce(function (e) { // Safari triggers focusout multiple times
|
||||
// Firefox does NOT support e.relatedTarget to see which element
|
||||
// just lost focus, so wait to find the next focused element
|
||||
setTimeout(function () {
|
||||
var parent = $(document.activeElement).closest('.mejs-sourcechooser-selector');
|
||||
if (!parent.length) {
|
||||
// focus is outside the control; close menu
|
||||
player.hideSourcechooserSelector();
|
||||
}
|
||||
}, 0);
|
||||
}, 100))
|
||||
|
||||
media.setSrc(src);
|
||||
media.load();
|
||||
});
|
||||
// handle clicks to the source radio buttons
|
||||
.delegate('input[type=radio]', 'click', function() {
|
||||
// set aria states
|
||||
$(this).attr('aria-selected', true).attr('checked', 'checked');
|
||||
$(this).closest('.mejs-sourcechooser-selector').find('input[type=radio]').not(this).attr('aria-selected', 'false').removeAttr('checked');
|
||||
|
||||
// add to list
|
||||
for (var i in this.node.children) {
|
||||
var src = this.node.children[i];
|
||||
if (src.nodeName === 'SOURCE' && (media.canPlayType(src.type) === 'probably' || media.canPlayType(src.type) === 'maybe')) {
|
||||
player.addSourceButton(src.src, src.title, src.type, media.src === src.src);
|
||||
}
|
||||
}
|
||||
},
|
||||
var src = this.value;
|
||||
|
||||
addSourceButton: function(src, label, type, isCurrent) {
|
||||
var t = this;
|
||||
if (label === '' || label === undefined) {
|
||||
label = src;
|
||||
}
|
||||
type = type.split('/')[1];
|
||||
if (media.currentSrc != src) {
|
||||
var currentTime = media.currentTime;
|
||||
var paused = media.paused;
|
||||
media.pause();
|
||||
media.setSrc(src);
|
||||
|
||||
t.sourcechooserButton.find('ul').append(
|
||||
$('<li>'+
|
||||
'<label>' +
|
||||
'<input type="radio" name="' + t.id + '_sourcechooser" value="' + src + '" ' + (isCurrent ? 'checked="checked"' : '') + ' />' +
|
||||
label + ' (' + type + ')</label>' +
|
||||
'</li>')
|
||||
);
|
||||
media.addEventListener('loadedmetadata', function(e) {
|
||||
media.currentTime = currentTime;
|
||||
}, true);
|
||||
|
||||
t.adjustSourcechooserBox();
|
||||
},
|
||||
var canPlayAfterSourceSwitchHandler = function(e) {
|
||||
if (!paused) {
|
||||
media.play();
|
||||
}
|
||||
media.removeEventListener("canplay", canPlayAfterSourceSwitchHandler, true);
|
||||
};
|
||||
media.addEventListener('canplay', canPlayAfterSourceSwitchHandler, true);
|
||||
media.load();
|
||||
}
|
||||
|
||||
adjustSourcechooserBox: function() {
|
||||
var t = this;
|
||||
// adjust the size of the outer box
|
||||
t.sourcechooserButton.find('.mejs-sourcechooser-selector').height(
|
||||
t.sourcechooserButton.find('.mejs-sourcechooser-selector ul').outerHeight(true)
|
||||
);
|
||||
}
|
||||
});
|
||||
t.setAriaLabel(media);
|
||||
})
|
||||
|
||||
// Handle click so that screen readers can toggle the menu
|
||||
.delegate('button', 'click', function (e) {
|
||||
if ($(this).siblings('.mejs-sourcechooser-selector').hasClass('mejs-offscreen')) {
|
||||
player.showSourcechooserSelector();
|
||||
$(this).siblings('.mejs-sourcechooser-selector').find('input[type=radio]:checked').first().focus();
|
||||
} else {
|
||||
player.hideSourcechooserSelector();
|
||||
}
|
||||
});
|
||||
|
||||
// add to list
|
||||
for (var i in this.node.children) {
|
||||
var src = this.node.children[i];
|
||||
if (src.nodeName === 'SOURCE' && (media.canPlayType(src.type) == 'probably' || media.canPlayType(src.type) == 'maybe')) {
|
||||
t.sources.push(src);
|
||||
player.addSourceButton(src.src, src.title, src.type, media.src == src.src);
|
||||
}
|
||||
}
|
||||
|
||||
t.setAriaLabel(media);
|
||||
},
|
||||
|
||||
setAriaLabel: function(media) {
|
||||
var label = mejs.i18n.t(this.options.sourcechooserText)
|
||||
var current = this.currentSource(media);
|
||||
|
||||
if (current) {
|
||||
label += ': ' + mejs.i18n.t(current);
|
||||
}
|
||||
|
||||
this.sourcechooserButton.find('button')
|
||||
.attr('aria-label', label)
|
||||
.attr('title', label);
|
||||
},
|
||||
|
||||
addSourceButton: function(src, label, type, isCurrent) {
|
||||
var t = this;
|
||||
if (label === '' || label == undefined) {
|
||||
label = src;
|
||||
}
|
||||
type = type.split('/')[1];
|
||||
|
||||
t.sourcechooserButton.find('ul').append(
|
||||
$('<li>'+
|
||||
'<input type="radio" name="' + t.id + '_sourcechooser" id="' + t.id + '_sourcechooser_' + label + type + '" role="menuitemradio" value="' + src + '" ' + (isCurrent ? 'checked="checked"' : '') + 'aria-selected="' + isCurrent + '" aria-label="' + label + '"' + ' />'+
|
||||
'<label for="' + t.id + '_sourcechooser_' + label + type + '" aria-hidden="true">' + label + ' (' + type + ')</label>'+
|
||||
'</li>')
|
||||
);
|
||||
|
||||
t.adjustSourcechooserBox();
|
||||
|
||||
},
|
||||
|
||||
currentSource: function(media) {
|
||||
var current = this.sources.filter(function(src) {
|
||||
return src.src == media.src;
|
||||
})[0];
|
||||
|
||||
if (current) {
|
||||
return current.title || '';
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
|
||||
adjustSourcechooserBox: function() {
|
||||
var t = this;
|
||||
// adjust the size of the outer box
|
||||
t.sourcechooserButton.find('.mejs-sourcechooser-selector').height(
|
||||
t.sourcechooserButton.find('.mejs-sourcechooser-selector ul').outerHeight(true)
|
||||
);
|
||||
},
|
||||
|
||||
hideSourcechooserSelector: function () {
|
||||
this.sourcechooserButton.find('.mejs-sourcechooser-selector')
|
||||
.addClass('mejs-offscreen')
|
||||
.attr('aria-expanded', 'false')
|
||||
.attr('aria-hidden', 'true')
|
||||
.find('input[type=radio]') // make radios not focusable
|
||||
.attr('tabindex', '-1');
|
||||
},
|
||||
|
||||
showSourcechooserSelector: function () {
|
||||
this.sourcechooserButton.find('.mejs-sourcechooser-selector')
|
||||
.removeClass('mejs-offscreen')
|
||||
.attr('aria-expanded', 'true')
|
||||
.attr('aria-hidden', 'false')
|
||||
.find('input[type=radio]')
|
||||
.attr('tabindex', '0');
|
||||
}
|
||||
});
|
||||
|
||||
})(mejs.$);
|
||||
|
|
|
@ -1,104 +1,225 @@
|
|||
//
|
||||
// Playback speed control is based on code from an as-yet-unmerged pull request to mediaelement.js
|
||||
// See: https://github.com/matthillman/mediaelement/commit/e9efc9473ca38c240b712a11ba4c035651c204d4
|
||||
// And: https://github.com/johndyer/mediaelement/pull/1249
|
||||
// mep-feature-speed.js with additional customizations
|
||||
//
|
||||
// to see the diff, run:
|
||||
//
|
||||
// upstream_url='https://raw.githubusercontent.com/instructure/mediaelement/1a177ed2cc3d51689a210d5c034c88112d5a2e42/src/js/mep-feature-speed.js'
|
||||
// diff -bu \
|
||||
// <(curl -s "${upstream_url}") \
|
||||
// public/javascripts/mediaelement/mep-feature-speed-instructure.js
|
||||
//
|
||||
(function($) {
|
||||
|
||||
// Speed
|
||||
$.extend(mejs.MepDefaults, {
|
||||
|
||||
// INSTRUCTURE CUSTOMIZATION: adjust default available speeds
|
||||
// We also support to pass object like this:
|
||||
// [{name: 'Slow', value: '0.75'}, {name: 'Normal', value: '1.00'}, ...]
|
||||
|
||||
// INSTRUCTURE CUSTOMIZATION: remove 1.25 speed and add 0.50
|
||||
speeds: ['2.00', '1.50', '1.00', '0.75', '0.50'],
|
||||
|
||||
defaultSpeed: '1.00'
|
||||
defaultSpeed: '1.00',
|
||||
|
||||
speedChar: 'x',
|
||||
|
||||
speedLabel: 'Change playback speed'
|
||||
});
|
||||
|
||||
$.extend(MediaElementPlayer.prototype, {
|
||||
|
||||
// INSTRUCTURE CUSTOMIZATION - pulling latest definition of isIE from ME.js master with IE11 fixes
|
||||
isIE: function() {
|
||||
return (window.navigator.appName.match(/microsoft/gi) !== null) || (window.navigator.userAgent.match(/trident/gi) !== null);
|
||||
},
|
||||
|
||||
buildspeed: function(player, controls, layers, media) {
|
||||
// INSTRUCTURE CUSTOMIZATION: enable playback speed controls for both audio and video
|
||||
// if (!player.isVideo)
|
||||
// return;
|
||||
|
||||
var t = this;
|
||||
var hoverTimeout;
|
||||
|
||||
if (t.media.pluginType !== 'native') { return; }
|
||||
if (t.media.pluginType == 'native') {
|
||||
var
|
||||
speedButton = null,
|
||||
speedSelector = null,
|
||||
playbackSpeed = null,
|
||||
inputId = null,
|
||||
isCurrent = null;
|
||||
|
||||
var s = '<div class="mejs-button mejs-speed-button"><button type="button">'+t.options.defaultSpeed+'x</button><div class="mejs-speed-selector"><ul>';
|
||||
var i, ss;
|
||||
|
||||
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 (i = 0; i < t.options.speeds.length; i++) {
|
||||
s += '<li>';
|
||||
if (t.options.speeds[i] === t.options.defaultSpeed) {
|
||||
s += '<label class="mejs-speed-selected">'+ t.options.speeds[i] + 'x';
|
||||
s += '<input type="radio" name="speed" value="' + t.options.speeds[i] + '" checked=true />';
|
||||
} else {
|
||||
s += '<label>'+ t.options.speeds[i] + 'x';
|
||||
s += '<input type="radio" name="speed" value="' + t.options.speeds[i] + '" />';
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
s += '</label></li>';
|
||||
}
|
||||
s += '</ul></div></div>';
|
||||
|
||||
player.speedButton = $(s).appendTo(controls);
|
||||
|
||||
player.playbackspeed = t.options.defaultSpeed;
|
||||
|
||||
player.$media.on('loadedmetadata', function() {
|
||||
media.playbackRate = parseFloat(player.playbackspeed);
|
||||
});
|
||||
|
||||
player.speedButton.on('click', 'input[type=radio]', function() {
|
||||
player.playbackspeed = $(this).attr('value');
|
||||
media.playbackRate = parseFloat(player.playbackspeed);
|
||||
player.speedButton.find('button').text(player.playbackspeed + 'x');
|
||||
player.speedButton.find('.mejs-speed-selected').removeClass('mejs-speed-selected');
|
||||
player.speedButton.find('input[type=radio]:checked').parent().addClass('mejs-speed-selected');
|
||||
|
||||
//
|
||||
// INSTRUCTURE CUSTOMIZATION - IE fixes
|
||||
//
|
||||
if (t.isIE()) {
|
||||
// After playback completes, IE will reset the rate to the
|
||||
// defaultPlaybackRate of 1.00 (with the UI still reflecting the
|
||||
// selected value) unless we set defaultPlaybackRate as well.
|
||||
media.defaultPlaybackRate = media.playbackRate;
|
||||
|
||||
// Internet Explorer fires a 'waiting' event in addition to the
|
||||
// 'ratechange' event when the playback speed is changed, even though
|
||||
// the HTML5 standard says not to. >_<
|
||||
//
|
||||
// Even worse, the 'waiting' state does not resolve with any other
|
||||
// event that would indicate that we are done waiting, like 'playing'
|
||||
// or 'seeked', so we are left with nothing to hook on but ye olde
|
||||
// arbitrary point in the future.
|
||||
$(media).one('waiting', function() {
|
||||
setTimeout(function() {
|
||||
layers.find('.mejs-overlay-loading').parent().hide();
|
||||
controls.find('.mejs-time-buffering').hide();
|
||||
}, 500);
|
||||
if (!defaultInArray) {
|
||||
speeds.push({
|
||||
name: t.options.defaultSpeed + t.options.speedChar,
|
||||
value: t.options.defaultSpeed
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
ss = player.speedButton.find('.mejs-speed-selector');
|
||||
ss.height(this.speedButton.find('.mejs-speed-selector ul').outerHeight(true) + player.speedButton.find('.mejs-speed-translations').outerHeight(true));
|
||||
ss.css('top', (-1 * ss.height()) + 'px');
|
||||
speeds.sort(function(a, b) {
|
||||
return parseFloat(b.value) - parseFloat(a.value);
|
||||
});
|
||||
|
||||
var getSpeedNameFromValue = function(value) {
|
||||
for(i=0,len=speeds.length; i <len; i++) {
|
||||
if (speeds[i].value === value) {
|
||||
return speeds[i].name;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var speedLabel = function(speed) {
|
||||
return mejs.i18n.t(t.options.speedLabel + ': Current speed ' + getSpeedNameFromValue(speed));
|
||||
}
|
||||
|
||||
var html = '<div class="mejs-button mejs-speed-button">' +
|
||||
'<button role="button" aria-haspopup="true" aria-controls="' + t.id + '" type="button" aria-label="' + speedLabel(t.options.defaultSpeed) + '" aria-live="assertive">' + getSpeedNameFromValue(t.options.defaultSpeed) + '</button>' +
|
||||
'<div class="mejs-speed-selector mejs-offscreen" role="menu" aria-expanded="false" aria-hidden="true">' +
|
||||
'<ul>';
|
||||
|
||||
for (i = 0, il = speeds.length; i<il; i++) {
|
||||
inputId = t.id + '-speed-' + speeds[i].value;
|
||||
isCurrent = (speeds[i].value === t.options.defaultSpeed);
|
||||
html += '<li>' +
|
||||
'<input type="radio" name="speed" role="menuitemradio"' +
|
||||
'value="' + speeds[i].value + '" ' +
|
||||
'id="' + inputId + '" ' +
|
||||
(isCurrent ? ' checked="checked"' : '') +
|
||||
' aria-selected="' + isCurrent + '"' +
|
||||
' aria-label="' + getSpeedNameFromValue(speeds[i].value) + '"' +
|
||||
' />' +
|
||||
'<label for="' + inputId + '" ' + 'aria-hidden="true"' +
|
||||
(isCurrent ? ' class="mejs-speed-selected"' : '') +
|
||||
'>' + speeds[i].name + '</label>' +
|
||||
'</li>';
|
||||
}
|
||||
html += '</ul></div></div>';
|
||||
|
||||
player.speedButton = speedButton = $(html).appendTo(controls);
|
||||
speedSelector = speedButton.find('.mejs-speed-selector');
|
||||
|
||||
playbackSpeed = t.options.defaultSpeed;
|
||||
|
||||
media.addEventListener('loadedmetadata', function(e) {
|
||||
if (playbackSpeed) {
|
||||
media.playbackRate = parseFloat(playbackSpeed);
|
||||
}
|
||||
}, true);
|
||||
|
||||
speedSelector
|
||||
.on('click', 'input[type="radio"]', function() {
|
||||
// set aria states
|
||||
$(this).attr('aria-selected', true).attr('checked', 'checked');
|
||||
$(this).closest('.mejs-speed-selector').find('input[type=radio]').not(this).attr('aria-selected', 'false').removeAttr('checked');
|
||||
|
||||
var newSpeed = $(this).attr('value');
|
||||
playbackSpeed = newSpeed;
|
||||
media.playbackRate = parseFloat(newSpeed);
|
||||
speedButton.find('button')
|
||||
.html(getSpeedNameFromValue(newSpeed))
|
||||
.attr('aria-label', speedLabel(newSpeed));
|
||||
speedButton.find('.mejs-speed-selected').removeClass('mejs-speed-selected');
|
||||
speedButton.find('input[type="radio"]:checked').next().addClass('mejs-speed-selected');
|
||||
});
|
||||
speedButton
|
||||
// set size on demand
|
||||
.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');
|
||||
})
|
||||
|
||||
// hover
|
||||
.hover(function() {
|
||||
clearTimeout(hoverTimeout);
|
||||
player.showSpeedSelector();
|
||||
}, function() {
|
||||
hoverTimeout = setTimeout(function () {
|
||||
player.hideSpeedSelector();
|
||||
}, t.options.menuTimeoutMouseLeave);
|
||||
})
|
||||
|
||||
// keyboard menu activation
|
||||
.on('keydown', function (e) {
|
||||
var keyCode = e.keyCode;
|
||||
|
||||
switch (keyCode) {
|
||||
case 32: // space
|
||||
if (!mejs.MediaFeatures.isFirefox) { // space sends the click event in Firefox
|
||||
player.showSpeedSelector();
|
||||
}
|
||||
$(this).find('.mejs-speed-selector')
|
||||
.find('input[type=radio]:checked').first().focus();
|
||||
break;
|
||||
case 13: // enter
|
||||
player.showSpeedSelector();
|
||||
$(this).find('.mejs-speed-selector')
|
||||
.find('input[type=radio]:checked').first().focus();
|
||||
break;
|
||||
case 27: // esc
|
||||
player.hideSpeedSelector();
|
||||
$(this).find('button').focus();
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
})
|
||||
|
||||
// close menu when tabbing away
|
||||
.on('focusout', mejs.Utility.debounce(function (e) { // Safari triggers focusout multiple times
|
||||
// Firefox does NOT support e.relatedTarget to see which element
|
||||
// just lost focus, so wait to find the next focused element
|
||||
setTimeout(function () {
|
||||
var parent = $(document.activeElement).closest('.mejs-speed-selector');
|
||||
if (!parent.length) {
|
||||
// focus is outside the control; close menu
|
||||
player.hideSpeedSelector();
|
||||
}
|
||||
}, 0);
|
||||
}, 100))
|
||||
|
||||
// Handle click so that screen readers can toggle the menu
|
||||
.on('click', 'button', function (e) {
|
||||
if ($(this).siblings('.mejs-speed-selector').hasClass('mejs-offscreen')) {
|
||||
player.showSpeedSelector();
|
||||
$(this).siblings('.mejs-speed-selector').find('input[type=radio]:checked').first().focus();
|
||||
} else {
|
||||
player.hideSpeedSelector();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
hideSpeedSelector: function () {
|
||||
this.speedButton.find('.mejs-speed-selector')
|
||||
.addClass('mejs-offscreen')
|
||||
.attr('aria-expanded', 'false')
|
||||
.attr('aria-hidden', 'true')
|
||||
.find('input[type=radio]') // make radios not focusable
|
||||
.attr('tabindex', '-1');
|
||||
},
|
||||
|
||||
showSpeedSelector: function () {
|
||||
this.speedButton.find('.mejs-speed-selector')
|
||||
.removeClass('mejs-offscreen')
|
||||
.attr('aria-expanded', 'true')
|
||||
.attr('aria-hidden', 'false')
|
||||
.find('input[type=radio]')
|
||||
.attr('tabindex', '0');
|
||||
}
|
||||
});
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue