Removes empty bindHandlers function from video_progress_slider
Refactors trigger function. Fixes Ui bugs: captions. Fixes bug with Youtube on page load. Hotfixes html5 video streaming bug. Adds test video files to the project.
This commit is contained in:
committed by
Alexander Kryklia
parent
aefb1aa9c8
commit
a77c386f76
@@ -12,6 +12,16 @@ is enabled.
|
||||
Studio: Added improvements to Course Creation: richer error messaging, tip
|
||||
text, and fourth field for course run.
|
||||
|
||||
Blades: New features for VideoAlpha player:
|
||||
1.) Controls are auto hidden after a delay of mouse inactivity - the full video
|
||||
becomes visible.
|
||||
2.) When captions (CC) button is pressed, captions stick (not auto hidden after
|
||||
a delay of mouse inactivity). The video player size does not change - the video
|
||||
is down-sized and placed in the middle of the black area.
|
||||
3.) All source code of Video Alpha 2 is written in JavaScript. It is not a basic
|
||||
conversion from CoffeeScript. The structure of the player has been changed.
|
||||
4.) A lot of additional unit tests.
|
||||
|
||||
LMS: Added user preferences (arbitrary user/key/value tuples, for which
|
||||
which user/key is unique) and a REST API for reading users and
|
||||
preferences. Access to the REST API is restricted by use of the
|
||||
|
||||
4
common/lib/xmodule/.gitignore
vendored
4
common/lib/xmodule/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
test.mp4
|
||||
test.ogv
|
||||
test.webm
|
||||
jasmine_test_runner.html
|
||||
BIN
common/lib/xmodule/test_files/test.mp4
Normal file
BIN
common/lib/xmodule/test_files/test.mp4
Normal file
Binary file not shown.
BIN
common/lib/xmodule/test_files/test.ogv
Normal file
BIN
common/lib/xmodule/test_files/test.ogv
Normal file
Binary file not shown.
BIN
common/lib/xmodule/test_files/test.webm
Normal file
BIN
common/lib/xmodule/test_files/test.webm
Normal file
Binary file not shown.
@@ -12,6 +12,7 @@ div.videoalpha {
|
||||
|
||||
div.tc-wrapper {
|
||||
position: relative;
|
||||
@include clearfix;
|
||||
}
|
||||
|
||||
article.video-wrapper {
|
||||
@@ -82,7 +83,7 @@ div.videoalpha {
|
||||
-moz-transition: -moz-transform 0.7s ease-in-out;
|
||||
-ms-transition: -ms-transform 0.7s ease-in-out;
|
||||
transition: transform 0.7s ease-in-out;
|
||||
@include transform(scaleY(0.5) translateY(50%));
|
||||
@include transform(scaleY(0.5) translate3d(0, 50%, 0));
|
||||
|
||||
div.ui-widget-header {
|
||||
background: #777;
|
||||
@@ -244,7 +245,7 @@ div.videoalpha {
|
||||
|
||||
// fix for now
|
||||
ol.video_speeds {
|
||||
box-shadow: inset 1px 0 0 #555, 0 3px 0 #444;
|
||||
box-shadow: inset 1px 0 0 #555, 0 4px 0 #444;
|
||||
@include transition(none);
|
||||
background-color: #444;
|
||||
border: 1px solid #000;
|
||||
@@ -252,7 +253,7 @@ div.videoalpha {
|
||||
display: none;
|
||||
opacity: 0.0;
|
||||
position: absolute;
|
||||
width: 133px;
|
||||
width: 131px;
|
||||
z-index: 10;
|
||||
|
||||
li {
|
||||
@@ -454,7 +455,7 @@ div.videoalpha {
|
||||
}
|
||||
|
||||
div.slider {
|
||||
@include transform(scaleY(1) translateY(0));
|
||||
@include transform(scaleY(1) translate3d(0, 0, 0));
|
||||
|
||||
a.ui-slider-handle {
|
||||
@include transform(scale(1) translate3d(-50%, -15%, 0));
|
||||
@@ -509,6 +510,7 @@ div.videoalpha {
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
article.video-wrapper section.video-controls div.secondary-controls a.hide-subtitles {
|
||||
@@ -528,11 +530,14 @@ div.videoalpha {
|
||||
|
||||
ol.subtitles.html5 {
|
||||
background-color: rgba(243, 243, 243, 0.8);
|
||||
height: 380px;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
width: 275px;
|
||||
margin-top: 20px;
|
||||
padding: 0 20px;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
data-end=""
|
||||
data-caption-asset-path="/static/subs/"
|
||||
data-sub="test_name_of_the_subtitles"
|
||||
data-mp4-source="test.mp4"
|
||||
data-webm-source="test.webm"
|
||||
data-ogg-source="test.ogv"
|
||||
data-mp4-source="test_files/test.mp4"
|
||||
data-webm-source="test_files/test.webm"
|
||||
data-ogg-source="test_files/test.ogv"
|
||||
data-autoplay="False"
|
||||
>
|
||||
<div class="tc-wrapper">
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
data-end=""
|
||||
data-caption-asset-path="/static/subs/"
|
||||
data-sub="test_name_of_the_subtitles"
|
||||
data-mp4-source="test.mp4"
|
||||
data-webm-source="test.webm"
|
||||
data-ogg-source="test.ogv"
|
||||
data-mp4-source="test_files/test.mp4"
|
||||
data-webm-source="test_files/test.webm"
|
||||
data-ogg-source="test_files/test.ogv"
|
||||
data-autoplay="False"
|
||||
>
|
||||
<div class="tc-wrapper">
|
||||
|
||||
@@ -117,9 +117,9 @@
|
||||
|
||||
it('parse Html5 sources', function () {
|
||||
var html5Sources = {
|
||||
mp4: 'test.mp4',
|
||||
webm: 'test.webm',
|
||||
ogg: 'test.ogv'
|
||||
mp4: 'test_files/test.mp4',
|
||||
webm: 'test_files/test.webm',
|
||||
ogg: 'test_files/test.ogv'
|
||||
};
|
||||
|
||||
expect(state.html5Sources).toEqual(html5Sources);
|
||||
|
||||
@@ -134,8 +134,8 @@ function (VideoPlayer) {
|
||||
) {
|
||||
VideoPlayer(state);
|
||||
} else {
|
||||
onPlayerReadyFunc = (this.videoType === 'youtube') ? 'onYouTubePlayerAPIReady' : 'onHTML5PlayerAPIReady';
|
||||
window[onPlayerReadyFunc] = _.bind(window.VideoPlayer, state);
|
||||
onPlayerReadyFunc = (state.videoType === 'youtube') ? 'onYouTubePlayerAPIReady' : 'onHTML5PlayerAPIReady';
|
||||
window[onPlayerReadyFunc] = _.bind(VideoPlayer, window, state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,70 +341,30 @@ function (VideoPlayer) {
|
||||
return this.metadata[this.youtubeId()].duration;
|
||||
}
|
||||
|
||||
/* The function .trigger() expects the parameter @callType one of
|
||||
*
|
||||
* 'event'
|
||||
* 'method'
|
||||
*
|
||||
* The default value (if @callType and @eventName are not specified) is 'method'. Based on this parameter, this
|
||||
* function can be used in two ways.
|
||||
*
|
||||
*
|
||||
*
|
||||
* First use: A safe way to trigger jQuery events.
|
||||
* -----------------------------------------------
|
||||
*
|
||||
* @callType === 'event'
|
||||
*
|
||||
* Because jQuery events can be triggered on some jQuery object, we must make sure that
|
||||
* we don't trigger an event on an undefined object. For this we will have an in-between
|
||||
* method that will check for the existance of an object before triggering an event on it.
|
||||
*
|
||||
* @objChain is an array that contains the chain of properties on the 'state' object. For
|
||||
* example, if
|
||||
*
|
||||
* objChain = ['videoPlayer', 'stopVideo'];
|
||||
*
|
||||
* then we will check for the existance of the
|
||||
*
|
||||
* state.videoPlayer.stopVideo
|
||||
*
|
||||
* object, and, if found to be present, will trigger the specified event on this object.
|
||||
*
|
||||
* @eventName is a string the name of the event to trigger on the specified object.
|
||||
*
|
||||
* @extraParameters is an object or an array that should be passed to the triggered method.
|
||||
*
|
||||
*
|
||||
* Second use: A safe way to call methods.
|
||||
* ---------------------------------------
|
||||
*
|
||||
* @callType === 'method'
|
||||
*
|
||||
* Parameter @eventName is NOT necessary.
|
||||
*
|
||||
/*
|
||||
* The trigger() function will assume that the @objChain is a complete chain with a method
|
||||
* (function) at the end. It will call this function. So for example, when trigger() is
|
||||
* called like so:
|
||||
*
|
||||
* state.trigger(['videoPlayer', 'pause'], {'param1': 10}, 'method');
|
||||
* state.trigger('videoPlayer.pause', {'param1': 10});
|
||||
*
|
||||
* Then trigger() will execute:
|
||||
*
|
||||
* state.videoPlayer.pause({'param1': 10});
|
||||
*/
|
||||
function trigger(objChain, extraParameters, callType, eventName) {
|
||||
var i, tmpObj;
|
||||
function trigger(objChain, extraParameters) {
|
||||
var i, tmpObj, chain;
|
||||
|
||||
// Remember that 'this' is the 'state' object.
|
||||
tmpObj = this;
|
||||
chain = objChain.split('.');
|
||||
|
||||
// At the end of the loop the variable 'tmpObj' will either be the correct
|
||||
// object/function to trigger/invoke. If the 'objChain' chain of object is
|
||||
// object/function to trigger/invoke. If the 'chain' chain of object is
|
||||
// incorrect (one of the link is non-existent), then the loop will immediately
|
||||
// exit.
|
||||
while (objChain.length) {
|
||||
i = objChain.shift();
|
||||
while (chain.length) {
|
||||
i = chain.shift();
|
||||
|
||||
if (tmpObj.hasOwnProperty(i)) {
|
||||
tmpObj = tmpObj[i];
|
||||
@@ -415,18 +375,7 @@ function (VideoPlayer) {
|
||||
}
|
||||
}
|
||||
|
||||
if ((typeof callType === 'undefined') && (typeof eventName === 'undefined')) {
|
||||
callType = 'method';
|
||||
}
|
||||
|
||||
// Based on the type, either trigger, or invoke.
|
||||
if (callType === 'event') {
|
||||
tmpObj.trigger(eventName, extraParameters);
|
||||
} else if (callType === 'method') {
|
||||
tmpObj(extraParameters);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
tmpObj(extraParameters);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -218,6 +218,7 @@ function () {
|
||||
// The player state is used by other parts of the VideoPlayer to detrermine what the video is
|
||||
// currently doing.
|
||||
this.video = this.videoEl[0];
|
||||
this.video.load();
|
||||
this.playerState = HTML5Video.PlayerState.UNSTARTED;
|
||||
// this.callStateChangeCallback();
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ function (HTML5Video) {
|
||||
}
|
||||
}
|
||||
|
||||
// Every 200 ms, if the video is playing, we call the function update, via
|
||||
// Every 200 ms, if the video is playing, we call the function update, via
|
||||
// clearInterval. This interval is called updateInterval.
|
||||
// It is created on a onPlay event. Cleared on a onPause event.
|
||||
// Reinitialized on a onSeek event.
|
||||
@@ -252,10 +252,10 @@ function (HTML5Video) {
|
||||
}
|
||||
|
||||
function onEnded() {
|
||||
this.trigger(['videoControl','pause'], null);
|
||||
this.trigger('videoControl.pause', null);
|
||||
|
||||
if (this.config.show_captions) {
|
||||
this.trigger(['videoCaption','pause'], null);
|
||||
this.trigger('videoCaption.pause', null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,10 +270,10 @@ function (HTML5Video) {
|
||||
clearInterval(this.videoPlayer.updateInterval);
|
||||
delete this.videoPlayer.updateInterval;
|
||||
|
||||
this.trigger(['videoControl','pause'], null);
|
||||
this.trigger('videoControl.pause', null);
|
||||
|
||||
if (this.config.show_captions) {
|
||||
this.trigger(['videoCaption','pause'], null);
|
||||
this.trigger('videoCaption.pause', null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,10 +289,10 @@ function (HTML5Video) {
|
||||
this.videoPlayer.updateInterval = setInterval(this.videoPlayer.update, 200);
|
||||
}
|
||||
|
||||
this.trigger(['videoControl','play'], null);
|
||||
this.trigger('videoControl.play', null);
|
||||
|
||||
if (this.config.show_captions) {
|
||||
this.trigger(['videoCaption','play'], null);
|
||||
this.trigger('videoCaption.play', null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ function (HTML5Video) {
|
||||
|
||||
quality = this.videoPlayer.player.getPlaybackQuality();
|
||||
|
||||
this.trigger(['videoQualityControl', 'onQualityChange'], quality);
|
||||
this.trigger('videoQualityControl.onQualityChange', quality);
|
||||
}
|
||||
|
||||
function onReady() {
|
||||
@@ -327,7 +327,7 @@ function (HTML5Video) {
|
||||
|
||||
baseSpeedSubs = this.videos['1.0'];
|
||||
_this = this;
|
||||
// this.videos is a dictionary containing various frame rates
|
||||
// this.videos is a dictionary containing various frame rates
|
||||
// and their associated subs.
|
||||
|
||||
// First clear the dictionary.
|
||||
@@ -342,7 +342,7 @@ function (HTML5Video) {
|
||||
_this.speeds.push(value.toFixed(2).replace(/\.00$/, '.0'));
|
||||
});
|
||||
|
||||
this.trigger(['videoSpeedControl', 'reRender'], {'newSpeeds': this.speeds, 'currentSpeed': this.speed});
|
||||
this.trigger('videoSpeedControl.reRender', {'newSpeeds': this.speeds, 'currentSpeed': this.speed});
|
||||
|
||||
this.setSpeed($.cookie('video_speed'));
|
||||
}
|
||||
@@ -379,9 +379,9 @@ function (HTML5Video) {
|
||||
|
||||
duration = this.videoPlayer.duration();
|
||||
|
||||
this.trigger(['videoProgressSlider', 'updatePlayTime'], {'time': time, 'duration': duration});
|
||||
this.trigger(['videoControl', 'updateVcrVidTime'], {'time': time, 'duration': duration});
|
||||
this.trigger(['videoCaption', 'updatePlayTime'], time);
|
||||
this.trigger('videoProgressSlider.updatePlayTime', {'time': time, 'duration': duration});
|
||||
this.trigger('videoControl.updateVcrVidTime', {'time': time, 'duration': duration});
|
||||
this.trigger('videoCaption.updatePlayTime', time);
|
||||
}
|
||||
|
||||
function isPlaying() {
|
||||
|
||||
@@ -146,9 +146,9 @@ function () {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.videoControl.isPlaying) {
|
||||
this.trigger(['videoPlayer', 'pause'], null);
|
||||
this.trigger('videoPlayer.pause', null);
|
||||
} else {
|
||||
this.trigger(['videoPlayer', 'play'], null);
|
||||
this.trigger('videoPlayer.play', null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ function () {
|
||||
this.videoControl.fullScreenEl.attr('title', 'Exit fullscreen');
|
||||
}
|
||||
|
||||
this.trigger(['videoCaption', 'resize'], null);
|
||||
this.trigger('videoCaption.resize', null);
|
||||
}
|
||||
|
||||
function exitFullScreen(event) {
|
||||
|
||||
@@ -90,7 +90,7 @@ function () {
|
||||
newQuality = 'hd720';
|
||||
}
|
||||
|
||||
this.trigger(['videoPlayer', 'handlePlaybackQualityChange'], newQuality);
|
||||
this.trigger('videoPlayer.handlePlaybackQualityChange', newQuality);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -19,7 +19,7 @@ function () {
|
||||
|
||||
makeFunctionsPublic(state);
|
||||
renderElements(state);
|
||||
bindHandlers(state);
|
||||
// No callbacks to DOM events (click, mousemove, etc.).
|
||||
};
|
||||
|
||||
// ***************************************************************
|
||||
@@ -54,13 +54,6 @@ function () {
|
||||
}
|
||||
}
|
||||
|
||||
// function bindHandlers(state)
|
||||
//
|
||||
// Bind any necessary function callbacks to DOM events (click, mousemove, etc.).
|
||||
function bindHandlers(state) {
|
||||
|
||||
}
|
||||
|
||||
function buildSlider(state) {
|
||||
state.videoProgressSlider.slider = state.videoProgressSlider.el.slider({
|
||||
range: 'min',
|
||||
@@ -100,7 +93,7 @@ function () {
|
||||
this.videoProgressSlider.frozen = true;
|
||||
this.videoProgressSlider.updateTooltip(ui.value);
|
||||
|
||||
this.trigger(['videoPlayer', 'onSlideSeek'], {'type': 'onSlideSeek', 'time': ui.value});
|
||||
this.trigger('videoPlayer.onSlideSeek', {'type': 'onSlideSeek', 'time': ui.value});
|
||||
}
|
||||
|
||||
function onChange(event, ui) {
|
||||
@@ -112,7 +105,7 @@ function () {
|
||||
|
||||
this.videoProgressSlider.frozen = true;
|
||||
|
||||
this.trigger(['videoPlayer', 'onSlideSeek'], {'type': 'onSlideSeek', 'time': ui.value});
|
||||
this.trigger('videoPlayer.onSlideSeek', {'type': 'onSlideSeek', 'time': ui.value});
|
||||
|
||||
setTimeout(function() {
|
||||
_this.videoProgressSlider.frozen = false;
|
||||
|
||||
@@ -94,7 +94,7 @@ function () {
|
||||
path: '/'
|
||||
});
|
||||
|
||||
this.trigger(['videoPlayer', 'onVolumeChange'], ui.value);
|
||||
this.trigger('videoPlayer.onVolumeChange', ui.value);
|
||||
}
|
||||
|
||||
function toggleMute(event) {
|
||||
|
||||
@@ -105,7 +105,7 @@ function () {
|
||||
parseFloat(this.videoSpeedControl.currentSpeed).toFixed(2).replace(/\.00$/, '.0')
|
||||
);
|
||||
|
||||
this.trigger(['videoPlayer', 'onSpeedChange'], this.videoSpeedControl.currentSpeed);
|
||||
this.trigger('videoPlayer.onSpeedChange', this.videoSpeedControl.currentSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ function () {
|
||||
this.el.find('.video-controls .secondary-controls').append(this.videoCaption.hideSubtitlesEl);
|
||||
|
||||
this.el.find('.subtitles').css({
|
||||
maxHeight: this.el.find('.video-wrapper').height() - 5
|
||||
maxHeight: this.el.find('.video-wrapper').height()
|
||||
});
|
||||
|
||||
this.videoCaption.fetchCaption();
|
||||
@@ -329,7 +329,7 @@ function () {
|
||||
event.preventDefault();
|
||||
time = Math.round(Time.convert($(event.target).data('start'), '1.0', this.speed) / 1000);
|
||||
|
||||
this.trigger(['videoPlayer', 'onCaptionSeek'], {'type': 'onCaptionSeek', 'time': time});
|
||||
this.trigger('videoPlayer.onCaptionSeek', {'type': 'onCaptionSeek', 'time': time});
|
||||
}
|
||||
|
||||
function calculateOffset(element) {
|
||||
|
||||
Reference in New Issue
Block a user