Commit 0e24ff14 by Valera Rozuvan Committed by Vasyl Nakvasiuk

Added fade in/fade out for controls in HTML5 videos.

Making video in the center and not stretched when captions are shown.
Added auto hide/show for captions.
Removed old styling and debug info.
Added missing initalizations for captions auto show/hide.
Fixing bugs. Adding black backgorund to video for when it has not yet loaded. Making sure that the captions are not shown when in YouTube mode.
Added back missing styles for captions when in YouTube mode.
Code review 1 comments
Minor refactoring.
Documenting. Part 1.
parent 9d2eec07
......@@ -10,11 +10,29 @@ div.videoalpha {
padding: 12px;
border-radius: 5px;
div.tc-wrapper {
position: relative;
}
article.video-wrapper {
float: left;
margin-right: flex-gutter(9);
width: flex-grid(6, 9);
background-color: black;
position: relative;
div.video-player-pre {
height: 50px;
background-color: black;
}
div.video-player-post {
height: 50px;
background-color: black;
}
section.video-player {
height: 0;
overflow: hidden;
......@@ -31,15 +49,6 @@ div.videoalpha {
}
}
section.video-roof {
background-color: green;
z-index: 10000;
width: 540px;
height: 100px;
position: absolute;
top: 387px;
}
section.video-controls {
@include clearfix();
background: #333;
......@@ -396,7 +405,6 @@ div.videoalpha {
a.hide-subtitles {
background: url('../images/cc.png') center no-repeat;
color: #797979;
display: block;
float: left;
font-weight: 800;
......@@ -419,6 +427,10 @@ div.videoalpha {
&.off {
opacity: 0.7;
}
background-color: #444;
color: #fff;
text-decoration: none;
}
}
}
......@@ -480,11 +492,39 @@ div.videoalpha {
article.video-wrapper {
width: flex-grid(9,9);
background-color: inherit;
}
article.video-wrapper section.video-controls.html5 {
bottom: 0px;
left: 0px;
right: 0px;
position: absolute;
}
article.video-wrapper section.video-controls div.secondary-controls a.hide-subtitles {
background-color: inherit;
color: #797979;
text-decoration: inherit;
}
article.video-wrapper div.video-player-pre, article.video-wrapper div.video-player-post {
height: 0px;
}
ol.subtitles {
width: 0;
height: 0;
width: 0;
height: 0;
}
ol.subtitles.html5 {
background-color: rgba(243, 243, 243, 0.5);
height: 380px;
position: absolute;
right: 0;
width: 275px;
margin-top: 20px;
}
}
......@@ -545,7 +585,7 @@ div.videoalpha {
background: rgba(#000, .8);
bottom: 0;
height: 100%;
max-height: 100%;
max-height: 460px;
max-width: flex-grid(3);
padding: lh();
position: fixed;
......
(function (requirejs, require, define) {
// Bind module.
define(
'videoalpha/display/bind.js',
[],
function () {
// bind() function.
return function (fn, me) {
return function () {
return fn.apply(me, arguments);
};
};
});
}(RequireJS.requirejs, RequireJS.require, RequireJS.define));
// var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
/**
* @file HTML5 video player module. Provides methods to control the in-browser HTML5 video player.
*
* The goal was to write this module so that it closely resembles the YouTube API. The main reason
* for this is because initially the edX video player supported only YouTube videos. When HTML5
* support was added, for greater compatibility, and to reduce the amount of code that needed to
* be modified, it was decided to write a similar API as the one provided by YouTube.
*
* @external RequireJS
*
* @module HTML5Video
*/
(function (requirejs, require, define) {
// HTML5Video module.
define(
'videoalpha/display/html5_video.js',
[],
......@@ -11,7 +23,7 @@ function () {
HTML5Video.Player = (function () {
Player.prototype.callStateChangeCallback = function () {
if ($.isFunction(this.config.events.onStateChange) === true) {
if ($.isFunction(this.config.events.onStateChange)) {
this.config.events.onStateChange({
'data': this.playerState
});
......@@ -62,7 +74,7 @@ function () {
newSpeed = parseFloat(value);
if (isFinite(newSpeed) === true) {
if (isFinite(newSpeed)) {
this.video.playbackRate = value;
}
};
......@@ -76,10 +88,10 @@ function () {
/*
* Constructor function for HTML5 Video player.
*
* @el - A DOM element where the HTML5 player will be inserted (as returned by jQuery(selector) function),
* @param {String|Object} el A DOM element where the HTML5 player will be inserted (as returned by jQuery(selector) function),
* or a selector string which will be used to select an element. This is a required parameter.
*
* @config - An object whose properties will be used as configuration options for the HTML5 video
* @param config - An object whose properties will be used as configuration options for the HTML5 video
* player. This is an optional parameter. In the case if this parameter is missing, or some of the config
* object's properties are missing, defaults will be used. The available options (and their defaults) are as
* follows:
......@@ -130,14 +142,14 @@ function () {
}
// A simple test to see that the 'config' is a normal object.
if ($.isPlainObject(config) === true) {
if ($.isPlainObject(config)) {
this.config = config;
} else {
return;
}
// We should have at least one video source. Otherwise there is no point to continue.
if (config.hasOwnProperty('videoSources') === false) {
if (!config.videoSources) {
return;
}
......@@ -154,9 +166,8 @@ function () {
// Create HTML markup for individual sources of the HTML5 <video> element.
$.each(sourceStr, function (videoType, videoSource) {
if (
(_this.config.videoSources.hasOwnProperty(videoType) === true) &&
(typeof _this.config.videoSources[videoType] === 'string') &&
(_this.config.videoSources[videoType].length > 0)
(_this.config.videoSources[videoType]) &&
(_this.config.videoSources[videoType].length)
) {
sourceStr[videoType] =
'<source ' +
......@@ -174,14 +185,14 @@ function () {
// Determine the starting and ending time for the video.
this.start = 0;
this.end = null;
if (config.hasOwnProperty('playerVars') === true) {
if (config.playerVars) {
this.start = parseFloat(config.playerVars.start);
if ((isFinite(this.start) !== true) || (this.start < 0)) {
if ((!isFinite(this.start)) || (this.start < 0)) {
this.start = 0;
}
this.end = parseFloat(config.playerVars.end);
if ((isFinite(this.end) !== true) || (this.end < this.start)) {
if ((!isFinite(this.end)) || (this.end < this.start)) {
this.end = null;
}
}
......@@ -242,7 +253,7 @@ function () {
}
_this.video.currentTime = _this.start;
if ($.isFunction(_this.config.events.onReady) === true) {
if ($.isFunction(_this.config.events.onReady)) {
_this.config.events.onReady(null);
}
}, false);
......
/**
* @file Initialize module works with the JSON config, and sets up various settings, parameters,
* variables. After all setup actions are performed, it invokes the video player to play the
* specified video. This module must be invoked first. It provides several functions which do not
* fit in with other modules.
*
* @external VideoPlayer
*
* @module Initialize
*/
(function (requirejs, require, define) {
// Initialize module.
define(
'videoalpha/display/initialize.js',
[
'videoalpha/display/bind.js',
'videoalpha/display/video_player.js'
],
function (bind, VideoPlayer) {
['videoalpha/display/video_player.js'],
function (VideoPlayer) {
// Initialize() function - what this module "exports".
/**
* @function
*
* Initialize module exports this function.
*
* @param {Object} state A place for all properties, and methods of Video Alpha.
* @param {DOM element} element Container of the entire Video Alpha DOM element.
*/
return function (state, element) {
checkForNativeFunctions();
makeFunctionsPublic(state);
renderElements(state, element);
};
......@@ -19,15 +34,19 @@ function (bind, VideoPlayer) {
// Private functions start here.
// ***************************************************************
// function makeFunctionsPublic(state)
//
// Functions which will be accessible via 'state' object. When called, these functions will
// get the 'state' object as a context.
/**
* @function makeFunctionsPublic
*
* Functions which will be accessible via 'state' object. When called, these functions will get the 'state'
* object as a context.
*
* @param {Object} state A place for all properties, and methods of Video Alpha.
*/
function makeFunctionsPublic(state) {
state.setSpeed = bind(setSpeed, state);
state.youtubeId = bind(youtubeId, state);
state.getDuration = bind(getDuration, state);
state.trigger = bind(trigger, state);
state.setSpeed = setSpeed.bind(state);
state.youtubeId = youtubeId.bind(state);
state.getDuration = getDuration.bind(state);
state.trigger = trigger.bind(state);
}
// function renderElements(state)
......@@ -36,6 +55,8 @@ function (bind, VideoPlayer) {
// make the created DOM elements available via the 'state' object. Much easier to work this
// way - you don't have to do repeated jQuery element selects.
function renderElements(state, element) {
var onPlayerReadyFunc;
// The parent element of the video, and the ID.
state.el = $(element).find('.videoalpha');
state.id = state.el.attr('id').replace(/video_/, '');
......@@ -50,8 +71,7 @@ function (bind, VideoPlayer) {
'caption_data_dir': state.el.data('caption-data-dir'),
'caption_asset_path': state.el.data('caption-asset-path'),
'show_captions': (state.el.data('show-captions').toString() === 'true'),
'show_captions': (state.el.data('show-captions').toString().toLowerCase === 'true'),
'youtubeStreams': state.el.data('streams'),
'sub': state.el.data('sub'),
......@@ -61,7 +81,7 @@ function (bind, VideoPlayer) {
};
// Try to parse YouTube stream ID's. If
if (parseYoutubeStreams(state, state.config.youtubeStreams) === true) {
if (parseYoutubeStreams(state, state.config.youtubeStreams)) {
state.videoType = 'youtube';
fetchMetadata(state);
......@@ -74,12 +94,14 @@ function (bind, VideoPlayer) {
parseVideoSources(
state,
state.config.mp4Source,
state.config.webmSource,
state.config.oggSource
{
'mp4': state.config.mp4Source,
'webm': state.config.webmSource,
'ogg': state.config.oggSource
}
);
if ((typeof state.config.sub !== 'string') || (state.config.sub.length === 0)) {
if (!state.config.sub.length) {
state.config.sub = '';
state.config.show_captions = false;
}
......@@ -108,7 +130,7 @@ function (bind, VideoPlayer) {
// state.hide_captions = true | false
//
// represents the user's choice of having the subtitles shown or hidden. This choice is stored in cookies.
if (state.config.show_captions === true) {
if (state.config.show_captions) {
state.hide_captions = ($.cookie('hide_captions') === 'true');
} else {
state.hide_captions = true;
......@@ -141,6 +163,8 @@ function (bind, VideoPlayer) {
// Possible value are: 'visible', 'hiding', and 'invisible'.
state.controlState = 'visible';
state.controlHideTimeout = null;
state.captionState = 'visible';
state.captionHideTimeout = null;
// Launch embedding of actual video content, or set it up so that it will be done as soon as the
// appropriate video player (YouTube or stand alone HTML5) is loaded, and can handle embedding.
......@@ -152,17 +176,10 @@ function (bind, VideoPlayer) {
((state.videoType === 'youtube') && (window.YT) && (window.YT.Player)) ||
(state.videoType === 'html5')
) {
embed(state);
VideoPlayer(state);
} else {
if (state.videoType === 'youtube') {
window.onYouTubePlayerAPIReady = function() {
embed(state);
};
} else { // if (state.videoType === 'html5') {
window.onHTML5PlayerAPIReady = function() {
embed(state);
};
}
onPlayerReadyFunc = (state.videoType === 'youtube') ? 'onYouTubePlayerAPIReady' : 'onHTML5PlayerAPIReady';
window[onPlayerReadyFunc] = VideoPlayer.bind(window, state);
}
}
......@@ -176,8 +193,10 @@ function (bind, VideoPlayer) {
// @return
// false: We don't have YouTube video IDs to work with; most likely we have HTML5 video sources.
// true: Parsing of YouTube video IDs went OK, and we can proceed onwards to play YouTube videos.
function parseYoutubeStreams(state, youtubeStreams) {
if ((typeof youtubeStreams !== 'string') || (youtubeStreams.length === 0)) {
if (!youtubeStreams.length) {
return false;
}
......@@ -199,18 +218,14 @@ function (bind, VideoPlayer) {
//
// Take the HTML5 sources (URLs of videos), and make them available explictly for each type
// of video format (mp4, webm, ogg).
function parseVideoSources(state, mp4Source, webmSource, oggSource) {
function parseVideoSources(state, sources) {
state.html5Sources = { 'mp4': null, 'webm': null, 'ogg': null };
if ((typeof mp4Source === 'string') && (mp4Source.length > 0)) {
state.html5Sources.mp4 = mp4Source;
}
if ((typeof webmSource === 'string') && (webmSource.length > 0)) {
state.html5Sources.webm = webmSource;
}
if ((typeof oggSource === 'string') && (oggSource.length > 0)) {
state.html5Sources.ogg = oggSource;
}
$.each(sources, function (name, source) {
if (source.length) {
state.html5Sources[name] = source;
}
});
}
// function fetchMetadata(state)
......@@ -239,12 +254,38 @@ function (bind, VideoPlayer) {
state.setSpeed($.cookie('video_speed'));
}
// function embed(state)
//
// This function is called when the current type of video player API becomes available.
// It instantiates the core video module.
function embed(state) {
VideoPlayer(state);
function checkForNativeFunctions() {
// The bind function is a recent addition to ECMA-262, 5th edition; as such it may not be present in all
// browsers. You can partially work around this by inserting the following code at the beginning of your
// scripts, allowing use of much of the functionality of bind() in implementations that do not natively support
// it.
//
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
var aArgs, fToBind, fNOP, fBound;
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
aArgs = Array.prototype.slice.call(arguments, 1);
fToBind = this;
fNOP = function () {};
fBound = function () {
return fToBind.apply(
this instanceof fNOP && oThis ? this : oThis,
aArgs.concat(Array.prototype.slice.call(arguments))
);
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
}
// ***************************************************************
......@@ -260,7 +301,7 @@ function (bind, VideoPlayer) {
this.speed = '1.0';
}
if (updateCookie !== false) {
if (updateCookie) {
$.cookie('video_speed', this.speed, {
'expires': 3650,
'path': '/'
......@@ -281,7 +322,8 @@ function (bind, VideoPlayer) {
* 'event'
* 'method'
*
* Based on this parameter, this function can be used in two ways.
* The default value (if @callType and @eventName are not specified) is 'method'. Based on this parameter, this
* function can be used in two ways.
*
*
*
......@@ -337,10 +379,10 @@ function (bind, VideoPlayer) {
// object/function to trigger/invoke. If the 'objChain' chain of object is
// incorrect (one of the link is non-existent), then the loop will immediately
// exit.
while (objChain.length > 0) {
while (objChain.length) {
i = objChain.shift();
if (tmpObj.hasOwnProperty(i) === true) {
if (tmpObj.hasOwnProperty(i)) {
tmpObj = tmpObj[i];
} else {
// An incorrect object chain was specified.
......@@ -349,6 +391,10 @@ function (bind, 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);
......
......@@ -3,8 +3,8 @@
// VideoCaption module.
define(
'videoalpha/display/video_caption.js',
['videoalpha/display/bind.js'],
function (bind) {
[],
function () {
// VideoCaption() function - what this module "exports".
return function (state) {
......@@ -24,23 +24,25 @@ function (bind) {
// Functions which will be accessible via 'state' object. When called, these functions will
// get the 'state' object as a context.
function makeFunctionsPublic(state) {
state.videoCaption.resize = bind(resize, state);
state.videoCaption.toggle = bind(toggle, state);
state.videoCaption.onMouseEnter = bind(onMouseEnter, state);
state.videoCaption.onMouseLeave = bind(onMouseLeave, state);
state.videoCaption.onMovement = bind(onMovement, state);
state.videoCaption.renderCaption = bind(renderCaption, state);
state.videoCaption.captionHeight = bind(captionHeight, state);
state.videoCaption.topSpacingHeight = bind(topSpacingHeight, state);
state.videoCaption.bottomSpacingHeight = bind(bottomSpacingHeight, state);
state.videoCaption.scrollCaption = bind(scrollCaption, state);
state.videoCaption.search = bind(search, state);
state.videoCaption.play = bind(play, state);
state.videoCaption.pause = bind(pause, state);
state.videoCaption.seekPlayer = bind(seekPlayer, state);
state.videoCaption.hideCaptions = bind(hideCaptions, state);
state.videoCaption.calculateOffset = bind(calculateOffset, state);
state.videoCaption.updatePlayTime = bind(updatePlayTime, state);
state.videoCaption.autoShowCaptions = autoShowCaptions.bind(state);
state.videoCaption.autoHideCaptions = autoHideCaptions.bind(state);
state.videoCaption.resize = resize.bind(state);
state.videoCaption.toggle = toggle.bind(state);
state.videoCaption.onMouseEnter = onMouseEnter.bind(state);
state.videoCaption.onMouseLeave = onMouseLeave.bind(state);
state.videoCaption.onMovement = onMovement.bind(state);
state.videoCaption.renderCaption = renderCaption.bind(state);
state.videoCaption.captionHeight = captionHeight.bind(state);
state.videoCaption.topSpacingHeight = topSpacingHeight.bind(state);
state.videoCaption.bottomSpacingHeight = bottomSpacingHeight.bind(state);
state.videoCaption.scrollCaption = scrollCaption.bind(state);
state.videoCaption.search = search.bind(state);
state.videoCaption.play = play.bind(state);
state.videoCaption.pause = pause.bind(state);
state.videoCaption.seekPlayer = seekPlayer.bind(state);
state.videoCaption.hideCaptions = hideCaptions.bind(state);
state.videoCaption.calculateOffset = calculateOffset.bind(state);
state.videoCaption.updatePlayTime = updatePlayTime.bind(state);
}
// function renderElements(state)
......@@ -64,6 +66,13 @@ function (bind) {
});
fetchCaption(state);
if (state.videoType === 'html5') {
state.videoCaption.fadeOutTimeout = 1400;
state.videoCaption.subtitlesEl.addClass('html5');
state.captionHideTimeout = setTimeout(state.videoCaption.autoHideCaptions, state.videoCaption.fadeOutTimeout);
}
}
// function bindHandlers(state)
......@@ -85,6 +94,10 @@ function (bind) {
'DOMMouseScroll',
state.videoCaption.onMovement
);
if (state.videoType === 'html5') {
state.el.on('mousemove', state.videoCaption.autoShowCaptions)
}
}
function fetchCaption(state) {
......@@ -106,10 +119,6 @@ function (bind) {
}
function captionURL(state) {
console.log('We are inside captionURL() function.');
console.log('state.config.caption_asset_path = "' + state.config.caption_asset_path + '".');
console.log('state.youtubeId("1.0") = "' + state.youtubeId('1.0') + '".');
return '' + state.config.caption_asset_path + state.youtubeId('1.0') + '.srt.sjson';
}
......@@ -119,6 +128,51 @@ function (bind) {
// The magic private function that makes them available and sets up their context is makeFunctionsPublic().
// ***************************************************************
function autoShowCaptions(event) {
if (!this.captionsShowLock) {
if (!this.captionsHidden) {
return;
}
this.captionsShowLock = true;
if (this.captionState === 'invisible') {
this.videoCaption.subtitlesEl.show();
this.captionState = 'visible';
this.captionHideTimeout = setTimeout(this.videoCaption.autoHideCaptions, this.videoCaption.fadeOutTimeout);
} else if (this.captionState === 'hiding') {
this.videoCaption.subtitlesEl.stop(true, false);
this.videoCaption.subtitlesEl.css('opacity', 1);
this.videoCaption.subtitlesEl.show();
this.captionState = 'visible';
this.captionHideTimeout = setTimeout(this.videoCaption.autoHideCaptions, this.videoCaption.fadeOutTimeout);
} else if (this.captionState === 'visible') {
clearTimeout(this.captionHideTimeout);
this.captionHideTimeout = setTimeout(this.videoCaption.autoHideCaptions, this.videoCaption.fadeOutTimeout);
}
this.captionsShowLock = false;
}
}
function autoHideCaptions() {
var _this;
this.captionHideTimeout = null;
if (!this.captionsHidden) {
return;
}
this.captionState = 'hiding';
_this = this;
this.videoCaption.subtitlesEl.fadeOut(1000, function () {
_this.captionState = 'invisible';
});
}
function resize() {
this.videoCaption.subtitlesEl.css({
'maxHeight': this.videoCaption.captionHeight()
......@@ -254,7 +308,7 @@ function (bind) {
event.preventDefault();
time = Math.round(Time.convert($(event.target).data('start'), '1.0', this.speed) / 1000);
this.trigger(['videoPlayer', 'onSeek'], time, 'method');
this.trigger(['videoPlayer', 'onSeek'], time);
}
function calculateOffset(element) {
......@@ -281,9 +335,11 @@ function (bind) {
function hideCaptions(hide_captions) {
if (hide_captions) {
this.captionsHidden = true;
this.videoCaption.hideSubtitlesEl.attr('title', 'Turn on captions');
this.el.addClass('closed');
} else {
this.captionsHidden = false;
this.videoCaption.hideSubtitlesEl.attr('title', 'Turn off captions');
this.el.removeClass('closed');
this.videoCaption.scrollCaption();
......
......@@ -3,8 +3,8 @@
// VideoControl module.
define(
'videoalpha/display/video_control.js',
['videoalpha/display/bind.js'],
function (bind) {
[],
function () {
// VideoControl() function - what this module "exports".
return function (state) {
......@@ -24,14 +24,14 @@ function (bind) {
// Functions which will be accessible via 'state' object. When called, these functions will
// get the 'state' object as a context.
function makeFunctionsPublic(state) {
state.videoControl.showControls = bind(showControls, state);
state.videoControl.hideControls = bind(hideControls, state);
state.videoControl.play = bind(play, state);
state.videoControl.pause = bind(pause, state);
state.videoControl.togglePlayback = bind(togglePlayback, state);
state.videoControl.toggleFullScreen = bind(toggleFullScreen, state);
state.videoControl.exitFullScreen = bind(exitFullScreen, state);
state.videoControl.updateVcrVidTime = bind(updateVcrVidTime, state);
state.videoControl.showControls = showControls.bind(state);
state.videoControl.hideControls = hideControls.bind(state);
state.videoControl.play = play.bind(state);
state.videoControl.pause = pause.bind(state);
state.videoControl.togglePlayback = togglePlayback.bind(state);
state.videoControl.toggleFullScreen = toggleFullScreen.bind(state);
state.videoControl.exitFullScreen = exitFullScreen.bind(state);
state.videoControl.updateVcrVidTime = updateVcrVidTime.bind(state);
}
// function renderElements(state)
......@@ -41,7 +41,7 @@ function (bind) {
// way - you don't have to do repeated jQuery element selects.
function renderElements(state) {
var el, qTipConfig;
// REFACTOR move templates and css to one file- to python part
el = $(
'<div class="slider"></div>' +
'<div>' +
......@@ -82,8 +82,12 @@ function (bind) {
state.videoControl.play();
}
state.controlHideTimeout = setTimeout(state.videoControl.hideControls, 3000);
state.el.find('.video-roof').on('mousemove', state.videoControl.showControls);
if (state.videoType === 'html5') {
state.videoControl.fadeOutTimeout = 1400;
state.videoControl.el.addClass('html5');
state.controlHideTimeout = setTimeout(state.videoControl.hideControls, state.videoControl.fadeOutTimeout);
}
}
// function bindHandlers(state)
......@@ -93,6 +97,10 @@ function (bind) {
state.videoControl.playPauseEl.on('click', state.videoControl.togglePlayback);
state.videoControl.fullScreenEl.on('click', state.videoControl.toggleFullScreen);
$(document).on('keyup', state.videoControl.exitFullScreen);
if (state.videoType === 'html5') {
state.el.on('mousemove', state.videoControl.showControls)
}
}
// ***************************************************************
......@@ -100,61 +108,32 @@ function (bind) {
// These are available via the 'state' object. Their context ('this' keyword) is the 'state' object.
// The magic private function that makes them available and sets up their context is makeFunctionsPublic().
// ***************************************************************
// REFACTOR document
function showControls(event) {
var elPosition, elWidth, elHeight;
normalize(event);
elPosition = this.el.position();
elWidth = this.el.width();
elHeight = this.el.height();
if (
(event.pageX < elPosition.left) ||
(event.pageX > elPosition.left + elWidth) ||
(event.pageY < elPosition.top) ||
(event.pageY > elPosition.top + elHeight)
) {
return;
}
if (this.controlState === 'invisible') {
this.videoControl.el.show();
this.controlState = 'visible';
this.controlHideTimeout = setTimeout(this.videoControl.hideControls, 3000);
}/* else if (this.controlState === 'hiding') {
this.videoControl.el.stop(true, false);
this.videoControl.el.show();
this.controlState = 'visible';
this.controlHideTimeout = setTimeout(this.videoControl.hideControls, 3000);
}*/ else if (this.controlState === 'visible') {
clearTimeout(this.controlHideTimeout);
this.controlHideTimeout = setTimeout(this.videoControl.hideControls, 3000);
}
this.controlShowLock = false;
if (this.videoPlayer && this.videoPlayer.player) {
(function (event, _this) {
var c1;
c1 = 0;
_this.el.find('#' + _this.id).children().each(function (index, value) {
$(value).trigger(event);
c1 += 1;
});
}(event, this));
}
return;
if (!this.controlShowLock) {
if (!this.captionsHidden) {
return;
}
function normalize(event) {
if(!event.offsetX) {
event.offsetX = (event.pageX - $(event.target).offset().left);
event.offsetY = (event.pageY - $(event.target).offset().top);
this.controlShowLock = true;
// Refactor: separate UI state in object. No code duplication.
if (this.controlState === 'invisible') {
this.videoControl.el.show();
this.controlState = 'visible';
this.controlHideTimeout = setTimeout(this.videoControl.hideControls, this.videoControl.fadeOutTimeout);
} else if (this.controlState === 'hiding') {
this.videoControl.el.stop(true, false);
this.videoControl.el.css('opacity', 1);
this.videoControl.el.show();
this.controlState = 'visible';
this.controlHideTimeout = setTimeout(this.videoControl.hideControls, this.videoControl.fadeOutTimeout);
} else if (this.controlState === 'visible') {
clearTimeout(this.controlHideTimeout);
this.controlHideTimeout = setTimeout(this.videoControl.hideControls, this.videoControl.fadeOutTimeout);
}
return event;
this.controlShowLock = false;
}
}
......@@ -162,6 +141,11 @@ function (bind) {
var _this;
this.controlHideTimeout = null;
if (!this.captionsHidden) {
return;
}
this.controlState = 'hiding';
_this = this;
......@@ -185,16 +169,16 @@ function (bind) {
event.preventDefault();
if (this.videoControl.playPauseState === 'playing') {
this.trigger(['videoPlayer', 'pause'], null, 'method');
this.trigger(['videoPlayer', 'pause'], null);
} else { // if (this.videoControl.playPauseState === 'paused') {
this.trigger(['videoPlayer', 'play'], null, 'method');
this.trigger(['videoPlayer', 'play'], null);
}
}
function toggleFullScreen(event) {
event.preventDefault();
if (this.videoControl.fullScreenState === true) {
if (this.videoControl.fullScreenState) {
this.videoControl.fullScreenState = false;
this.el.removeClass('fullscreen');
this.videoControl.fullScreenEl.attr('title', 'Fill browser');
......@@ -204,11 +188,11 @@ function (bind) {
this.videoControl.fullScreenEl.attr('title', 'Exit fill browser');
}
this.trigger(['videoCaption', 'resize'], null, 'method');
this.trigger(['videoCaption', 'resize'], null);
}
function exitFullScreen(event) {
if ((this.el.hasClass('fullscreen') === true) && (event.keyCode === 27)) {
if ((this.el.hasClass('fullscreen')) && (event.keyCode === 27)) {
this.videoControl.toggleFullScreen(event);
}
}
......
......@@ -3,11 +3,8 @@
// VideoPlayer module.
define(
'videoalpha/display/video_player.js',
[
'videoalpha/display/html5_video.js',
'videoalpha/display/bind.js'
],
function (HTML5Video, bind) {
['videoalpha/display/html5_video.js'],
function (HTML5Video) {
// VideoPlayer() function - what this module "exports".
return function (state) {
......@@ -27,24 +24,24 @@ function (HTML5Video, bind) {
// Functions which will be accessible via 'state' object. When called, these functions will
// get the 'state' object as a context.
function makeFunctionsPublic(state) {
state.videoPlayer.pause = bind(pause, state);
state.videoPlayer.play = bind(play, state);
state.videoPlayer.update = bind(update, state);
state.videoPlayer.onSpeedChange = bind(onSpeedChange, state);
state.videoPlayer.onSeek = bind(onSeek, state);
state.videoPlayer.onEnded = bind(onEnded, state);
state.videoPlayer.onPause = bind(onPause, state);
state.videoPlayer.onPlay = bind(onPlay, state);
state.videoPlayer.onUnstarted = bind(onUnstarted, state);
state.videoPlayer.handlePlaybackQualityChange = bind(handlePlaybackQualityChange, state);
state.videoPlayer.onPlaybackQualityChange = bind(onPlaybackQualityChange, state);
state.videoPlayer.onStateChange = bind(onStateChange, state);
state.videoPlayer.onReady = bind(onReady, state);
state.videoPlayer.updatePlayTime = bind(updatePlayTime, state);
state.videoPlayer.isPlaying = bind(isPlaying, state);
state.videoPlayer.log = bind(log, state);
state.videoPlayer.duration = bind(duration, state);
state.videoPlayer.onVolumeChange = bind(onVolumeChange, state);
state.videoPlayer.pause = pause.bind(state);
state.videoPlayer.play = play.bind(state);
state.videoPlayer.update = update.bind(state);
state.videoPlayer.onSpeedChange = onSpeedChange.bind(state);
state.videoPlayer.onSeek = onSeek.bind(state);
state.videoPlayer.onEnded = onEnded.bind(state);
state.videoPlayer.onPause = onPause.bind(state);
state.videoPlayer.onPlay = onPlay.bind(state);
state.videoPlayer.onUnstarted = onUnstarted.bind(state);
state.videoPlayer.handlePlaybackQualityChange = handlePlaybackQualityChange.bind(state);
state.videoPlayer.onPlaybackQualityChange = onPlaybackQualityChange.bind(state);
state.videoPlayer.onStateChange = onStateChange.bind(state);
state.videoPlayer.onReady = onReady.bind(state);
state.videoPlayer.updatePlayTime = updatePlayTime.bind(state);
state.videoPlayer.isPlaying = isPlaying.bind(state);
state.videoPlayer.log = log.bind(state);
state.videoPlayer.duration = duration.bind(state);
state.videoPlayer.onVolumeChange = onVolumeChange.bind(state);
}
// function renderElements(state)
......@@ -171,7 +168,7 @@ function (HTML5Video, bind) {
function update() {
this.videoPlayer.currentTime = this.videoPlayer.player.getCurrentTime();
if (isFinite(this.videoPlayer.currentTime) === true) {
if (isFinite(this.videoPlayer.currentTime)) {
this.videoPlayer.updatePlayTime(this.videoPlayer.currentTime);
}
}
......@@ -214,7 +211,7 @@ function (HTML5Video, bind) {
}
function onEnded() {
this.trigger(['videoControl','pause'], null, 'method');
this.trigger(['videoControl','pause'], null);
}
function onPause() {
......@@ -223,7 +220,7 @@ function (HTML5Video, bind) {
clearInterval(this.videoPlayer.updateInterval);
delete this.videoPlayer.updateInterval;
this.trigger(['videoControl','pause'], null, 'method');
this.trigger(['videoControl','pause'], null);
}
function onPlay() {
......@@ -233,7 +230,7 @@ function (HTML5Video, bind) {
this.videoPlayer.updateInterval = setInterval(this.videoPlayer.update, 200);
}
this.trigger(['videoControl','play'], null, 'method');
this.trigger(['videoControl','play'], null);
}
function onUnstarted() { }
......@@ -247,7 +244,7 @@ function (HTML5Video, bind) {
quality = this.videoPlayer.player.getPlaybackQuality();
this.trigger(['videoQualityControl', 'onQualityChange'], quality, 'method');
this.trigger(['videoQualityControl', 'onQualityChange'], quality);
}
function onReady() {
......@@ -274,7 +271,7 @@ function (HTML5Video, bind) {
_this.speeds.push(value.toFixed(2).replace(/\.00$/, '.0'));
});
this.trigger(['videoSpeedControl', 'reRender'], {'newSpeeds': this.speeds, 'currentSpeed': this.speed}, 'method');
this.trigger(['videoSpeedControl', 'reRender'], {'newSpeeds': this.speeds, 'currentSpeed': this.speed});
this.setSpeed($.cookie('video_speed'));
}
......@@ -311,9 +308,9 @@ function (HTML5Video, bind) {
duration = this.videoPlayer.duration();
this.trigger(['videoProgressSlider', 'updatePlayTime'], {'time': time, 'duration': duration}, 'method');
this.trigger(['videoControl', 'updateVcrVidTime'], {'time': time, 'duration': duration}, 'method');
this.trigger(['videoCaption', 'updatePlayTime'], time, 'method');
this.trigger(['videoProgressSlider', 'updatePlayTime'], {'time': time, 'duration': duration});
this.trigger(['videoControl', 'updateVcrVidTime'], {'time': time, 'duration': duration});
this.trigger(['videoCaption', 'updatePlayTime'], time);
}
function isPlaying() {
......@@ -324,7 +321,7 @@ function (HTML5Video, bind) {
var duration;
duration = this.videoPlayer.player.getDuration();
if (isFinite(duration) === false) {
if (!isFinite(duration)) {
duration = this.getDuration();
}
......
......@@ -10,8 +10,8 @@ mind, or whether to act, and in acting, to live."
// VideoProgressSlider module.
define(
'videoalpha/display/video_progress_slider.js',
['videoalpha/display/bind.js'],
function (bind) {
[],
function () {
// VideoProgressSlider() function - what this module "exports".
return function (state) {
......@@ -31,11 +31,11 @@ function (bind) {
// Functions which will be accessible via 'state' object. When called, these functions will
// get the 'state' object as a context.
function makeFunctionsPublic(state) {
state.videoProgressSlider.onSlide = bind(onSlide, state);
state.videoProgressSlider.onChange = bind(onChange, state);
state.videoProgressSlider.onStop = bind(onStop, state);
state.videoProgressSlider.updateTooltip = bind(updateTooltip, state);
state.videoProgressSlider.updatePlayTime = bind(updatePlayTime, state);
state.videoProgressSlider.onSlide = onSlide.bind(state);
state.videoProgressSlider.onChange = onChange.bind(state);
state.videoProgressSlider.onStop = onStop.bind(state);
state.videoProgressSlider.updateTooltip = updateTooltip.bind(state);
state.videoProgressSlider.updatePlayTime = updatePlayTime.bind(state);
}
// function renderElements(state)
......@@ -98,7 +98,7 @@ function (bind) {
this.videoProgressSlider.frozen = true;
this.videoProgressSlider.updateTooltip(ui.value);
this.trigger(['videoPlayer', 'onSeek'], ui.value, 'method');
this.trigger(['videoPlayer', 'onSeek'], ui.value);
}
function onChange(event, ui) {
......@@ -112,7 +112,7 @@ function (bind) {
this.videoProgressSlider.frozen = true;
this.trigger(['videoPlayer', 'onSeek'], ui.value, 'method');
this.trigger(['videoPlayer', 'onSeek'], ui.value);
setTimeout(function() {
_this.videoProgressSlider.frozen = false;
......
......@@ -3,8 +3,8 @@
// VideoQualityControl module.
define(
'videoalpha/display/video_quality_control.js',
['videoalpha/display/bind.js'],
function (bind) {
[],
function () {
// VideoQualityControl() function - what this module "exports".
return function (state) {
......@@ -29,8 +29,8 @@ function (bind) {
// Functions which will be accessible via 'state' object. When called, these functions will
// get the 'state' object as a context.
function makeFunctionsPublic(state) {
state.videoQualityControl.onQualityChange = bind(onQualityChange, state);
state.videoQualityControl.toggleQuality = bind(toggleQuality, state);
state.videoQualityControl.onQualityChange = onQualityChange.bind(state);
state.videoQualityControl.toggleQuality = toggleQuality.bind(state);
}
// function renderElements(state)
......@@ -92,7 +92,7 @@ function (bind) {
newQuality = 'hd720';
}
this.trigger(['videoPlayer', 'handlePlaybackQualityChange'], newQuality, 'method');
this.trigger(['videoPlayer', 'handlePlaybackQualityChange'], newQuality);
}
});
......
......@@ -3,8 +3,8 @@
// VideoSpeedControl module.
define(
'videoalpha/display/video_speed_control.js',
['videoalpha/display/bind.js'],
function (bind) {
[],
function () {
// VideoSpeedControl() function - what this module "exports".
return function (state) {
......@@ -24,9 +24,9 @@ function (bind) {
// Functions which will be accessible via 'state' object. When called, these functions will
// get the 'state' object as a context.
function makeFunctionsPublic(state) {
state.videoSpeedControl.changeVideoSpeed = bind(changeVideoSpeed, state);
state.videoSpeedControl.setSpeed = bind(setSpeed, state);
state.videoSpeedControl.reRender = bind(reRender, state);
state.videoSpeedControl.changeVideoSpeed = changeVideoSpeed.bind(state);
state.videoSpeedControl.setSpeed = setSpeed.bind(state);
state.videoSpeedControl.reRender = reRender.bind(state);
}
// function renderElements(state)
......@@ -113,7 +113,7 @@ function (bind) {
parseFloat(this.videoSpeedControl.currentSpeed).toFixed(2).replace(/\.00$/, '.0')
);
this.trigger(['videoPlayer', 'onSpeedChange'], this.videoSpeedControl.currentSpeed, 'method');
this.trigger(['videoPlayer', 'onSpeedChange'], this.videoSpeedControl.currentSpeed);
}
}
......
......@@ -3,8 +3,8 @@
// VideoVolumeControl module.
define(
'videoalpha/display/video_volume_control.js',
['videoalpha/display/bind.js'],
function (bind) {
[],
function () {
// VideoVolumeControl() function - what this module "exports".
return function (state) {
......@@ -24,8 +24,8 @@ function (bind) {
// Functions which will be accessible via 'state' object. When called, these functions will
// get the 'state' object as a context.
function makeFunctionsPublic(state) {
state.videoVolumeControl.onChange = bind(onChange, state);
state.videoVolumeControl.toggleMute = bind(toggleMute, state);
state.videoVolumeControl.onChange = onChange.bind(state);
state.videoVolumeControl.toggleMute = toggleMute.bind(state);
}
// function renderElements(state)
......@@ -54,7 +54,7 @@ function (bind) {
state.videoVolumeControl.currentVolume = parseInt($.cookie('video_player_volume_level'), 10);
state.videoVolumeControl.previousVolume = 100;
if (
(isFinite(state.videoVolumeControl.currentVolume) === false) ||
(!isFinite(state.videoVolumeControl.currentVolume)) ||
(state.videoVolumeControl.currentVolume < 0) ||
(state.videoVolumeControl.currentVolume > 100)
) {
......@@ -104,7 +104,7 @@ function (bind) {
'path': '/'
});
this.trigger(['videoPlayer', 'onVolumeChange'], ui.value, 'method');
this.trigger(['videoPlayer', 'onVolumeChange'], ui.value);
}
function toggleMute(event) {
......
......@@ -69,7 +69,6 @@ class VideoAlphaModule(VideoAlphaFields, XModule):
js = {
'js': [
resource_string(__name__, 'js/src/videoalpha/display/bind.js'),
resource_string(__name__, 'js/src/videoalpha/display/initialize.js'),
resource_string(__name__, 'js/src/videoalpha/display/html5_video.js'),
resource_string(__name__, 'js/src/videoalpha/display/video_player.js'),
......
......@@ -29,10 +29,11 @@
>
<div class="tc-wrapper">
<article class="video-wrapper">
<div class="video-player-pre"></div>
<section class="video-player">
<div id="${id}"></div>
</section>
<section class="video-roof"></section>
<div class="video-player-post"></div>
<section class="video-controls"></section>
</article>
</div>
......
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