Commit a77c386f by jmclaus Committed by Alexander Kryklia

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.
parent aefb1aa9
...@@ -12,6 +12,16 @@ is enabled. ...@@ -12,6 +12,16 @@ is enabled.
Studio: Added improvements to Course Creation: richer error messaging, tip Studio: Added improvements to Course Creation: richer error messaging, tip
text, and fourth field for course run. 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 LMS: Added user preferences (arbitrary user/key/value tuples, for which
which user/key is unique) and a REST API for reading users and 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 preferences. Access to the REST API is restricted by use of the
......
test.mp4
test.ogv
test.webm
jasmine_test_runner.html
...@@ -12,6 +12,7 @@ div.videoalpha { ...@@ -12,6 +12,7 @@ div.videoalpha {
div.tc-wrapper { div.tc-wrapper {
position: relative; position: relative;
@include clearfix;
} }
article.video-wrapper { article.video-wrapper {
...@@ -82,7 +83,7 @@ div.videoalpha { ...@@ -82,7 +83,7 @@ div.videoalpha {
-moz-transition: -moz-transform 0.7s ease-in-out; -moz-transition: -moz-transform 0.7s ease-in-out;
-ms-transition: -ms-transform 0.7s ease-in-out; -ms-transition: -ms-transform 0.7s ease-in-out;
transition: 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 { div.ui-widget-header {
background: #777; background: #777;
...@@ -244,7 +245,7 @@ div.videoalpha { ...@@ -244,7 +245,7 @@ div.videoalpha {
// fix for now // fix for now
ol.video_speeds { 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); @include transition(none);
background-color: #444; background-color: #444;
border: 1px solid #000; border: 1px solid #000;
...@@ -252,7 +253,7 @@ div.videoalpha { ...@@ -252,7 +253,7 @@ div.videoalpha {
display: none; display: none;
opacity: 0.0; opacity: 0.0;
position: absolute; position: absolute;
width: 133px; width: 131px;
z-index: 10; z-index: 10;
li { li {
...@@ -454,7 +455,7 @@ div.videoalpha { ...@@ -454,7 +455,7 @@ div.videoalpha {
} }
div.slider { div.slider {
@include transform(scaleY(1) translateY(0)); @include transform(scaleY(1) translate3d(0, 0, 0));
a.ui-slider-handle { a.ui-slider-handle {
@include transform(scale(1) translate3d(-50%, -15%, 0)); @include transform(scale(1) translate3d(-50%, -15%, 0));
...@@ -509,6 +510,7 @@ div.videoalpha { ...@@ -509,6 +510,7 @@ div.videoalpha {
left: 0px; left: 0px;
right: 0px; right: 0px;
position: absolute; position: absolute;
z-index: 1;
} }
article.video-wrapper section.video-controls div.secondary-controls a.hide-subtitles { article.video-wrapper section.video-controls div.secondary-controls a.hide-subtitles {
...@@ -528,11 +530,14 @@ div.videoalpha { ...@@ -528,11 +530,14 @@ div.videoalpha {
ol.subtitles.html5 { ol.subtitles.html5 {
background-color: rgba(243, 243, 243, 0.8); background-color: rgba(243, 243, 243, 0.8);
height: 380px; height: 100%;
position: absolute; position: absolute;
right: 0; right: 0;
bottom: 0;
top: 0;
width: 275px; width: 275px;
margin-top: 20px; padding: 0 20px;
z-index: 0;
} }
} }
......
...@@ -9,9 +9,9 @@ ...@@ -9,9 +9,9 @@
data-end="" data-end=""
data-caption-asset-path="/static/subs/" data-caption-asset-path="/static/subs/"
data-sub="test_name_of_the_subtitles" data-sub="test_name_of_the_subtitles"
data-mp4-source="test.mp4" data-mp4-source="test_files/test.mp4"
data-webm-source="test.webm" data-webm-source="test_files/test.webm"
data-ogg-source="test.ogv" data-ogg-source="test_files/test.ogv"
data-autoplay="False" data-autoplay="False"
> >
<div class="tc-wrapper"> <div class="tc-wrapper">
......
...@@ -9,9 +9,9 @@ ...@@ -9,9 +9,9 @@
data-end="" data-end=""
data-caption-asset-path="/static/subs/" data-caption-asset-path="/static/subs/"
data-sub="test_name_of_the_subtitles" data-sub="test_name_of_the_subtitles"
data-mp4-source="test.mp4" data-mp4-source="test_files/test.mp4"
data-webm-source="test.webm" data-webm-source="test_files/test.webm"
data-ogg-source="test.ogv" data-ogg-source="test_files/test.ogv"
data-autoplay="False" data-autoplay="False"
> >
<div class="tc-wrapper"> <div class="tc-wrapper">
......
...@@ -117,9 +117,9 @@ ...@@ -117,9 +117,9 @@
it('parse Html5 sources', function () { it('parse Html5 sources', function () {
var html5Sources = { var html5Sources = {
mp4: 'test.mp4', mp4: 'test_files/test.mp4',
webm: 'test.webm', webm: 'test_files/test.webm',
ogg: 'test.ogv' ogg: 'test_files/test.ogv'
}; };
expect(state.html5Sources).toEqual(html5Sources); expect(state.html5Sources).toEqual(html5Sources);
......
...@@ -134,8 +134,8 @@ function (VideoPlayer) { ...@@ -134,8 +134,8 @@ function (VideoPlayer) {
) { ) {
VideoPlayer(state); VideoPlayer(state);
} else { } else {
onPlayerReadyFunc = (this.videoType === 'youtube') ? 'onYouTubePlayerAPIReady' : 'onHTML5PlayerAPIReady'; onPlayerReadyFunc = (state.videoType === 'youtube') ? 'onYouTubePlayerAPIReady' : 'onHTML5PlayerAPIReady';
window[onPlayerReadyFunc] = _.bind(window.VideoPlayer, state); window[onPlayerReadyFunc] = _.bind(VideoPlayer, window, state);
} }
} }
...@@ -341,70 +341,30 @@ function (VideoPlayer) { ...@@ -341,70 +341,30 @@ function (VideoPlayer) {
return this.metadata[this.youtubeId()].duration; 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 * 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 * (function) at the end. It will call this function. So for example, when trigger() is
* called like so: * called like so:
* *
* state.trigger(['videoPlayer', 'pause'], {'param1': 10}, 'method'); * state.trigger('videoPlayer.pause', {'param1': 10});
* *
* Then trigger() will execute: * Then trigger() will execute:
* *
* state.videoPlayer.pause({'param1': 10}); * state.videoPlayer.pause({'param1': 10});
*/ */
function trigger(objChain, extraParameters, callType, eventName) { function trigger(objChain, extraParameters) {
var i, tmpObj; var i, tmpObj, chain;
// Remember that 'this' is the 'state' object. // Remember that 'this' is the 'state' object.
tmpObj = this; tmpObj = this;
chain = objChain.split('.');
// At the end of the loop the variable 'tmpObj' will either be the correct // 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 // incorrect (one of the link is non-existent), then the loop will immediately
// exit. // exit.
while (objChain.length) { while (chain.length) {
i = objChain.shift(); i = chain.shift();
if (tmpObj.hasOwnProperty(i)) { if (tmpObj.hasOwnProperty(i)) {
tmpObj = tmpObj[i]; tmpObj = tmpObj[i];
...@@ -415,18 +375,7 @@ function (VideoPlayer) { ...@@ -415,18 +375,7 @@ function (VideoPlayer) {
} }
} }
if ((typeof callType === 'undefined') && (typeof eventName === 'undefined')) { tmpObj(extraParameters);
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;
}
return true; return true;
} }
......
...@@ -218,6 +218,7 @@ function () { ...@@ -218,6 +218,7 @@ function () {
// The player state is used by other parts of the VideoPlayer to detrermine what the video is // The player state is used by other parts of the VideoPlayer to detrermine what the video is
// currently doing. // currently doing.
this.video = this.videoEl[0]; this.video = this.videoEl[0];
this.video.load();
this.playerState = HTML5Video.PlayerState.UNSTARTED; this.playerState = HTML5Video.PlayerState.UNSTARTED;
// this.callStateChangeCallback(); // this.callStateChangeCallback();
......
...@@ -225,7 +225,7 @@ function (HTML5Video) { ...@@ -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. // clearInterval. This interval is called updateInterval.
// It is created on a onPlay event. Cleared on a onPause event. // It is created on a onPlay event. Cleared on a onPause event.
// Reinitialized on a onSeek event. // Reinitialized on a onSeek event.
...@@ -252,10 +252,10 @@ function (HTML5Video) { ...@@ -252,10 +252,10 @@ function (HTML5Video) {
} }
function onEnded() { function onEnded() {
this.trigger(['videoControl','pause'], null); this.trigger('videoControl.pause', null);
if (this.config.show_captions) { if (this.config.show_captions) {
this.trigger(['videoCaption','pause'], null); this.trigger('videoCaption.pause', null);
} }
} }
...@@ -270,10 +270,10 @@ function (HTML5Video) { ...@@ -270,10 +270,10 @@ function (HTML5Video) {
clearInterval(this.videoPlayer.updateInterval); clearInterval(this.videoPlayer.updateInterval);
delete this.videoPlayer.updateInterval; delete this.videoPlayer.updateInterval;
this.trigger(['videoControl','pause'], null); this.trigger('videoControl.pause', null);
if (this.config.show_captions) { if (this.config.show_captions) {
this.trigger(['videoCaption','pause'], null); this.trigger('videoCaption.pause', null);
} }
} }
...@@ -289,10 +289,10 @@ function (HTML5Video) { ...@@ -289,10 +289,10 @@ function (HTML5Video) {
this.videoPlayer.updateInterval = setInterval(this.videoPlayer.update, 200); this.videoPlayer.updateInterval = setInterval(this.videoPlayer.update, 200);
} }
this.trigger(['videoControl','play'], null); this.trigger('videoControl.play', null);
if (this.config.show_captions) { if (this.config.show_captions) {
this.trigger(['videoCaption','play'], null); this.trigger('videoCaption.play', null);
} }
} }
...@@ -307,7 +307,7 @@ function (HTML5Video) { ...@@ -307,7 +307,7 @@ function (HTML5Video) {
quality = this.videoPlayer.player.getPlaybackQuality(); quality = this.videoPlayer.player.getPlaybackQuality();
this.trigger(['videoQualityControl', 'onQualityChange'], quality); this.trigger('videoQualityControl.onQualityChange', quality);
} }
function onReady() { function onReady() {
...@@ -327,7 +327,7 @@ function (HTML5Video) { ...@@ -327,7 +327,7 @@ function (HTML5Video) {
baseSpeedSubs = this.videos['1.0']; baseSpeedSubs = this.videos['1.0'];
_this = this; _this = this;
// this.videos is a dictionary containing various frame rates // this.videos is a dictionary containing various frame rates
// and their associated subs. // and their associated subs.
// First clear the dictionary. // First clear the dictionary.
...@@ -342,7 +342,7 @@ function (HTML5Video) { ...@@ -342,7 +342,7 @@ function (HTML5Video) {
_this.speeds.push(value.toFixed(2).replace(/\.00$/, '.0')); _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')); this.setSpeed($.cookie('video_speed'));
} }
...@@ -379,9 +379,9 @@ function (HTML5Video) { ...@@ -379,9 +379,9 @@ function (HTML5Video) {
duration = this.videoPlayer.duration(); duration = this.videoPlayer.duration();
this.trigger(['videoProgressSlider', 'updatePlayTime'], {'time': time, 'duration': duration}); this.trigger('videoProgressSlider.updatePlayTime', {'time': time, 'duration': duration});
this.trigger(['videoControl', 'updateVcrVidTime'], {'time': time, 'duration': duration}); this.trigger('videoControl.updateVcrVidTime', {'time': time, 'duration': duration});
this.trigger(['videoCaption', 'updatePlayTime'], time); this.trigger('videoCaption.updatePlayTime', time);
} }
function isPlaying() { function isPlaying() {
......
...@@ -146,9 +146,9 @@ function () { ...@@ -146,9 +146,9 @@ function () {
event.preventDefault(); event.preventDefault();
if (this.videoControl.isPlaying) { if (this.videoControl.isPlaying) {
this.trigger(['videoPlayer', 'pause'], null); this.trigger('videoPlayer.pause', null);
} else { } else {
this.trigger(['videoPlayer', 'play'], null); this.trigger('videoPlayer.play', null);
} }
} }
...@@ -167,7 +167,7 @@ function () { ...@@ -167,7 +167,7 @@ function () {
this.videoControl.fullScreenEl.attr('title', 'Exit fullscreen'); this.videoControl.fullScreenEl.attr('title', 'Exit fullscreen');
} }
this.trigger(['videoCaption', 'resize'], null); this.trigger('videoCaption.resize', null);
} }
function exitFullScreen(event) { function exitFullScreen(event) {
......
...@@ -90,7 +90,7 @@ function () { ...@@ -90,7 +90,7 @@ function () {
newQuality = 'hd720'; newQuality = 'hd720';
} }
this.trigger(['videoPlayer', 'handlePlaybackQualityChange'], newQuality); this.trigger('videoPlayer.handlePlaybackQualityChange', newQuality);
} }
}); });
......
...@@ -19,7 +19,7 @@ function () { ...@@ -19,7 +19,7 @@ function () {
makeFunctionsPublic(state); makeFunctionsPublic(state);
renderElements(state); renderElements(state);
bindHandlers(state); // No callbacks to DOM events (click, mousemove, etc.).
}; };
// *************************************************************** // ***************************************************************
...@@ -54,13 +54,6 @@ function () { ...@@ -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) { function buildSlider(state) {
state.videoProgressSlider.slider = state.videoProgressSlider.el.slider({ state.videoProgressSlider.slider = state.videoProgressSlider.el.slider({
range: 'min', range: 'min',
...@@ -100,7 +93,7 @@ function () { ...@@ -100,7 +93,7 @@ function () {
this.videoProgressSlider.frozen = true; this.videoProgressSlider.frozen = true;
this.videoProgressSlider.updateTooltip(ui.value); 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) { function onChange(event, ui) {
...@@ -112,7 +105,7 @@ function () { ...@@ -112,7 +105,7 @@ function () {
this.videoProgressSlider.frozen = true; this.videoProgressSlider.frozen = true;
this.trigger(['videoPlayer', 'onSlideSeek'], {'type': 'onSlideSeek', 'time': ui.value}); this.trigger('videoPlayer.onSlideSeek', {'type': 'onSlideSeek', 'time': ui.value});
setTimeout(function() { setTimeout(function() {
_this.videoProgressSlider.frozen = false; _this.videoProgressSlider.frozen = false;
......
...@@ -94,7 +94,7 @@ function () { ...@@ -94,7 +94,7 @@ function () {
path: '/' path: '/'
}); });
this.trigger(['videoPlayer', 'onVolumeChange'], ui.value); this.trigger('videoPlayer.onVolumeChange', ui.value);
} }
function toggleMute(event) { function toggleMute(event) {
......
...@@ -105,7 +105,7 @@ function () { ...@@ -105,7 +105,7 @@ function () {
parseFloat(this.videoSpeedControl.currentSpeed).toFixed(2).replace(/\.00$/, '.0') 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 () { ...@@ -65,7 +65,7 @@ function () {
this.el.find('.video-controls .secondary-controls').append(this.videoCaption.hideSubtitlesEl); this.el.find('.video-controls .secondary-controls').append(this.videoCaption.hideSubtitlesEl);
this.el.find('.subtitles').css({ this.el.find('.subtitles').css({
maxHeight: this.el.find('.video-wrapper').height() - 5 maxHeight: this.el.find('.video-wrapper').height()
}); });
this.videoCaption.fetchCaption(); this.videoCaption.fetchCaption();
...@@ -329,7 +329,7 @@ function () { ...@@ -329,7 +329,7 @@ function () {
event.preventDefault(); event.preventDefault();
time = Math.round(Time.convert($(event.target).data('start'), '1.0', this.speed) / 1000); 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) { function calculateOffset(element) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment