Commit 7fca5e8b by brianhw

Merge pull request #1360 from edx/valera/carry_over_of_add_option_transcripts_remove_onhover_2

Valera/carry over of add option transcripts remove onhover 2
parents 5c883993 4b371d47
...@@ -2,15 +2,18 @@ ...@@ -2,15 +2,18 @@
Feature: CMS.Video Component Feature: CMS.Video Component
As a course author, I want to be able to view my created videos in Studio. As a course author, I want to be able to view my created videos in Studio.
# 1
# Video Alpha Features will work in Firefox only when Firefox is the active window # Video Alpha Features will work in Firefox only when Firefox is the active window
Scenario: Autoplay is disabled in Studio Scenario: Autoplay is disabled in Studio
Given I have created a Video component Given I have created a Video component
Then when I view the video it does not have autoplay enabled Then when I view the video it does not have autoplay enabled
# 2
Scenario: Creating a video takes a single click Scenario: Creating a video takes a single click
Given I have clicked the new unit button Given I have clicked the new unit button
Then creating a video takes a single click Then creating a video takes a single click
# 3
# Sauce Labs cannot delete cookies # Sauce Labs cannot delete cookies
@skip_sauce @skip_sauce
Scenario: Captions are hidden correctly Scenario: Captions are hidden correctly
...@@ -18,12 +21,14 @@ Feature: CMS.Video Component ...@@ -18,12 +21,14 @@ Feature: CMS.Video Component
And I have hidden captions And I have hidden captions
Then when I view the video it does not show the captions Then when I view the video it does not show the captions
# 4
# Sauce Labs cannot delete cookies # Sauce Labs cannot delete cookies
@skip_sauce @skip_sauce
Scenario: Captions are shown correctly Scenario: Captions are shown correctly
Given I have created a Video component with subtitles Given I have created a Video component with subtitles
Then when I view the video it does show the captions Then when I view the video it does show the captions
# 5
# Sauce Labs cannot delete cookies # Sauce Labs cannot delete cookies
@skip_sauce @skip_sauce
Scenario: Captions are toggled correctly Scenario: Captions are toggled correctly
...@@ -31,6 +36,35 @@ Feature: CMS.Video Component ...@@ -31,6 +36,35 @@ Feature: CMS.Video Component
And I have toggled captions And I have toggled captions
Then when I view the video it does show the captions Then when I view the video it does show the captions
# 6
Scenario: Video data is shown correctly Scenario: Video data is shown correctly
Given I have created a video with only XML data Given I have created a video with only XML data
Then the correct Youtube video is shown Then the correct Youtube video is shown
# 7
Scenario: Closed captions become visible when the mouse hovers over CC button
Given I have created a Video component with subtitles
And Make sure captions are closed
Then Captions become "invisible" after 3 seconds
And I hover over button "CC"
Then Captions become "visible"
And I hover over button "volume"
Then Captions become "invisible" after 3 seconds
# 8
Scenario: Open captions never become invisible
Given I have created a Video component with subtitles
And Make sure captions are open
Then Captions are "visible"
And I hover over button "CC"
Then Captions are "visible"
And I hover over button "volume"
Then Captions are "visible"
# 9
Scenario: Closed captions are invisible when mouse doesn't hover on CC button
Given I have created a Video component with subtitles
And Make sure captions are closed
Then Captions become "invisible" after 3 seconds
And I hover over button "volume"
Then Captions are "invisible"
...@@ -5,6 +5,11 @@ from terrain.steps import reload_the_page ...@@ -5,6 +5,11 @@ from terrain.steps import reload_the_page
from xmodule.modulestore import Location from xmodule.modulestore import Location
from contentstore.utils import get_modulestore from contentstore.utils import get_modulestore
BUTTONS = {
'CC': '.hide-subtitles',
'volume': '.volume',
}
@step('I have created a Video component$') @step('I have created a Video component$')
def i_created_a_video_component(step): def i_created_a_video_component(step):
...@@ -17,8 +22,13 @@ def i_created_a_video_component(step): ...@@ -17,8 +22,13 @@ def i_created_a_video_component(step):
@step('I have created a Video component with subtitles$') @step('I have created a Video component with subtitles$')
def i_created_a_video_component_subtitles(step): def i_created_a_video_with_subs(_step):
step.given('I have created a Video component') _step.given('I have created a Video component with subtitles "OEoXaMPEzfM"')
@step('I have created a Video component with subtitles "([^"]*)"$')
def i_created_a_video_with_subs_with_name(_step, sub_id):
_step.given('I have created a Video component')
# Store the current URL so we can return here # Store the current URL so we can return here
video_url = world.browser.url video_url = world.browser.url
...@@ -108,3 +118,37 @@ def the_youtube_video_is_shown(_step): ...@@ -108,3 +118,37 @@ def the_youtube_video_is_shown(_step):
ele = world.css_find('.video').first ele = world.css_find('.video').first
assert ele['data-streams'].split(':')[1] == world.scenario_dict['YOUTUBE_ID'] assert ele['data-streams'].split(':')[1] == world.scenario_dict['YOUTUBE_ID']
@step('Make sure captions are (.+)$')
def set_captions_visibility_state(_step, captions_state):
if captions_state == 'closed':
if world.css_visible('.subtitles'):
world.browser.find_by_css('.hide-subtitles').click()
else:
if not world.css_visible('.subtitles'):
world.browser.find_by_css('.hide-subtitles').click()
@step('I hover over button "([^"]*)"$')
def hover_over_button(_step, button):
world.css_find(BUTTONS[button.strip()]).mouse_over()
@step('Captions (?:are|become) "([^"]*)"$')
def are_captions_visibile(_step, visibility_state):
_step.given('Captions become "{0}" after 0 seconds'.format(visibility_state))
@step('Captions (?:are|become) "([^"]*)" after (.+) seconds$')
def check_captions_visibility_state(_step, visibility_state, timeout):
timeout = int(timeout.strip())
# Captions become invisible by fading out. We must wait by a specified
# time.
world.wait(timeout)
if visibility_state == 'visible':
assert world.css_visible('.subtitles')
else:
assert not world.css_visible('.subtitles')
...@@ -392,7 +392,7 @@ div.video { ...@@ -392,7 +392,7 @@ div.video {
@include transition(none); @include transition(none);
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
width: 30px; width: 30px;
&:hover, &:active { &:hover, &:active {
background-color: #444; background-color: #444;
color: #fff; color: #fff;
...@@ -457,7 +457,7 @@ div.video { ...@@ -457,7 +457,7 @@ div.video {
text-indent: -9999px; text-indent: -9999px;
@include transition(none); @include transition(none);
width: 30px; width: 30px;
&:hover, &:active { &:hover, &:active {
background-color: #444; background-color: #444;
color: #fff; color: #fff;
...@@ -610,6 +610,8 @@ div.video { ...@@ -610,6 +610,8 @@ div.video {
ol.subtitles { ol.subtitles {
width: 0; width: 0;
height: 0; height: 0;
visibility: hidden;
} }
ol.subtitles.html5 { ol.subtitles.html5 {
...@@ -643,6 +645,8 @@ div.video { ...@@ -643,6 +645,8 @@ div.video {
ol.subtitles { ol.subtitles {
right: -(flex-grid(4)); right: -(flex-grid(4));
width: auto; width: auto;
visibility: hidden;
} }
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
data-autoplay="False" data-autoplay="False"
data-yt-test-timeout="1500" data-yt-test-timeout="1500"
data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/" data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/"
data-autohide-html5="True"
> >
<div class="focus_grabber first"></div> <div class="focus_grabber first"></div>
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
data-autoplay="False" data-autoplay="False"
data-yt-test-timeout="1500" data-yt-test-timeout="1500"
data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/" data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/"
data-autohide-html5="True"
> >
<div class="focus_grabber first"></div> <div class="focus_grabber first"></div>
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
data-autoplay="False" data-autoplay="False"
data-yt-test-timeout="1500" data-yt-test-timeout="1500"
data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/" data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/"
data-autohide-html5="True"
> >
<div class="focus_grabber first"></div> <div class="focus_grabber first"></div>
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
data-autoplay="False" data-autoplay="False"
data-yt-test-timeout="1500" data-yt-test-timeout="1500"
data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/" data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/"
data-autohide-html5="True"
> >
<div class="focus_grabber first"></div> <div class="focus_grabber first"></div>
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
data-autoplay="False" data-autoplay="False"
data-yt-test-timeout="1500" data-yt-test-timeout="1500"
data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/" data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/"
data-autohide-html5="True"
> >
<div class="focus_grabber first"></div> <div class="focus_grabber first"></div>
...@@ -73,6 +74,8 @@ ...@@ -73,6 +74,8 @@
data-autoplay="False" data-autoplay="False"
data-yt-test-timeout="1500" data-yt-test-timeout="1500"
data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/" data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/"
data-autohide-html5="True"
> >
<div class="tc-wrapper"> <div class="tc-wrapper">
<article class="video-wrapper"> <article class="video-wrapper">
...@@ -130,6 +133,8 @@ ...@@ -130,6 +133,8 @@
data-autoplay="False" data-autoplay="False"
data-yt-test-timeout="1500" data-yt-test-timeout="1500"
data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/" data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/"
data-autohide-html5="True"
> >
<div class="tc-wrapper"> <div class="tc-wrapper">
<article class="video-wrapper"> <article class="video-wrapper">
......
(function() { (function () {
describe('VideoCaption', function() { describe('VideoCaption', function () {
var state, videoPlayer, videoCaption, videoSpeedControl, oldOTBD; var state, videoPlayer, videoCaption, videoSpeedControl, oldOTBD;
function initialize() { function initialize() {
loadFixtures('video_all.html'); loadFixtures('video_all.html');
state = new Video('#example'); state = new Video('#example');
videoPlayer = state.videoPlayer; videoPlayer = state.videoPlayer;
videoCaption = state.videoCaption; videoCaption = state.videoCaption;
videoSpeedControl = state.videoSpeedControl; videoSpeedControl = state.videoSpeedControl;
videoControl = state.videoControl; videoControl = state.videoControl;
} }
beforeEach(function() {
oldOTBD = window.onTouchBasedDevice;
window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn(false);
initialize();
});
afterEach(function() {
YT.Player = void 0;
$.fn.scrollTo.reset();
$('.subtitles').remove();
$('source').remove();
window.onTouchBasedDevice = oldOTBD;
});
describe('constructor', function() {
describe('always', function() {
beforeEach(function() {
spyOn($, 'ajaxWithPrefix').andCallThrough();
initialize();
});
it('create the caption element', function() {
expect($('.video')).toContain('ol.subtitles');
});
it('add caption control to video player', function() {
expect($('.video')).toContain('a.hide-subtitles');
});
it('fetch the caption', function() {
waitsFor(function () {
if (videoCaption.loaded === true) {
return true;
}
return false;
}, 'Expect captions to be loaded.', 1000);
runs(function () {
expect($.ajaxWithPrefix).toHaveBeenCalledWith({
url: videoCaption.captionURL(),
notifyOnError: false,
success: jasmine.any(Function),
error: jasmine.any(Function),
});
});
});
it('bind window resize event', function() {
expect($(window)).toHandleWith('resize', videoCaption.resize);
});
it('bind the hide caption button', function() {
expect($('.hide-subtitles')).toHandleWith('click', videoCaption.toggle);
});
it('bind the mouse movement', function() {
expect($('.subtitles')).toHandleWith('mouseover', videoCaption.onMouseEnter);
expect($('.subtitles')).toHandleWith('mouseout', videoCaption.onMouseLeave);
expect($('.subtitles')).toHandleWith('mousemove', videoCaption.onMovement);
expect($('.subtitles')).toHandleWith('mousewheel', videoCaption.onMovement);
expect($('.subtitles')).toHandleWith('DOMMouseScroll', videoCaption.onMovement);
});
it('bind the scroll', function() {
expect($('.subtitles')).toHandleWith('scroll', videoCaption.autoShowCaptions);
expect($('.subtitles')).toHandleWith('scroll', videoControl.showControls);
});
});
describe('when on a non touch-based device', function() {
beforeEach(function() {
initialize();
});
it('render the caption', function() {
var captionsData;
captionsData = jasmine.stubbedCaption;
$('.subtitles li[data-index]').each(function(index, link) {
expect($(link)).toHaveData('index', index);
expect($(link)).toHaveData('start', captionsData.start[index]);
expect($(link)).toHaveAttr('tabindex', 0);
expect($(link)).toHaveText(captionsData.text[index]);
});
});
it('add a padding element to caption', function() {
expect($('.subtitles li:first').hasClass('spacing')).toBe(true);
expect($('.subtitles li:last').hasClass('spacing')).toBe(true);
});
it('bind all the caption link', function() {
$('.subtitles li[data-index]').each(function(index, link) {
expect($(link)).toHandleWith('mouseover', videoCaption.captionMouseOverOut);
expect($(link)).toHandleWith('mouseout', videoCaption.captionMouseOverOut);
expect($(link)).toHandleWith('mousedown', videoCaption.captionMouseDown);
expect($(link)).toHandleWith('click', videoCaption.captionClick);
expect($(link)).toHandleWith('focus', videoCaption.captionFocus);
expect($(link)).toHandleWith('blur', videoCaption.captionBlur);
expect($(link)).toHandleWith('keydown', videoCaption.captionKeyDown);
});
});
it('set rendered to true', function() {
expect(videoCaption.rendered).toBeTruthy();
});
});
describe('when on a touch-based device', function() {
beforeEach(function() {
window.onTouchBasedDevice.andReturn(true);
initialize();
});
it('show explaination message', function() {
expect($('.subtitles li')).toHaveHtml("Caption will be displayed when you start playing the video.");
});
it('does not set rendered to true', function() {
expect(videoCaption.rendered).toBeFalsy();
});
});
describe('when no captions file was specified', function () {
beforeEach(function () {
loadFixtures('video_all.html');
// Unspecify the captions file.
$('#example').find('#video_id').data('sub', '');
state = new Video('#example');
videoCaption = state.videoCaption;
});
it('captions panel is not shown', function () {
expect(videoCaption.hideSubtitlesEl).toBeHidden();
});
});
});
describe('mouse movement', function() {
// We will store default window.setTimeout() function here.
var oldSetTimeout = null;
beforeEach(function() {
// Store original window.setTimeout() function. If we do not do this, then
// all other tests that rely on code which uses window.setTimeout()
// function might (and probably will) fail.
oldSetTimeout = window.setTimeout;
// Redefine window.setTimeout() function as a spy.
window.setTimeout = jasmine.createSpy().andCallFake(function(callback, timeout) { return 5; })
window.setTimeout.andReturn(100);
spyOn(window, 'clearTimeout');
});
afterEach(function () {
// Reset the default window.setTimeout() function. If we do not do this,
// then all other tests that rely on code which uses window.setTimeout()
// function might (and probably will) fail.
window.setTimeout = oldSetTimeout;
});
describe('when cursor is outside of the caption box', function() {
beforeEach(function() {
$(window).trigger(jQuery.Event('mousemove'));
});
it('does not set freezing timeout', function() {
expect(videoCaption.frozen).toBeFalsy();
});
});
describe('when cursor is in the caption box', function() {
beforeEach(function() {
$('.subtitles').trigger(jQuery.Event('mouseenter'));
});
it('set the freezing timeout', function() {
expect(videoCaption.frozen).toEqual(100);
});
describe('when the cursor is moving', function() {
beforeEach(function() {
$('.subtitles').trigger(jQuery.Event('mousemove'));
});
it('reset the freezing timeout', function() {
expect(window.clearTimeout).toHaveBeenCalledWith(100);
});
});
describe('when the mouse is scrolling', function() {
beforeEach(function() {
$('.subtitles').trigger(jQuery.Event('mousewheel'));
});
it('reset the freezing timeout', function() {
expect(window.clearTimeout).toHaveBeenCalledWith(100);
});
});
});
describe('when cursor is moving out of the caption box', function() {
beforeEach(function() {
videoCaption.frozen = 100;
$.fn.scrollTo.reset();
});
describe('always', function() {
beforeEach(function() {
$('.subtitles').trigger(jQuery.Event('mouseout'));
});
it('reset the freezing timeout', function() {
expect(window.clearTimeout).toHaveBeenCalledWith(100);
});
it('unfreeze the caption', function() {
expect(videoCaption.frozen).toBeNull();
});
});
describe('when the player is playing', function() {
beforeEach(function() {
videoCaption.playing = true;
$('.subtitles li[data-index]:first').addClass('current');
$('.subtitles').trigger(jQuery.Event('mouseout'));
});
it('scroll the caption', function() {
expect($.fn.scrollTo).toHaveBeenCalled();
});
});
describe('when the player is not playing', function() {
beforeEach(function() {
videoCaption.playing = false;
$('.subtitles').trigger(jQuery.Event('mouseout'));
});
it('does not scroll the caption', function() {
expect($.fn.scrollTo).not.toHaveBeenCalled();
});
});
});
});
describe('search', function() {
it('return a correct caption index', function() {
expect(videoCaption.search(0)).toEqual(-1);
expect(videoCaption.search(3120)).toEqual(1);
expect(videoCaption.search(6270)).toEqual(2);
expect(videoCaption.search(8490)).toEqual(2);
expect(videoCaption.search(21620)).toEqual(4);
expect(videoCaption.search(24920)).toEqual(5);
});
});
describe('play', function() {
describe('when the caption was not rendered', function() {
beforeEach(function() {
window.onTouchBasedDevice.andReturn(true);
initialize();
videoCaption.play();
});
it('render the caption', function() {
var captionsData;
captionsData = jasmine.stubbedCaption;
$('.subtitles li[data-index]').each(function(index, link) {
expect($(link)).toHaveData('index', index);
expect($(link)).toHaveData('start', captionsData.start[index]);
expect($(link)).toHaveAttr('tabindex', 0);
expect($(link)).toHaveText(captionsData.text[index]);
});
});
it('add a padding element to caption', function() {
expect($('.subtitles li:first')).toBe('.spacing');
expect($('.subtitles li:last')).toBe('.spacing');
});
it('bind all the caption link', function() {
$('.subtitles li[data-index]').each(function(index, link) {
expect($(link)).toHandleWith('mouseover', videoCaption.captionMouseOverOut);
expect($(link)).toHandleWith('mouseout', videoCaption.captionMouseOverOut);
expect($(link)).toHandleWith('mousedown', videoCaption.captionMouseDown);
expect($(link)).toHandleWith('click', videoCaption.captionClick);
expect($(link)).toHandleWith('focus', videoCaption.captionFocus);
expect($(link)).toHandleWith('blur', videoCaption.captionBlur);
expect($(link)).toHandleWith('keydown', videoCaption.captionKeyDown);
});
});
it('set rendered to true', function() {
expect(videoCaption.rendered).toBeTruthy();
});
it('set playing to true', function() {
expect(videoCaption.playing).toBeTruthy();
});
});
});
describe('pause', function() {
beforeEach(function() {
videoCaption.playing = true;
videoCaption.pause();
});
it('set playing to false', function() {
expect(videoCaption.playing).toBeFalsy();
});
});
describe('updatePlayTime', function() {
describe('when the video speed is 1.0x', function() {
beforeEach(function() {
videoSpeedControl.currentSpeed = '1.0';
videoCaption.updatePlayTime(25.000);
});
it('search the caption based on time', function() {
expect(videoCaption.currentIndex).toEqual(5);
});
});
describe('when the video speed is not 1.0x', function() {
beforeEach(function() {
videoSpeedControl.currentSpeed = '0.75';
videoCaption.updatePlayTime(25.000);
});
it('search the caption based on 1.0x speed', function() {
expect(videoCaption.currentIndex).toEqual(5);
});
});
describe('when the index is not the same', function() {
beforeEach(function() {
videoCaption.currentIndex = 1;
$('.subtitles li[data-index=5]').addClass('current');
videoCaption.updatePlayTime(25.000);
});
it('deactivate the previous caption', function() {
expect($('.subtitles li[data-index=1]')).not.toHaveClass('current');
});
it('activate new caption', function() {
expect($('.subtitles li[data-index=5]')).toHaveClass('current');
});
it('save new index', function() {
expect(videoCaption.currentIndex).toEqual(5);
});
it('scroll caption to new position', function() {
expect($.fn.scrollTo).toHaveBeenCalled();
});
});
describe('when the index is the same', function() {
beforeEach(function() {
videoCaption.currentIndex = 1;
$('.subtitles li[data-index=3]').addClass('current');
videoCaption.updatePlayTime(15.000);
});
it('does not change current subtitle', function() {
expect($('.subtitles li[data-index=3]')).toHaveClass('current');
});
});
});
describe('resize', function() {
beforeEach(function() {
initialize();
$('.subtitles li[data-index=1]').addClass('current');
videoCaption.resize();
});
describe('set the height of caption container', function(){
// Temporarily disabled due to intermittent failures
// with error "Expected 745 to be close to 805, 2." in Firefox
xit('when CC button is enabled', function() {
var realHeight = parseInt($('.subtitles').css('maxHeight'), 10),
shouldBeHeight = $('.video-wrapper').height();
// Because of some problems with rounding on different enviroments:
// Linux * Mac * FF * Chrome
expect(realHeight).toBeCloseTo(shouldBeHeight, 2);
});
it('when CC button is disabled ', function() {
var realHeight, videoWrapperHeight, progressSliderHeight,
controlHeight, shouldBeHeight;
state.captionsHidden = true;
videoCaption.setSubtitlesHeight();
realHeight = parseInt($('.subtitles').css('maxHeight'), 10);
videoWrapperHeight = $('.video-wrapper').height();
progressSliderHeight = videoControl.sliderEl.height();
controlHeight = videoControl.el.height();
shouldBeHeight = videoWrapperHeight -
0.5 * progressSliderHeight -
controlHeight;
expect(realHeight).toBe(shouldBeHeight);
});
});
it('set the height of caption spacing', function() {
var firstSpacing, lastSpacing;
firstSpacing = Math.abs(parseInt($('.subtitles .spacing:first').css('height'), 10));
lastSpacing = Math.abs(parseInt($('.subtitles .spacing:last').css('height'), 10));
expect(firstSpacing - videoCaption.topSpacingHeight()).toBeLessThan(1);
expect(lastSpacing - videoCaption.bottomSpacingHeight()).toBeLessThan(1);
});
it('scroll caption to new position', function() {
expect($.fn.scrollTo).toHaveBeenCalled();
});
});
describe('scrollCaption', function() {
beforeEach(function() {
initialize();
});
describe('when frozen', function() {
beforeEach(function() {
videoCaption.frozen = true;
$('.subtitles li[data-index=1]').addClass('current');
videoCaption.scrollCaption();
});
it('does not scroll the caption', function() {
expect($.fn.scrollTo).not.toHaveBeenCalled();
});
});
describe('when not frozen', function() {
beforeEach(function() {
videoCaption.frozen = false;
});
describe('when there is no current caption', function() {
beforeEach(function() {
videoCaption.scrollCaption();
});
it('does not scroll the caption', function() {
expect($.fn.scrollTo).not.toHaveBeenCalled();
});
});
describe('when there is a current caption', function() {
beforeEach(function() {
$('.subtitles li[data-index=1]').addClass('current');
videoCaption.scrollCaption();
});
it('scroll to current caption', function() {
expect($.fn.scrollTo).toHaveBeenCalled();
});
});
});
});
describe('seekPlayer', function() {
describe('when the video speed is 1.0x', function() {
beforeEach(function() {
videoSpeedControl.currentSpeed = '1.0';
$('.subtitles li[data-start="14910"]').trigger('click');
});
// Temporarily disabled due to intermittent failures
// Fails with error: "InvalidStateError: An attempt was made to
// use an object that is not, or is no longer, usable
// Expected 0 to equal 14.91."
// on Firefox
xit('trigger seek event with the correct time', function() {
expect(videoPlayer.currentTime).toEqual(14.91);
});
});
describe('when the video speed is not 1.0x', function() {
beforeEach(function() {
initialize();
videoSpeedControl.currentSpeed = '0.75';
$('.subtitles li[data-start="14910"]').trigger('click');
});
it('trigger seek event with the correct time', function() {
expect(videoPlayer.currentTime).toEqual(14.91);
});
});
describe('when the player type is Flash at speed 0.75x', function () {
beforeEach(function () { beforeEach(function () {
oldOTBD = window.onTouchBasedDevice;
window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice')
.andReturn(false);
initialize(); initialize();
videoSpeedControl.currentSpeed = '0.75';
state.currentPlayerMode = 'flash';
$('.subtitles li[data-start="14910"]').trigger('click');
});
it('trigger seek event with the correct time', function () {
expect(videoPlayer.currentTime).toEqual(15);
});
});
});
describe('toggle', function() {
beforeEach(function() {
initialize();
spyOn(videoPlayer, 'log');
$('.subtitles li[data-index=1]').addClass('current');
});
describe('when the caption is visible', function() {
beforeEach(function() {
state.el.removeClass('closed');
videoCaption.toggle(jQuery.Event('click'));
});
it('log the hide_transcript event', function() {
expect(videoPlayer.log).toHaveBeenCalledWith('hide_transcript', {
currentTime: videoPlayer.currentTime
});
});
it('hide the caption', function() {
expect(state.el).toHaveClass('closed');
});
});
describe('when the caption is hidden', function() {
beforeEach(function() {
state.el.addClass('closed');
videoCaption.toggle(jQuery.Event('click'));
});
it('log the show_transcript event', function() {
expect(videoPlayer.log).toHaveBeenCalledWith('show_transcript', {
currentTime: videoPlayer.currentTime
});
});
it('show the caption', function() {
expect(state.el).not.toHaveClass('closed');
});
it('scroll the caption', function() {
expect($.fn.scrollTo).toHaveBeenCalled();
});
});
});
describe('caption accessibility', function() {
beforeEach(function() {
initialize();
});
describe('when getting focus through TAB key', function() {
beforeEach(function() {
videoCaption.isMouseFocus = false;
$('.subtitles li[data-index=0]').trigger(jQuery.Event('focus'));
});
it('shows an outline around the caption', function() {
expect($('.subtitles li[data-index=0]')).toHaveClass('focused');
});
it('has automatic scrolling disabled', function() {
expect(videoCaption.autoScrolling).toBe(false);
});
});
describe('when loosing focus through TAB key', function() {
beforeEach(function() {
$('.subtitles li[data-index=0]').trigger(jQuery.Event('blur'));
});
it('does not show an outline around the caption', function() {
expect($('.subtitles li[data-index=0]')).not.toHaveClass('focused');
});
it('has automatic scrolling enabled', function() {
expect(videoCaption.autoScrolling).toBe(true);
});
});
describe('when same caption gets the focus through mouse after having focus through TAB key', function() {
beforeEach(function() {
videoCaption.isMouseFocus = false;
$('.subtitles li[data-index=0]').trigger(jQuery.Event('focus'));
$('.subtitles li[data-index=0]').trigger(jQuery.Event('mousedown'));
});
it('does not show an outline around it', function() {
expect($('.subtitles li[data-index=0]')).not.toHaveClass('focused');
});
it('has automatic scrolling enabled', function() {
expect(videoCaption.autoScrolling).toBe(true);
});
});
describe('when a second caption gets focus through mouse after first had focus through TAB key', function() {
beforeEach(function() {
videoCaption.isMouseFocus = false;
$('.subtitles li[data-index=0]').trigger(jQuery.Event('focus'));
$('.subtitles li[data-index=0]').trigger(jQuery.Event('blur'));
videoCaption.isMouseFocus = true;
$('.subtitles li[data-index=1]').trigger(jQuery.Event('mousedown'));
});
it('does not show an outline around the first', function() {
expect($('.subtitles li[data-index=0]')).not.toHaveClass('focused');
});
it('does not show an outline around the second', function() {
expect($('.subtitles li[data-index=1]')).not.toHaveClass('focused');
});
it('has automatic scrolling enabled', function() {
expect(videoCaption.autoScrolling).toBe(true);
});
});
xdescribe('when enter key is pressed on a caption', function() {
beforeEach(function() {
var e;
spyOn(videoCaption, 'seekPlayer').andCallThrough();
videoCaption.isMouseFocus = false;
$('.subtitles li[data-index=0]').trigger(jQuery.Event('focus'));
e = jQuery.Event('keydown');
e.which = 13; // ENTER key
$('.subtitles li[data-index=0]').trigger(e);
});
// Temporarily disabled due to intermittent failures
// Fails with error: "InvalidStateError: InvalidStateError: An attempt
// was made to use an object that is not, or is no longer, usable"
xit('shows an outline around it', function() {
expect($('.subtitles li[data-index=0]')).toHaveClass('focused');
}); });
xit('calls seekPlayer', function() { afterEach(function () {
expect(videoCaption.seekPlayer).toHaveBeenCalled(); YT.Player = undefined;
$.fn.scrollTo.reset();
$('.subtitles').remove();
$('source').remove();
window.onTouchBasedDevice = oldOTBD;
});
describe('constructor', function () {
describe('always', function () {
beforeEach(function () {
spyOn($, 'ajaxWithPrefix').andCallThrough();
initialize();
});
it('create the caption element', function () {
expect($('.video')).toContain('ol.subtitles');
});
it('add caption control to video player', function () {
expect($('.video')).toContain('a.hide-subtitles');
});
it('fetch the caption', function () {
waitsFor(function () {
if (videoCaption.loaded === true) {
return true;
}
return false;
}, 'Expect captions to be loaded.', 1000);
runs(function () {
expect($.ajaxWithPrefix).toHaveBeenCalledWith({
url: videoCaption.captionURL(),
notifyOnError: false,
success: jasmine.any(Function),
error: jasmine.any(Function)
});
});
});
it('bind window resize event', function () {
expect($(window)).toHandleWith(
'resize', videoCaption.resize
);
});
it('bind the hide caption button', function () {
expect($('.hide-subtitles')).toHandleWith(
'click', videoCaption.toggle
);
});
it('bind the mouse movement', function () {
expect($('.subtitles')).toHandleWith(
'mouseover', videoCaption.onMouseEnter
);
expect($('.subtitles')).toHandleWith(
'mouseout', videoCaption.onMouseLeave
);
expect($('.subtitles')).toHandleWith(
'mousemove', videoCaption.onMovement
);
expect($('.subtitles')).toHandleWith(
'mousewheel', videoCaption.onMovement
);
expect($('.subtitles')).toHandleWith(
'DOMMouseScroll', videoCaption.onMovement
);
});
it('bind the scroll', function () {
expect($('.subtitles'))
.toHandleWith('scroll', videoCaption.autoShowCaptions);
expect($('.subtitles'))
.toHandleWith('scroll', videoControl.showControls);
});
});
describe('when on a non touch-based device', function () {
beforeEach(function () {
initialize();
});
it('render the caption', function () {
var captionsData;
captionsData = jasmine.stubbedCaption;
$('.subtitles li[data-index]').each(
function (index, link) {
expect($(link)).toHaveData('index', index);
expect($(link)).toHaveData(
'start', captionsData.start[index]
);
expect($(link)).toHaveAttr('tabindex', 0);
expect($(link)).toHaveText(captionsData.text[index]);
});
});
it('add a padding element to caption', function () {
expect($('.subtitles li:first').hasClass('spacing'))
.toBe(true);
expect($('.subtitles li:last').hasClass('spacing'))
.toBe(true);
});
it('bind all the caption link', function () {
$('.subtitles li[data-index]').each(
function (index, link) {
expect($(link)).toHandleWith(
'mouseover', videoCaption.captionMouseOverOut
);
expect($(link)).toHandleWith(
'mouseout', videoCaption.captionMouseOverOut
);
expect($(link)).toHandleWith(
'mousedown', videoCaption.captionMouseDown
);
expect($(link)).toHandleWith(
'click', videoCaption.captionClick
);
expect($(link)).toHandleWith(
'focus', videoCaption.captionFocus
);
expect($(link)).toHandleWith(
'blur', videoCaption.captionBlur
);
expect($(link)).toHandleWith(
'keydown', videoCaption.captionKeyDown
);
});
});
it('set rendered to true', function () {
expect(videoCaption.rendered).toBeTruthy();
});
});
describe('when on a touch-based device', function () {
beforeEach(function () {
window.onTouchBasedDevice.andReturn(true);
initialize();
});
it('show explaination message', function () {
expect($('.subtitles li')).toHaveHtml(
'Caption will be displayed when you start playing ' +
'the video.'
);
});
it('does not set rendered to true', function () {
expect(videoCaption.rendered).toBeFalsy();
});
});
describe('when no captions file was specified', function () {
beforeEach(function () {
loadFixtures('video_all.html');
// Unspecify the captions file.
$('#example').find('#video_id').data('sub', '');
state = new Video('#example');
videoCaption = state.videoCaption;
});
it('captions panel is not shown', function () {
expect(videoCaption.hideSubtitlesEl).toBeHidden();
});
});
});
describe('mouse movement', function () {
// We will store default window.setTimeout() function here.
var oldSetTimeout = null;
beforeEach(function () {
// Store original window.setTimeout() function. If we do not do
// this, then all other tests that rely on code which uses
// window.setTimeout() function might (and probably will) fail.
oldSetTimeout = window.setTimeout;
// Redefine window.setTimeout() function as a spy.
window.setTimeout = jasmine.createSpy().andCallFake(
function (callback, timeout) {
return 5;
}
);
window.setTimeout.andReturn(100);
spyOn(window, 'clearTimeout');
});
afterEach(function () {
// Reset the default window.setTimeout() function. If we do not
// do this, then all other tests that rely on code which uses
// window.setTimeout() function might (and probably will) fail.
window.setTimeout = oldSetTimeout;
});
describe('when cursor is outside of the caption box', function () {
beforeEach(function () {
$(window).trigger(jQuery.Event('mousemove'));
});
it('does not set freezing timeout', function () {
expect(videoCaption.frozen).toBeFalsy();
});
});
describe('when cursor is in the caption box', function () {
beforeEach(function () {
$('.subtitles').trigger(jQuery.Event('mouseenter'));
});
it('set the freezing timeout', function () {
expect(videoCaption.frozen).toEqual(100);
});
describe('when the cursor is moving', function () {
beforeEach(function () {
$('.subtitles').trigger(jQuery.Event('mousemove'));
});
it('reset the freezing timeout', function () {
expect(window.clearTimeout).toHaveBeenCalledWith(100);
});
});
describe('when the mouse is scrolling', function () {
beforeEach(function () {
$('.subtitles').trigger(jQuery.Event('mousewheel'));
});
it('reset the freezing timeout', function () {
expect(window.clearTimeout).toHaveBeenCalledWith(100);
});
});
});
describe(
'when cursor is moving out of the caption box',
function () {
beforeEach(function () {
videoCaption.frozen = 100;
$.fn.scrollTo.reset();
});
describe('always', function () {
beforeEach(function () {
$('.subtitles').trigger(jQuery.Event('mouseout'));
});
it('reset the freezing timeout', function () {
expect(window.clearTimeout).toHaveBeenCalledWith(100);
});
it('unfreeze the caption', function () {
expect(videoCaption.frozen).toBeNull();
});
});
describe('when the player is playing', function () {
beforeEach(function () {
videoCaption.playing = true;
$('.subtitles li[data-index]:first')
.addClass('current');
$('.subtitles').trigger(jQuery.Event('mouseout'));
});
it('scroll the caption', function () {
expect($.fn.scrollTo).toHaveBeenCalled();
});
});
describe('when the player is not playing', function () {
beforeEach(function () {
videoCaption.playing = false;
$('.subtitles').trigger(jQuery.Event('mouseout'));
});
it('does not scroll the caption', function () {
expect($.fn.scrollTo).not.toHaveBeenCalled();
});
});
});
});
describe('search', function () {
it('return a correct caption index', function () {
expect(videoCaption.search(0)).toEqual(-1);
expect(videoCaption.search(3120)).toEqual(1);
expect(videoCaption.search(6270)).toEqual(2);
expect(videoCaption.search(8490)).toEqual(2);
expect(videoCaption.search(21620)).toEqual(4);
expect(videoCaption.search(24920)).toEqual(5);
});
});
describe('play', function () {
describe('when the caption was not rendered', function () {
beforeEach(function () {
window.onTouchBasedDevice.andReturn(true);
initialize();
videoCaption.play();
});
it('render the caption', function () {
var captionsData;
captionsData = jasmine.stubbedCaption;
$('.subtitles li[data-index]').each(
function (index, link) {
expect($(link)).toHaveData('index', index);
expect($(link)).toHaveData(
'start', captionsData.start[index]
);
expect($(link)).toHaveAttr('tabindex', 0);
expect($(link)).toHaveText(captionsData.text[index]);
});
});
it('add a padding element to caption', function () {
expect($('.subtitles li:first')).toBe('.spacing');
expect($('.subtitles li:last')).toBe('.spacing');
});
it('bind all the caption link', function () {
$('.subtitles li[data-index]').each(
function (index, link) {
expect($(link)).toHandleWith(
'mouseover', videoCaption.captionMouseOverOut
);
expect($(link)).toHandleWith(
'mouseout', videoCaption.captionMouseOverOut
);
expect($(link)).toHandleWith(
'mousedown', videoCaption.captionMouseDown
);
expect($(link)).toHandleWith(
'click', videoCaption.captionClick
);
expect($(link)).toHandleWith(
'focus', videoCaption.captionFocus
);
expect($(link)).toHandleWith(
'blur', videoCaption.captionBlur
);
expect($(link)).toHandleWith(
'keydown', videoCaption.captionKeyDown
);
});
});
it('set rendered to true', function () {
expect(videoCaption.rendered).toBeTruthy();
});
it('set playing to true', function () {
expect(videoCaption.playing).toBeTruthy();
});
});
});
describe('pause', function () {
beforeEach(function () {
videoCaption.playing = true;
videoCaption.pause();
});
it('set playing to false', function () {
expect(videoCaption.playing).toBeFalsy();
});
});
describe('updatePlayTime', function () {
describe('when the video speed is 1.0x', function () {
beforeEach(function () {
videoSpeedControl.currentSpeed = '1.0';
videoCaption.updatePlayTime(25.000);
});
it('search the caption based on time', function () {
expect(videoCaption.currentIndex).toEqual(5);
});
});
describe('when the video speed is not 1.0x', function () {
beforeEach(function () {
videoSpeedControl.currentSpeed = '0.75';
videoCaption.updatePlayTime(25.000);
});
it('search the caption based on 1.0x speed', function () {
expect(videoCaption.currentIndex).toEqual(5);
});
});
describe('when the index is not the same', function () {
beforeEach(function () {
videoCaption.currentIndex = 1;
$('.subtitles li[data-index=5]').addClass('current');
videoCaption.updatePlayTime(25.000);
});
it('deactivate the previous caption', function () {
expect($('.subtitles li[data-index=1]'))
.not.toHaveClass('current');
});
it('activate new caption', function () {
expect($('.subtitles li[data-index=5]'))
.toHaveClass('current');
});
it('save new index', function () {
expect(videoCaption.currentIndex).toEqual(5);
});
it('scroll caption to new position', function () {
expect($.fn.scrollTo).toHaveBeenCalled();
});
});
describe('when the index is the same', function () {
beforeEach(function () {
videoCaption.currentIndex = 1;
$('.subtitles li[data-index=3]').addClass('current');
videoCaption.updatePlayTime(15.000);
});
it('does not change current subtitle', function () {
expect($('.subtitles li[data-index=3]'))
.toHaveClass('current');
});
});
});
describe('resize', function () {
beforeEach(function () {
initialize();
$('.subtitles li[data-index=1]').addClass('current');
videoCaption.resize();
});
describe('set the height of caption container', function () {
// Temporarily disabled due to intermittent failures
// with error "Expected 745 to be close to 805, 2." in Firefox
xit('when CC button is enabled', function () {
var realHeight = parseInt(
$('.subtitles').css('maxHeight'), 10
),
shouldBeHeight = $('.video-wrapper').height();
// Because of some problems with rounding on different
// environments: Linux * Mac * FF * Chrome
expect(realHeight).toBeCloseTo(shouldBeHeight, 2);
});
it('when CC button is disabled ', function () {
var realHeight, videoWrapperHeight, progressSliderHeight,
controlHeight, shouldBeHeight;
state.captionsHidden = true;
videoCaption.setSubtitlesHeight();
realHeight = parseInt(
$('.subtitles').css('maxHeight'), 10
);
videoWrapperHeight = $('.video-wrapper').height();
progressSliderHeight = videoControl.sliderEl.height();
controlHeight = videoControl.el.height();
shouldBeHeight = videoWrapperHeight -
0.5 * progressSliderHeight -
controlHeight;
expect(realHeight).toBe(shouldBeHeight);
});
});
it('set the height of caption spacing', function () {
var firstSpacing, lastSpacing;
firstSpacing = Math.abs(parseInt(
$('.subtitles .spacing:first').css('height'), 10
));
lastSpacing = Math.abs(parseInt(
$('.subtitles .spacing:last').css('height'), 10
));
expect(firstSpacing - videoCaption.topSpacingHeight())
.toBeLessThan(1);
expect(lastSpacing - videoCaption.bottomSpacingHeight())
.toBeLessThan(1);
});
it('scroll caption to new position', function () {
expect($.fn.scrollTo).toHaveBeenCalled();
});
});
describe('scrollCaption', function () {
beforeEach(function () {
initialize();
});
describe('when frozen', function () {
beforeEach(function () {
videoCaption.frozen = true;
$('.subtitles li[data-index=1]').addClass('current');
videoCaption.scrollCaption();
});
it('does not scroll the caption', function () {
expect($.fn.scrollTo).not.toHaveBeenCalled();
});
});
describe('when not frozen', function () {
beforeEach(function () {
videoCaption.frozen = false;
});
describe('when there is no current caption', function () {
beforeEach(function () {
videoCaption.scrollCaption();
});
it('does not scroll the caption', function () {
expect($.fn.scrollTo).not.toHaveBeenCalled();
});
});
describe('when there is a current caption', function () {
beforeEach(function () {
$('.subtitles li[data-index=1]').addClass('current');
videoCaption.scrollCaption();
});
it('scroll to current caption', function () {
expect($.fn.scrollTo).toHaveBeenCalled();
});
});
});
});
describe('seekPlayer', function () {
describe('when the video speed is 1.0x', function () {
beforeEach(function () {
videoSpeedControl.currentSpeed = '1.0';
$('.subtitles li[data-start="14910"]').trigger('click');
});
// Temporarily disabled due to intermittent failures
// Fails with error: "InvalidStateError: An attempt was made to
// use an object that is not, or is no longer, usable
// Expected 0 to equal 14.91."
// on Firefox
xit('trigger seek event with the correct time', function () {
expect(videoPlayer.currentTime).toEqual(14.91);
});
});
describe('when the video speed is not 1.0x', function () {
beforeEach(function () {
initialize();
videoSpeedControl.currentSpeed = '0.75';
$('.subtitles li[data-start="14910"]').trigger('click');
});
it('trigger seek event with the correct time', function () {
expect(videoPlayer.currentTime).toEqual(14.91);
});
});
describe('when the player type is Flash at speed 0.75x',
function () {
beforeEach(function () {
initialize();
videoSpeedControl.currentSpeed = '0.75';
state.currentPlayerMode = 'flash';
$('.subtitles li[data-start="14910"]').trigger('click');
});
it('trigger seek event with the correct time', function () {
expect(videoPlayer.currentTime).toEqual(15);
});
});
});
describe('toggle', function () {
beforeEach(function () {
initialize();
spyOn(videoPlayer, 'log');
$('.subtitles li[data-index=1]').addClass('current');
});
describe('when the caption is visible', function () {
beforeEach(function () {
state.el.removeClass('closed');
videoCaption.toggle(jQuery.Event('click'));
});
it('log the hide_transcript event', function () {
expect(videoPlayer.log).toHaveBeenCalledWith(
'hide_transcript',
{
currentTime: videoPlayer.currentTime
}
);
});
it('hide the caption', function () {
expect(state.el).toHaveClass('closed');
});
});
describe('when the caption is hidden', function () {
beforeEach(function () {
state.el.addClass('closed');
videoCaption.toggle(jQuery.Event('click'));
});
it('log the show_transcript event', function () {
expect(videoPlayer.log).toHaveBeenCalledWith(
'show_transcript',
{
currentTime: videoPlayer.currentTime
}
);
});
it('show the caption', function () {
expect(state.el).not.toHaveClass('closed');
});
it('scroll the caption', function () {
expect($.fn.scrollTo).toHaveBeenCalled();
});
});
});
describe('caption accessibility', function () {
beforeEach(function () {
initialize();
});
describe('when getting focus through TAB key', function () {
beforeEach(function () {
videoCaption.isMouseFocus = false;
$('.subtitles li[data-index=0]').trigger(
jQuery.Event('focus')
);
});
it('shows an outline around the caption', function () {
expect($('.subtitles li[data-index=0]'))
.toHaveClass('focused');
});
it('has automatic scrolling disabled', function () {
expect(videoCaption.autoScrolling).toBe(false);
});
});
describe('when loosing focus through TAB key', function () {
beforeEach(function () {
$('.subtitles li[data-index=0]').trigger(
jQuery.Event('blur')
);
});
it('does not show an outline around the caption', function () {
expect($('.subtitles li[data-index=0]'))
.not.toHaveClass('focused');
});
it('has automatic scrolling enabled', function () {
expect(videoCaption.autoScrolling).toBe(true);
});
});
describe(
'when same caption gets the focus through mouse after ' +
'having focus through TAB key',
function () {
beforeEach(function () {
videoCaption.isMouseFocus = false;
$('.subtitles li[data-index=0]')
.trigger(jQuery.Event('focus'));
$('.subtitles li[data-index=0]')
.trigger(jQuery.Event('mousedown'));
});
it('does not show an outline around it', function () {
expect($('.subtitles li[data-index=0]'))
.not.toHaveClass('focused');
});
it('has automatic scrolling enabled', function () {
expect(videoCaption.autoScrolling).toBe(true);
});
});
describe(
'when a second caption gets focus through mouse after ' +
'first had focus through TAB key',
function () {
var subDataLiIdx__0, subDataLiIdx__1;
beforeEach(function () {
subDataLiIdx__0 = $('.subtitles li[data-index=0]');
subDataLiIdx__1 = $('.subtitles li[data-index=1]');
videoCaption.isMouseFocus = false;
subDataLiIdx__0.trigger(jQuery.Event('focus'));
subDataLiIdx__0.trigger(jQuery.Event('blur'));
videoCaption.isMouseFocus = true;
subDataLiIdx__1.trigger(jQuery.Event('mousedown'));
});
it('does not show an outline around the first', function () {
expect(subDataLiIdx__0).not.toHaveClass('focused');
});
it('does not show an outline around the second', function () {
expect(subDataLiIdx__1).not.toHaveClass('focused');
});
it('has automatic scrolling enabled', function () {
expect(videoCaption.autoScrolling).toBe(true);
});
});
xdescribe('when enter key is pressed on a caption', function () {
var subDataLiIdx__0;
beforeEach(function () {
var e;
subDataLiIdx__0 = $('.subtitles li[data-index=0]');
spyOn(videoCaption, 'seekPlayer').andCallThrough();
videoCaption.isMouseFocus = false;
subDataLiIdx__0.trigger(jQuery.Event('focus'));
e = jQuery.Event('keydown');
e.which = 13; // ENTER key
subDataLiIdx__0.trigger(e);
});
// Temporarily disabled due to intermittent failures.
//
// Fails with error: "InvalidStateError: InvalidStateError: An
// attempt was made to use an object that is not, or is no
// longer, usable".
xit('shows an outline around it', function () {
expect(subDataLiIdx__0).toHaveClass('focused');
});
xit('calls seekPlayer', function () {
expect(videoCaption.seekPlayer).toHaveBeenCalled();
});
});
}); });
});
}); });
});
}).call(this); }).call(this);
...@@ -264,15 +264,21 @@ function (VideoPlayer) { ...@@ -264,15 +264,21 @@ function (VideoPlayer) {
// The function set initial configuration and preparation. // The function set initial configuration and preparation.
function initialize(element) { function initialize(element) {
var _this = this, tempYtTestTimeout; var _this = this,
regExp = /^true$/i,
data, tempYtTestTimeout;
// This is used in places where we instead would have to check if an // This is used in places where we instead would have to check if an
// element has a CSS class 'fullscreen'. // element has a CSS class 'fullscreen'.
this.isFullScreen = false; this.isFullScreen = false;
// The parent element of the video, and the ID. // The parent element of the video, and the ID.
this.el = $(element).find('.video'); this.el = $(element).find('.video');
this.elVideoWrapper = this.el.find('.video-wrapper');
this.id = this.el.attr('id').replace(/video_/, ''); this.id = this.el.attr('id').replace(/video_/, '');
// jQuery .data() return object with keys in lower camelCase format.
data = this.el.data();
console.log( console.log(
'[Video info]: Initializing video with id "' + this.id + '".' '[Video info]: Initializing video with id "' + this.id + '".'
); );
...@@ -283,32 +289,26 @@ function (VideoPlayer) { ...@@ -283,32 +289,26 @@ function (VideoPlayer) {
this.config = { this.config = {
element: element, element: element,
start: this.el.data('start'), start: data['start'],
end: this.el.data('end'), end: data['end'],
caption_data_dir: data['captionDataDir'],
caption_data_dir: this.el.data('caption-data-dir'), caption_asset_path: data['captionAssetPath'],
caption_asset_path: this.el.data('caption-asset-path'), show_captions: regExp.test(data['showCaptions'].toString()),
show_captions: ( youtubeStreams: data['streams'],
this.el.data('show-captions') autohideHtml5: regExp.test(data['autohideHtml5'].toString()),
.toString().toLowerCase() === 'true' sub: data['sub'],
), mp4Source: data['mp4Source'],
youtubeStreams: this.el.data('streams'), webmSource: data['webmSource'],
oggSource: data['oggSource'],
sub: this.el.data('sub'), ytTestUrl: data['ytTestUrl'],
mp4Source: this.el.data('mp4-source'),
webmSource: this.el.data('webm-source'),
oggSource: this.el.data('ogg-source'),
ytTestUrl: this.el.data('yt-test-url'),
fadeOutTimeout: 1400, fadeOutTimeout: 1400,
captionsFreezeTime: 10000,
availableQualities: ['hd720', 'hd1080', 'highres'] availableQualities: ['hd720', 'hd1080', 'highres']
}; };
// Check if the YT test timeout has been set. If not, or it is in // Check if the YT test timeout has been set. If not, or it is in
// improper format, then set to default value. // improper format, then set to default value.
tempYtTestTimeout = parseInt(this.el.data('yt-test-timeout'), 10); tempYtTestTimeout = parseInt(data['ytTestTimeout'], 10);
if (!isFinite(tempYtTestTimeout)) { if (!isFinite(tempYtTestTimeout)) {
tempYtTestTimeout = 1500; tempYtTestTimeout = 1500;
} }
......
...@@ -57,7 +57,7 @@ function () { ...@@ -57,7 +57,7 @@ function () {
state.videoControl.play(); state.videoControl.play();
} }
if (state.videoType === 'html5') { if ((state.videoType === 'html5') && (state.config.autohideHtml5)) {
state.videoControl.fadeOutTimeout = state.config.fadeOutTimeout; state.videoControl.fadeOutTimeout = state.config.fadeOutTimeout;
state.videoControl.el.addClass('html5'); state.videoControl.el.addClass('html5');
...@@ -81,7 +81,7 @@ function () { ...@@ -81,7 +81,7 @@ function () {
state.videoControl.fullScreenEl.on('click', state.videoControl.toggleFullScreen); state.videoControl.fullScreenEl.on('click', state.videoControl.toggleFullScreen);
$(document).on('keyup', state.videoControl.exitFullScreen); $(document).on('keyup', state.videoControl.exitFullScreen);
if (state.videoType === 'html5') { if ((state.videoType === 'html5') && (state.config.autohideHtml5)) {
state.el.on('mousemove', state.videoControl.showControls); state.el.on('mousemove', state.videoControl.showControls);
state.el.on('keydown', state.videoControl.showControls); state.el.on('keydown', state.videoControl.showControls);
} }
......
...@@ -34,46 +34,63 @@ function () { ...@@ -34,46 +34,63 @@ function () {
// function _makeFunctionsPublic(state) // function _makeFunctionsPublic(state)
// //
// Functions which will be accessible via 'state' object. When called, these functions will // Functions which will be accessible via 'state' object. When called,
// get the 'state' object as a context. // these functions will get the 'state' object as a context.
function _makeFunctionsPublic(state) { function _makeFunctionsPublic(state) {
state.videoCaption.autoShowCaptions = _.bind(autoShowCaptions, state); state.videoCaption.autoShowCaptions = _.bind(
state.videoCaption.autoHideCaptions = _.bind(autoHideCaptions, state); autoShowCaptions, state
state.videoCaption.resize = _.bind(resize, state); );
state.videoCaption.toggle = _.bind(toggle, state); state.videoCaption.autoHideCaptions = _.bind(
state.videoCaption.onMouseEnter = _.bind(onMouseEnter, state); autoHideCaptions, state
state.videoCaption.onMouseLeave = _.bind(onMouseLeave, state); );
state.videoCaption.onMovement = _.bind(onMovement, state); state.videoCaption.resize = _.bind(resize, state);
state.videoCaption.renderCaption = _.bind(renderCaption, state); state.videoCaption.toggle = _.bind(toggle, state);
state.videoCaption.captionHeight = _.bind(captionHeight, state); state.videoCaption.onMouseEnter = _.bind(onMouseEnter, state);
state.videoCaption.topSpacingHeight = _.bind(topSpacingHeight, state); state.videoCaption.onMouseLeave = _.bind(onMouseLeave, state);
state.videoCaption.bottomSpacingHeight = _.bind(bottomSpacingHeight, state); state.videoCaption.onMovement = _.bind(onMovement, state);
state.videoCaption.scrollCaption = _.bind(scrollCaption, state); state.videoCaption.renderCaption = _.bind(renderCaption, state);
state.videoCaption.search = _.bind(search, state); state.videoCaption.captionHeight = _.bind(captionHeight, state);
state.videoCaption.play = _.bind(play, state); state.videoCaption.topSpacingHeight = _.bind(
state.videoCaption.pause = _.bind(pause, state); topSpacingHeight, state
state.videoCaption.seekPlayer = _.bind(seekPlayer, state); );
state.videoCaption.hideCaptions = _.bind(hideCaptions, state); state.videoCaption.bottomSpacingHeight = _.bind(
state.videoCaption.calculateOffset = _.bind(calculateOffset, state); bottomSpacingHeight, state
state.videoCaption.updatePlayTime = _.bind(updatePlayTime, state); );
state.videoCaption.setSubtitlesHeight = _.bind(setSubtitlesHeight, state); state.videoCaption.scrollCaption = _.bind(scrollCaption, state);
state.videoCaption.search = _.bind(search, state);
state.videoCaption.renderElements = _.bind(renderElements, state); state.videoCaption.play = _.bind(play, state);
state.videoCaption.bindHandlers = _.bind(bindHandlers, state); state.videoCaption.pause = _.bind(pause, state);
state.videoCaption.fetchCaption = _.bind(fetchCaption, state); state.videoCaption.seekPlayer = _.bind(seekPlayer, state);
state.videoCaption.captionURL = _.bind(captionURL, state); state.videoCaption.hideCaptions = _.bind(hideCaptions, state);
state.videoCaption.captionMouseOverOut = _.bind(captionMouseOverOut, state); state.videoCaption.calculateOffset = _.bind(
state.videoCaption.captionMouseDown = _.bind(captionMouseDown, state); calculateOffset, state
state.videoCaption.captionClick = _.bind(captionClick, state); );
state.videoCaption.captionFocus = _.bind(captionFocus, state); state.videoCaption.updatePlayTime = _.bind(updatePlayTime, state);
state.videoCaption.captionBlur = _.bind(captionBlur, state); state.videoCaption.setSubtitlesHeight = _.bind(
state.videoCaption.captionKeyDown = _.bind(captionKeyDown, state); setSubtitlesHeight, state
);
state.videoCaption.renderElements = _.bind(renderElements, state);
state.videoCaption.bindHandlers = _.bind(bindHandlers, state);
state.videoCaption.fetchCaption = _.bind(fetchCaption, state);
state.videoCaption.captionURL = _.bind(captionURL, state);
state.videoCaption.captionMouseOverOut = _.bind(
captionMouseOverOut, state
);
state.videoCaption.captionMouseDown = _.bind(
captionMouseDown, state
);
state.videoCaption.captionClick = _.bind(captionClick, state);
state.videoCaption.captionFocus = _.bind(captionFocus, state);
state.videoCaption.captionBlur = _.bind(captionBlur, state);
state.videoCaption.captionKeyDown = _.bind(captionKeyDown, state);
} }
// *************************************************************** // ***************************************************************
// Public functions start here. // Public functions start here.
// These are available via the 'state' object. Their context ('this' keyword) is the 'state' object. // These are available via the 'state' object. Their context ('this'
// The magic private function that makes them available and sets up their context is makeFunctionsPublic(). // keyword) is the 'state' object. The magic private function that makes
// them available and sets up their context is makeFunctionsPublic().
// *************************************************************** // ***************************************************************
/** /**
...@@ -109,10 +126,13 @@ function () { ...@@ -109,10 +126,13 @@ function () {
// function bindHandlers() // function bindHandlers()
// //
// Bind any necessary function callbacks to DOM events (click, mousemove, etc.). // Bind any necessary function callbacks to DOM events (click,
// mousemove, etc.).
function bindHandlers() { function bindHandlers() {
$(window).bind('resize', this.videoCaption.resize); $(window).bind('resize', this.videoCaption.resize);
this.videoCaption.hideSubtitlesEl.on('click', this.videoCaption.toggle); this.videoCaption.hideSubtitlesEl.on(
'click', this.videoCaption.toggle
);
this.videoCaption.subtitlesEl this.videoCaption.subtitlesEl
.on( .on(
...@@ -132,14 +152,41 @@ function () { ...@@ -132,14 +152,41 @@ function () {
this.videoCaption.onMovement this.videoCaption.onMovement
); );
if (this.videoType === 'html5') { if ((this.videoType === 'html5') && (this.config.autohideHtml5)) {
this.el.on('mousemove', this.videoCaption.autoShowCaptions); this.el.on({
this.el.on('keydown', this.videoCaption.autoShowCaptions); mousemove: this.videoCaption.autoShowCaptions,
keydown: this.videoCaption.autoShowCaptions
});
// Moving slider on subtitles is not a mouse move, // Moving slider on subtitles is not a mouse move, but captions and
// but captions and controls should be showed. // controls should be shown.
this.videoCaption.subtitlesEl.on('scroll', this.videoCaption.autoShowCaptions); this.videoCaption.subtitlesEl
this.videoCaption.subtitlesEl.on('scroll', this.videoControl.showControls); .on(
'scroll', this.videoCaption.autoShowCaptions
)
.on(
'scroll', this.videoControl.showControls
);
} else if (!this.config.autohideHtml5) {
this.videoCaption.subtitlesEl.on({
keydown: this.videoCaption.autoShowCaptions,
focus: this.videoCaption.autoShowCaptions,
// Moving slider on subtitles is not a mouse move, but captions
// should not be auto-hidden.
scroll: this.videoCaption.autoShowCaptions,
mouseout: this.videoCaption.autoHideCaptions,
blur: this.videoCaption.autoHideCaptions
});
this.videoCaption.hideSubtitlesEl.on({
mousemove: this.videoCaption.autoShowCaptions,
focus: this.videoCaption.autoShowCaptions,
mouseout: this.videoCaption.autoHideCaptions,
blur: this.videoCaption.autoHideCaptions
});
} }
} }
...@@ -209,7 +256,8 @@ function () { ...@@ -209,7 +256,8 @@ function () {
} }
function captionURL() { function captionURL() {
return '' + this.config.caption_asset_path + this.youtubeId('1.0') + '.srt.sjson'; return '' + this.config.caption_asset_path +
this.youtubeId('1.0') + '.srt.sjson';
} }
function autoShowCaptions(event) { function autoShowCaptions(event) {
...@@ -224,13 +272,19 @@ function () { ...@@ -224,13 +272,19 @@ function () {
this.videoCaption.subtitlesEl.show(); this.videoCaption.subtitlesEl.show();
this.captionState = 'visible'; this.captionState = 'visible';
} else if (this.captionState === 'hiding') { } else if (this.captionState === 'hiding') {
this.videoCaption.subtitlesEl.stop(true, false).css('opacity', 1).show(); this.videoCaption.subtitlesEl
.stop(true, false).css('opacity', 1).show();
this.captionState = 'visible'; this.captionState = 'visible';
} else if (this.captionState === 'visible') { } else if (this.captionState === 'visible') {
clearTimeout(this.captionHideTimeout); clearTimeout(this.captionHideTimeout);
} }
this.captionHideTimeout = setTimeout(this.videoCaption.autoHideCaptions, this.videoCaption.fadeOutTimeout); if (this.config.autohideHtml5) {
this.captionHideTimeout = setTimeout(
this.videoCaption.autoHideCaptions,
this.videoCaption.fadeOutTimeout
);
}
this.captionsShowLock = false; this.captionsShowLock = false;
} }
...@@ -249,15 +303,21 @@ function () { ...@@ -249,15 +303,21 @@ function () {
_this = this; _this = this;
this.videoCaption.subtitlesEl.fadeOut(this.videoCaption.fadeOutTimeout, function () { this.videoCaption.subtitlesEl
_this.captionState = 'invisible'; .fadeOut(
}); this.videoCaption.fadeOutTimeout,
function () {
_this.captionState = 'invisible';
}
);
} }
function resize() { function resize() {
this.videoCaption.subtitlesEl this.videoCaption.subtitlesEl
.find('.spacing:first').height(this.videoCaption.topSpacingHeight()) .find('.spacing:first')
.find('.spacing:last').height(this.videoCaption.bottomSpacingHeight()); .height(this.videoCaption.topSpacingHeight())
.find('.spacing:last')
.height(this.videoCaption.bottomSpacingHeight());
this.videoCaption.scrollCaption(); this.videoCaption.scrollCaption();
...@@ -269,7 +329,10 @@ function () { ...@@ -269,7 +329,10 @@ function () {
clearTimeout(this.videoCaption.frozen); clearTimeout(this.videoCaption.frozen);
} }
this.videoCaption.frozen = setTimeout(this.videoCaption.onMouseLeave, 10000); this.videoCaption.frozen = setTimeout(
this.videoCaption.onMouseLeave,
this.config.captionsFreezeTime
);
} }
function onMouseLeave() { function onMouseLeave() {
...@@ -285,6 +348,10 @@ function () { ...@@ -285,6 +348,10 @@ function () {
} }
function onMovement() { function onMovement() {
if (!this.config.autohideHtml5) {
this.videoCaption.autoShowCaptions();
}
this.videoCaption.onMouseEnter(); this.videoCaption.onMouseEnter();
} }
...@@ -292,16 +359,28 @@ function () { ...@@ -292,16 +359,28 @@ function () {
var container = $('<ol>'), var container = $('<ol>'),
_this = this; _this = this;
this.el.find('.video-wrapper').after(this.videoCaption.subtitlesEl); this.elVideoWrapper.after(this.videoCaption.subtitlesEl);
this.el.find('.video-controls .secondary-controls').append(this.videoCaption.hideSubtitlesEl); this.el.find('.video-controls .secondary-controls')
.append(this.videoCaption.hideSubtitlesEl);
this.videoCaption.setSubtitlesHeight(); this.videoCaption.setSubtitlesHeight();
if (this.videoType === 'html5') { if ((this.videoType === 'html5') && (this.config.autohideHtml5)) {
this.videoCaption.fadeOutTimeout = this.config.fadeOutTimeout; this.videoCaption.fadeOutTimeout = this.config.fadeOutTimeout;
this.videoCaption.subtitlesEl.addClass('html5'); this.videoCaption.subtitlesEl.addClass('html5');
this.captionHideTimeout = setTimeout(this.videoCaption.autoHideCaptions, this.videoCaption.fadeOutTimeout); this.captionHideTimeout = setTimeout(
this.videoCaption.autoHideCaptions,
this.videoCaption.fadeOutTimeout
);
} else if (!this.config.autohideHtml5) {
this.videoCaption.fadeOutTimeout = this.config.fadeOutTimeout;
this.videoCaption.subtitlesEl.addClass('html5');
this.captionHideTimeout = setTimeout(
this.videoCaption.autoHideCaptions,
0
);
} }
this.videoCaption.hideCaptions(this.hide_captions); this.videoCaption.hideCaptions(this.hide_captions);
...@@ -322,38 +401,47 @@ function () { ...@@ -322,38 +401,47 @@ function () {
container.append(liEl); container.append(liEl);
}); });
this.videoCaption.subtitlesEl.html(container.html()); this.videoCaption.subtitlesEl
.html(container.html())
this.videoCaption.subtitlesEl.find('li[data-index]').on({ .find('li[data-index]')
mouseover: this.videoCaption.captionMouseOverOut, .on({
mouseout: this.videoCaption.captionMouseOverOut, mouseover: this.videoCaption.captionMouseOverOut,
mousedown: this.videoCaption.captionMouseDown, mouseout: this.videoCaption.captionMouseOverOut,
click: this.videoCaption.captionClick, mousedown: this.videoCaption.captionMouseDown,
focus: this.videoCaption.captionFocus, click: this.videoCaption.captionClick,
blur: this.videoCaption.captionBlur, focus: this.videoCaption.captionFocus,
keydown: this.videoCaption.captionKeyDown blur: this.videoCaption.captionBlur,
}); keydown: this.videoCaption.captionKeyDown
});
// Enables or disables automatic scrolling of the captions when the // Enables or disables automatic scrolling of the captions when the
// video is playing. This feature has to be disabled when tabbing // video is playing. This feature has to be disabled when tabbing
// through them as it interferes with that action. Initially, have this // through them as it interferes with that action. Initially, have this
// flag enabled as we assume mouse use. Then, if the first caption // flag enabled as we assume mouse use. Then, if the first caption
// (through forward tabbing) or the last caption (through backwards // (through forward tabbing) or the last caption (through backwards
// tabbing) gets the focus, disable that feature. Renable it if tabbing // tabbing) gets the focus, disable that feature. Renable it if tabbing
// then cycles out of the the captions. // then cycles out of the the captions.
this.videoCaption.autoScrolling = true; this.videoCaption.autoScrolling = true;
// Keeps track of where the focus is situated in the array of captions. // Keeps track of where the focus is situated in the array of captions.
// Used to implement the automatic scrolling behavior and decide if the // Used to implement the automatic scrolling behavior and decide if the
// outline around a caption has to be hidden or shown on a mouseenter or // outline around a caption has to be hidden or shown on a mouseenter
// mouseleave. Initially, no caption has the focus, set the index to -1. // or mouseleave. Initially, no caption has the focus, set the
// index to -1.
this.videoCaption.currentCaptionIndex = -1; this.videoCaption.currentCaptionIndex = -1;
// Used to track if the focus is coming from a click or tabbing. This // Used to track if the focus is coming from a click or tabbing. This
// has to be known to decide if, when a caption gets the focus, an // has to be known to decide if, when a caption gets the focus, an
// outline has to be drawn (tabbing) or not (mouse click). // outline has to be drawn (tabbing) or not (mouse click).
this.videoCaption.isMouseFocus = false; this.videoCaption.isMouseFocus = false;
this.videoCaption.subtitlesEl.prepend($('<li class="spacing">').height(this.videoCaption.topSpacingHeight())); this.videoCaption.subtitlesEl
this.videoCaption.subtitlesEl.append($('<li class="spacing">').height(this.videoCaption.bottomSpacingHeight())); .prepend(
$('<li class="spacing">')
.height(this.videoCaption.topSpacingHeight())
)
.append(
$('<li class="spacing">')
.height(this.videoCaption.bottomSpacingHeight())
);
this.videoCaption.rendered = true; this.videoCaption.rendered = true;
} }
...@@ -362,7 +450,7 @@ function () { ...@@ -362,7 +450,7 @@ function () {
// On mouseOut, show the outline of a caption that has been tabbed to. // On mouseOut, show the outline of a caption that has been tabbed to.
function captionMouseOverOut(event) { function captionMouseOverOut(event) {
var caption = $(event.target), var caption = $(event.target),
captionIndex = parseInt(caption.attr('data-index'), 10); captionIndex = parseInt(caption.attr('data-index'), 10);
if (captionIndex === this.videoCaption.currentCaptionIndex) { if (captionIndex === this.videoCaption.currentCaptionIndex) {
if (event.type === 'mouseover') { if (event.type === 'mouseover') {
caption.removeClass('focused'); caption.removeClass('focused');
...@@ -370,7 +458,7 @@ function () { ...@@ -370,7 +458,7 @@ function () {
else { // mouseout else { // mouseout
caption.addClass('focused'); caption.addClass('focused');
} }
} }
} }
function captionMouseDown(event) { function captionMouseDown(event) {
...@@ -390,36 +478,41 @@ function () { ...@@ -390,36 +478,41 @@ function () {
captionIndex = parseInt(caption.attr('data-index'), 10); captionIndex = parseInt(caption.attr('data-index'), 10);
// If the focus comes from a mouse click, hide the outline, turn on // If the focus comes from a mouse click, hide the outline, turn on
// automatic scrolling and set currentCaptionIndex to point outside of // automatic scrolling and set currentCaptionIndex to point outside of
// caption list (ie -1) to disable mouseenter, mouseleave behavior. // caption list (ie -1) to disable mouseenter, mouseleave behavior.
if (this.videoCaption.isMouseFocus) { if (this.videoCaption.isMouseFocus) {
this.videoCaption.autoScrolling = true; this.videoCaption.autoScrolling = true;
caption.removeClass('focused'); caption.removeClass('focused');
this.videoCaption.currentCaptionIndex = -1; this.videoCaption.currentCaptionIndex = -1;
} }
// If the focus comes from tabbing, show the outline and turn off // If the focus comes from tabbing, show the outline and turn off
// automatic scrolling. // automatic scrolling.
else { else {
this.videoCaption.currentCaptionIndex = captionIndex; this.videoCaption.currentCaptionIndex = captionIndex;
caption.addClass('focused'); caption.addClass('focused');
// The second and second to last elements turn automatic scrolling // The second and second to last elements turn automatic scrolling
// off again as it may have been enabled in captionBlur. // off again as it may have been enabled in captionBlur.
if (captionIndex <= 1 || captionIndex >= this.videoCaption.captions.length-2) { if (
captionIndex <= 1 ||
captionIndex >= this.videoCaption.captions.length - 2
) {
this.videoCaption.autoScrolling = false; this.videoCaption.autoScrolling = false;
} }
} }
} }
function captionBlur(event) { function captionBlur(event) {
var caption = $(event.target), var caption = $(event.target),
captionIndex = parseInt(caption.attr('data-index'), 10); captionIndex = parseInt(caption.attr('data-index'), 10);
caption.removeClass('focused'); caption.removeClass('focused');
// If we are on first or last index, we have to turn automatic scroll on // If we are on first or last index, we have to turn automatic scroll
// again when losing focus. There is no way to know in what direction we // on again when losing focus. There is no way to know in what
// are tabbing. So we could be on the first element and tabbing back out // direction we are tabbing. So we could be on the first element and
// of the captions or on the last element and tabbing forward out of the // tabbing back out of the captions or on the last element and tabbing
// captions. // forward out of the captions.
if (captionIndex === 0 || if (captionIndex === 0 ||
captionIndex === this.videoCaption.captions.length-1) { captionIndex === this.videoCaption.captions.length-1) {
this.videoCaption.autoHideCaptions();
this.videoCaption.autoScrolling = true; this.videoCaption.autoScrolling = true;
} }
} }
...@@ -434,9 +527,13 @@ function () { ...@@ -434,9 +527,13 @@ function () {
function scrollCaption() { function scrollCaption() {
var el = this.videoCaption.subtitlesEl.find('.current:first'); var el = this.videoCaption.subtitlesEl.find('.current:first');
// Automatic scrolling gets disabled if one of the captions has received // Automatic scrolling gets disabled if one of the captions has
// focus through tabbing. // received focus through tabbing.
if (!this.videoCaption.frozen && el.length && this.videoCaption.autoScrolling) { if (
!this.videoCaption.frozen &&
el.length &&
this.videoCaption.autoScrolling
) {
this.videoCaption.subtitlesEl.scrollTo( this.videoCaption.subtitlesEl.scrollTo(
el, el,
{ {
...@@ -565,11 +662,15 @@ function () { ...@@ -565,11 +662,15 @@ function () {
} }
function topSpacingHeight() { function topSpacingHeight() {
return this.videoCaption.calculateOffset(this.videoCaption.subtitlesEl.find('li:not(.spacing):first')); return this.videoCaption.calculateOffset(
this.videoCaption.subtitlesEl.find('li:not(.spacing):first')
);
} }
function bottomSpacingHeight() { function bottomSpacingHeight() {
return this.videoCaption.calculateOffset(this.videoCaption.subtitlesEl.find('li:not(.spacing):last')); return this.videoCaption.calculateOffset(
this.videoCaption.subtitlesEl.find('li:not(.spacing):last')
);
} }
function toggle(event) { function toggle(event) {
...@@ -579,23 +680,45 @@ function () { ...@@ -579,23 +680,45 @@ function () {
this.videoCaption.hideCaptions(false); this.videoCaption.hideCaptions(false);
} else { } else {
this.videoCaption.hideCaptions(true); this.videoCaption.hideCaptions(true);
// In the case when captions are not auto-hidden based on mouse
// movement anywhere on the video, we must hide them explicitly
// after the "CC" button has been clicked (to hide captions).
//
// Otherwise, in order for the captions to disappear again, the
// user must move the mouse button over the "CC" button, or over
// the captions themselves. In this case, an "autoShow" will be
// triggered, and after a timeout, an "autoHide".
if (!this.config.autohideHtml5) {
this.captionHideTimeout = setTimeout(
this.videoCaption.autoHideCaptions(),
0
);
}
} }
} }
function hideCaptions(hide_captions) { function hideCaptions(hide_captions, update_cookie) {
var type; var hideSubtitlesEl = this.videoCaption.hideSubtitlesEl,
type;
if (hide_captions) { if (hide_captions) {
type = 'hide_transcript'; type = 'hide_transcript';
this.captionsHidden = true; this.captionsHidden = true;
this.videoCaption.hideSubtitlesEl.attr('title', gettext('Turn on captions'));
this.videoCaption.hideSubtitlesEl.text(gettext('Turn on captions')); hideSubtitlesEl
.attr('title', gettext('Turn on captions'))
.text(gettext('Turn on captions'));
this.el.addClass('closed'); this.el.addClass('closed');
} else { } else {
type = 'show_transcript'; type = 'show_transcript';
this.captionsHidden = false; this.captionsHidden = false;
this.videoCaption.hideSubtitlesEl.attr('title', gettext('Turn off captions'));
this.videoCaption.hideSubtitlesEl.text(gettext('Turn off captions')); hideSubtitlesEl
.attr('title', gettext('Turn off captions'))
.text(gettext('Turn off captions'));
this.el.removeClass('closed'); this.el.removeClass('closed');
this.videoCaption.scrollCaption(); this.videoCaption.scrollCaption();
} }
...@@ -615,27 +738,43 @@ function () { ...@@ -615,27 +738,43 @@ function () {
} }
function captionHeight() { function captionHeight() {
var paddingTop;
if (this.isFullScreen) { if (this.isFullScreen) {
return $(window).height() - this.el.find('.video-controls').height() - paddingTop = parseInt(
0.5 * this.videoControl.sliderEl.height() - this.videoCaption.subtitlesEl.css('padding-top'), 10
2 * parseInt(this.videoCaption.subtitlesEl.css('padding-top'), 10); );
return $(window).height() -
this.videoControl.el.height() -
0.5 * this.videoControl.sliderEl.height() -
2 * paddingTop;
} else { } else {
return this.el.find('.video-wrapper').height(); return this.elVideoWrapper.height();
} }
} }
function setSubtitlesHeight() { function setSubtitlesHeight() {
var height = 0; var height = 0;
if (this.videoType === 'html5'){ if (
((this.videoType === 'html5') && (this.config.autohideHtml5)) ||
(!this.config.autohideHtml5)
){
// on page load captionHidden = undefined // on page load captionHidden = undefined
if ( if (
(this.captionsHidden === undefined && this.hide_captions === true ) || (
(this.captionsHidden === true) ) { this.captionsHidden === undefined &&
// In case of html5 autoshowing subtitles, this.hide_captions === true
// we ajdust height of subs, by height of scrollbar ) ||
height = this.videoControl.el.height() + 0.5 * this.videoControl.sliderEl.height(); (this.captionsHidden === true)
// height of videoControl does not contain height of slider. ) {
// (css is set to absolute, to avoid yanking when slider autochanges its height) // In case of html5 autoshowing subtitles, we adjust height of
// subs, by height of scrollbar.
height = this.videoControl.el.height() +
0.5 * this.videoControl.sliderEl.height();
// Height of videoControl does not contain height of slider.
// css is set to absolute, to avoid yanking when slider
// autochanges its height.
} }
} }
this.videoCaption.subtitlesEl.css({ this.videoCaption.subtitlesEl.css({
......
...@@ -2,38 +2,45 @@ ...@@ -2,38 +2,45 @@
Feature: LMS.Video component Feature: LMS.Video component
As a student, I want to view course videos in LMS. As a student, I want to view course videos in LMS.
# 1
Scenario: Video component is fully rendered in the LMS in HTML5 mode Scenario: Video component is fully rendered in the LMS in HTML5 mode
Given the course has a Video component in HTML5 mode Given the course has a Video component in HTML5 mode
Then when I view the video it has rendered in HTML5 mode Then when I view the video it has rendered in HTML5 mode
And all sources are correct And all sources are correct
# 2
# Firefox doesn't have HTML5 (only mp4 - fix here) # Firefox doesn't have HTML5 (only mp4 - fix here)
@skip_firefox @skip_firefox
Scenario: Autoplay is disabled in LMS for a Video component Scenario: Autoplay is disabled in LMS for a Video component
Given the course has a Video component in HTML5 mode Given the course has a Video component in HTML5 mode
Then when I view the video it does not have autoplay enabled Then when I view the video it does not have autoplay enabled
# 3
# Youtube testing # Youtube testing
Scenario: Video component is fully rendered in the LMS in Youtube mode with HTML5 sources Scenario: Video component is fully rendered in the LMS in Youtube mode with HTML5 sources
Given youtube server is up and response time is 0.4 seconds Given youtube server is up and response time is 0.4 seconds
And the course has a Video component in Youtube_HTML5 mode And the course has a Video component in Youtube_HTML5 mode
Then when I view the video it has rendered in Youtube mode Then when I view the video it has rendered in Youtube mode
# 4
Scenario: Video component is not rendered in the LMS in Youtube mode with HTML5 sources Scenario: Video component is not rendered in the LMS in Youtube mode with HTML5 sources
Given youtube server is up and response time is 2 seconds Given youtube server is up and response time is 2 seconds
And the course has a Video component in Youtube_HTML5 mode And the course has a Video component in Youtube_HTML5 mode
Then when I view the video it has rendered in HTML5 mode Then when I view the video it has rendered in HTML5 mode
# 5
Scenario: Video component is rendered in the LMS in Youtube mode without HTML5 sources Scenario: Video component is rendered in the LMS in Youtube mode without HTML5 sources
Given youtube server is up and response time is 2 seconds Given youtube server is up and response time is 2 seconds
And the course has a Video component in Youtube mode And the course has a Video component in Youtube mode
Then when I view the video it has rendered in Youtube mode Then when I view the video it has rendered in Youtube mode
# 6
Scenario: Video component is rendered in the LMS in Youtube mode with HTML5 sources that doesn't supported by browser Scenario: Video component is rendered in the LMS in Youtube mode with HTML5 sources that doesn't supported by browser
Given youtube server is up and response time is 2 seconds Given youtube server is up and response time is 2 seconds
And the course has a Video component in Youtube_HTML5_Unsupported_Video mode And the course has a Video component in Youtube_HTML5_Unsupported_Video mode
Then when I view the video it has rendered in Youtube mode Then when I view the video it has rendered in Youtube mode
# 7
Scenario: Video component is rendered in the LMS in HTML5 mode with HTML5 sources that doesn't supported by browser Scenario: Video component is rendered in the LMS in HTML5 mode with HTML5 sources that doesn't supported by browser
Given the course has a Video component in HTML5_Unsupported_Video mode Given the course has a Video component in HTML5_Unsupported_Video mode
Then error message is shown Then error message is shown
......
...@@ -26,6 +26,19 @@ ...@@ -26,6 +26,19 @@
data-yt-test-timeout="${yt_test_timeout}" data-yt-test-timeout="${yt_test_timeout}"
data-yt-test-url="${yt_test_url}" data-yt-test-url="${yt_test_url}"
## For now, the option "data-autohide-html5" is hard coded. This option
## either enables or disables autohiding of controls and captions on mouse
## inactivity. If set to true, controls and captions will autohide for
## HTML5 sources (non-YouTube) after a period of mouse inactivity over the
## whole video. When the mouse moves (or a key is pressed while any part of
## the video player is focused), the captions and controls will be shown
## once again.
##
## There is no option in the "Advanced Editor" to set this option. However,
## this option will have an effect if changed to "True". The code on
## front-end exists.
data-autohide-html5="False"
tabindex="-1" tabindex="-1"
> >
<div class="focus_grabber first"></div> <div class="focus_grabber first"></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