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.
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
......
test.mp4
test.ogv
test.webm
jasmine_test_runner.html
......@@ -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) {
......
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