Initial commit for feature BLD-391: Add arrows to the interface for start/end times.

If end time is specified (which means that there is also a start time), we
add an extra DOM element to the slider that will show the are in time between
start time and end time. Unfortunately, this visual aid will become available
ony after start of playback. This is because until then we don't know the length
of the video.
This commit is contained in:
Valera Rozuvan
2013-10-18 12:09:17 +03:00
parent 6c08708131
commit d0fbd502fb
4 changed files with 149 additions and 61 deletions

View File

@@ -75,6 +75,8 @@ function (VideoPlayer) {
state.parseYoutubeStreams = _.bind(parseYoutubeStreams, state);
state.parseVideoSources = _.bind(parseVideoSources, state);
state.getVideoMetadata = _.bind(getVideoMetadata, state);
state.checkStartEndTimes = _.bind(checkStartEndTimes, state);
}
// function _renderElements(state)
@@ -277,6 +279,10 @@ function (VideoPlayer) {
availableQualities: ['hd720', 'hd1080', 'highres']
};
// Make sure that start end end times are valid. If not, they will be
// set to `null` and will not be used later on.
this.checkStartEndTimes();
// Check if the YT test timeout has been set. If not, or it is in
// improper format, then set to default value.
tempYtTestTimeout = parseInt(data['ytTestTimeout'], 10);
@@ -360,6 +366,30 @@ function (VideoPlayer) {
}
}
/*
* function checkStartEndTimes()
*
* Validate config.start and config.end times.
*
* We can check at this time if the times are proper integers, and if they
* make general sense. I.e. if start time is => 0 and <= end time.
*
* An invalid start time will be reset to 0. An invalid end time will be
* set to `null`. It the task for the appropriate player API to figure out
* if start time and/or end time are greater than the length of the video.
*/
function checkStartEndTimes() {
this.config.start = parseInt(this.config.start, 10);
if ((!isFinite(this.config.start)) || (this.config.start < 0)) {
this.config.start = 0;
}
this.config.end = parseInt(this.config.end, 10);
if ((!isFinite(this.config.end)) || (this.config.end < this.config.start)) {
this.config.end = null;
}
}
// function parseYoutubeStreams(state, youtubeStreams)
//
// Take a string in the form:

View File

@@ -191,19 +191,8 @@ function () {
}
// Determine the starting and ending time for the video.
this.start = 0;
this.end = null;
if (config.playerVars) {
this.start = parseFloat(config.playerVars.start);
if ((!isFinite(this.start)) || (this.start < 0)) {
this.start = 0;
}
this.end = parseFloat(config.playerVars.end);
if ((!isFinite(this.end)) || (this.end < this.start)) {
this.end = null;
}
}
this.start = config.playerVars.start;
this.end = config.playerVars.end;
// Create HTML markup for the <video> element, populating it with sources from previous step.
// Because of problems with creating video element via jquery
@@ -217,7 +206,6 @@ function () {
this.videoEl = $(this.video);
this.playerState = HTML5Video.PlayerState.UNSTARTED;
// this.callStateChangeCallback();
// Attach a 'click' event on the <video> element. It will cause the video to pause/play.
this.videoEl.on('click', function (event) {

View File

@@ -34,9 +34,7 @@ function (HTML5Video, Resizer) {
state.videoPlayer.onPause = _.bind(onPause, state);
state.videoPlayer.onPlay = _.bind(onPlay, state);
state.videoPlayer.onUnstarted = _.bind(
onUnstarted, state
);
state.videoPlayer.onUnstarted = _.bind(onUnstarted, state);
state.videoPlayer.handlePlaybackQualityChange = _.bind(
handlePlaybackQualityChange, state
@@ -86,13 +84,8 @@ function (HTML5Video, Resizer) {
state.videoPlayer.playerVars.html5 = 1;
}
if (state.config.start) {
state.videoPlayer.playerVars.start = state.config.start;
state.videoPlayer.playerVars.wmode = 'window';
}
if (state.config.end) {
state.videoPlayer.playerVars.end = state.config.end;
}
state.videoPlayer.playerVars.start = state.config.start;
state.videoPlayer.playerVars.end = state.config.end;
// There is a bug which prevents YouTube API to correctly set the speed
// to 1.0 from another speed in Firefox when in HTML5 mode. There is a
@@ -478,6 +471,13 @@ function (HTML5Video, Resizer) {
}
);
this.trigger(
'videoProgressSlider.updateStartEndTimeRegion',
{
duration: duration
}
);
this.trigger(
'videoControl.updateVcrVidTime',
{

View File

@@ -28,21 +28,29 @@ function () {
// function _makeFunctionsPublic(state)
//
// Functions which will be accessible via 'state' object. When called, these functions will
// get the 'state' object as a context.
// 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.onStop = _.bind(onStop, state);
state.videoProgressSlider.updatePlayTime = _.bind(updatePlayTime, state);
state.videoProgressSlider.updatePlayTime = _.bind(
updatePlayTime, state
);
//Added for tests -- JM
state.videoProgressSlider.buildSlider = _.bind(buildSlider, state);
state.videoProgressSlider.updateStartEndTimeRegion = _.bind(
updateStartEndTimeRegion, state
);
}
// function _renderElements(state)
//
// Create any necessary DOM elements, attach them, and set their initial configuration. Also
// 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.
// Create any necessary DOM elements, attach them, and set their
// initial configuration. Also 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) {
if (!onTouchBasedDevice()) {
state.videoProgressSlider.el = state.videoControl.sliderEl;
@@ -53,8 +61,9 @@ function () {
}
function _buildHandle(state) {
state.videoProgressSlider.handle = state.videoProgressSlider.el.find('.ui-slider-handle');
state.videoProgressSlider.handle = state.videoProgressSlider.el
.find('.ui-slider-handle');
// ARIA
// We just want the knob to be selectable with keyboard
state.videoProgressSlider.el.attr('tabindex', -1);
@@ -64,28 +73,75 @@ function () {
'role': 'slider',
'title': 'video position',
'aria-disabled': false,
'aria-valuetext': getTimeDescription(state.videoProgressSlider.slider.slider('option', 'value'))
'aria-valuetext': getTimeDescription(state.videoProgressSlider
.slider.slider('option', 'value'))
});
}
// ***************************************************************
// Public functions start here.
// 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().
// 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().
// ***************************************************************
function buildSlider(state) {
state.videoProgressSlider.slider = state.videoProgressSlider.el.slider({
range: 'min',
slide: state.videoProgressSlider.onSlide,
stop: state.videoProgressSlider.onStop
});
console.log('state = ', state);
state.videoProgressSlider.slider = state.videoProgressSlider.el
.slider({
range: 'min',
slide: state.videoProgressSlider.onSlide,
stop: state.videoProgressSlider.onStop
});
state.videoProgressSlider.sliderProgress = state.videoProgressSlider
.slider
.find('.ui-slider-range.ui-widget-header.ui-slider-range-min');
}
function updateStartEndTimeRegion(params) {
var left, width;
if (this.config.end && params.duration) {
left = parseInt(100 * (this.config.start / params.duration), 10);
width = parseInt(
100 * (
(this.config.end - this.config.start) / params.duration
),
10
);
if (!this.videoProgressSlider.sliderRange) {
this.videoProgressSlider.sliderRange = $('<div />')
.addClass('ui-slider-range')
.addClass('ui-widget-header')
.addClass('ui-corner-all')
.css({
left: left + '%',
width: width + '%',
'background-color': 'blue'
});
this.videoProgressSlider.sliderProgress
.after(this.videoProgressSlider.sliderRange);
} else {
this.videoProgressSlider.sliderRange
.css({
left: left + '%',
width: width + '%'
});
}
}
}
function onSlide(event, ui) {
this.videoProgressSlider.frozen = true;
this.trigger('videoPlayer.onSlideSeek', {'type': 'onSlideSeek', 'time': ui.value});
this.trigger(
'videoPlayer.onSlideSeek',
{'type': 'onSlideSeek', 'time': ui.value}
);
// ARIA
this.videoProgressSlider.handle.attr(
@@ -98,7 +154,10 @@ function () {
this.videoProgressSlider.frozen = true;
this.trigger('videoPlayer.onSlideSeek', {'type': 'onSlideSeek', 'time': ui.value});
this.trigger(
'videoPlayer.onSlideSeek',
{'type': 'onSlideSeek', 'time': ui.value}
);
// ARIA
this.videoProgressSlider.handle.attr(
@@ -110,23 +169,28 @@ function () {
}, 200);
}
//Changed for tests -- JM: Check if it is the cause of Chrome Bug Valera noticed
// Changed for tests -- JM: Check if it is the cause of Chrome Bug Valera
// noticed
function updatePlayTime(params) {
if ((this.videoProgressSlider.slider) && (!this.videoProgressSlider.frozen)) {
/*this.videoProgressSlider.slider
.slider('option', 'max', params.duration)
.slider('value', params.time);*/
this.videoProgressSlider.slider.slider('option', 'max', params.duration);
this.videoProgressSlider.slider.slider('option', 'value', params.time);
if (
(this.videoProgressSlider.slider) &&
(!this.videoProgressSlider.frozen)
) {
this.videoProgressSlider.slider
.slider('option', 'max', params.duration);
this.videoProgressSlider.slider
.slider('option', 'value', params.time);
}
}
// Returns a string describing the current time of video in hh:mm:ss format.
// Returns a string describing the current time of video in hh:mm:ss
// format.
function getTimeDescription(time) {
var seconds = Math.floor(time),
minutes = Math.floor(seconds / 60),
hours = Math.floor(minutes / 60),
hrStr, minStr, secStr;
seconds = seconds % 60;
minutes = minutes % 60;
@@ -136,30 +200,36 @@ function () {
if (hours) {
hrStr += (hours < 2 ? ' hour ' : ' hours ');
if (minutes) {
if (minutes) {
minStr += (minutes < 2 ? ' minute ' : ' minutes ');
} else {
minStr += ' 0 minutes ';
}
if (seconds) {
secStr += (seconds < 2 ? ' second ' : ' seconds ');
} else {
secStr += ' 0 seconds ';
}
return hrStr + minStr + secStr;
} else if (minutes) {
minStr += (minutes < 2 ? ' minute ' : ' minutes ');
if (seconds) {
if (seconds) {
secStr += (seconds < 2 ? ' second ' : ' seconds ');
} else {
secStr += ' 0 seconds ';
}
return hrStr + minStr + secStr;
} else if (minutes) {
minStr += (minutes < 2 ? ' minute ' : ' minutes ');
if (seconds) {
secStr += (seconds < 2 ? ' second ' : ' seconds ');
} else {
secStr += ' 0 seconds ';
}
return minStr + secStr;
} else if (seconds) {
secStr += (seconds < 2 ? ' second ' : ' seconds ');
return secStr;
}
return '0 seconds';
}