Merge pull request #1183 from edx/valera/add_option_transcripts_remove_onhover
Valera/add option transcripts remove onhover
This commit is contained in:
@@ -2,15 +2,18 @@
|
||||
Feature: CMS.Video Component
|
||||
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
|
||||
Scenario: Autoplay is disabled in Studio
|
||||
Given I have created a Video component
|
||||
Then when I view the video it does not have autoplay enabled
|
||||
|
||||
# 2
|
||||
Scenario: Creating a video takes a single click
|
||||
Given I have clicked the new unit button
|
||||
Then creating a video takes a single click
|
||||
|
||||
# 3
|
||||
# Sauce Labs cannot delete cookies
|
||||
@skip_sauce
|
||||
Scenario: Captions are hidden correctly
|
||||
@@ -18,12 +21,14 @@ Feature: CMS.Video Component
|
||||
And I have hidden captions
|
||||
Then when I view the video it does not show the captions
|
||||
|
||||
# 4
|
||||
# Sauce Labs cannot delete cookies
|
||||
@skip_sauce
|
||||
Scenario: Captions are shown correctly
|
||||
Given I have created a Video component with subtitles
|
||||
Then when I view the video it does show the captions
|
||||
|
||||
# 5
|
||||
# Sauce Labs cannot delete cookies
|
||||
@skip_sauce
|
||||
Scenario: Captions are toggled correctly
|
||||
@@ -31,7 +36,36 @@ Feature: CMS.Video Component
|
||||
And I have toggled captions
|
||||
Then when I view the video it does show the captions
|
||||
|
||||
# 6
|
||||
Scenario: Video data is shown correctly
|
||||
Given I have created a video with only XML data
|
||||
And I reload the page
|
||||
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"
|
||||
|
||||
@@ -4,6 +4,11 @@ from lettuce import world, step
|
||||
from xmodule.modulestore import Location
|
||||
from contentstore.utils import get_modulestore
|
||||
|
||||
BUTTONS = {
|
||||
'CC': '.hide-subtitles',
|
||||
'volume': '.volume',
|
||||
}
|
||||
|
||||
|
||||
@step('I have created a Video component$')
|
||||
def i_created_a_video_component(step):
|
||||
@@ -19,6 +24,7 @@ def i_created_a_video_component(step):
|
||||
def i_created_a_video_with_subs(_step):
|
||||
_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')
|
||||
@@ -115,3 +121,38 @@ def the_youtube_video_is_shown(_step):
|
||||
world.wait_for_xmodule()
|
||||
ele = world.css_find('.video').first
|
||||
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 {
|
||||
@include transition(none);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
width: 30px;
|
||||
|
||||
|
||||
&:hover, &:active {
|
||||
background-color: #444;
|
||||
color: #fff;
|
||||
@@ -457,7 +457,7 @@ div.video {
|
||||
text-indent: -9999px;
|
||||
@include transition(none);
|
||||
width: 30px;
|
||||
|
||||
|
||||
&:hover, &:active {
|
||||
background-color: #444;
|
||||
color: #fff;
|
||||
@@ -611,6 +611,7 @@ div.video {
|
||||
ol.subtitles {
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@@ -645,6 +646,7 @@ div.video {
|
||||
ol.subtitles {
|
||||
right: -(flex-grid(4));
|
||||
width: auto;
|
||||
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
data-autoplay="False"
|
||||
data-yt-test-timeout="1500"
|
||||
data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/"
|
||||
data-autohide-html5="True"
|
||||
>
|
||||
<div class="focus_grabber first"></div>
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
data-autoplay="False"
|
||||
data-yt-test-timeout="1500"
|
||||
data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/"
|
||||
data-autohide-html5="True"
|
||||
>
|
||||
<div class="focus_grabber first"></div>
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
data-autoplay="False"
|
||||
data-yt-test-timeout="1500"
|
||||
data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/"
|
||||
data-autohide-html5="True"
|
||||
>
|
||||
<div class="focus_grabber first"></div>
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
data-autoplay="False"
|
||||
data-yt-test-timeout="1500"
|
||||
data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/"
|
||||
data-autohide-html5="True"
|
||||
>
|
||||
<div class="focus_grabber first"></div>
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
data-autoplay="False"
|
||||
data-yt-test-timeout="1500"
|
||||
data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/"
|
||||
data-autohide-html5="True"
|
||||
>
|
||||
<div class="focus_grabber first"></div>
|
||||
|
||||
@@ -73,6 +74,8 @@
|
||||
data-autoplay="False"
|
||||
data-yt-test-timeout="1500"
|
||||
data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/"
|
||||
|
||||
data-autohide-html5="True"
|
||||
>
|
||||
<div class="tc-wrapper">
|
||||
<article class="video-wrapper">
|
||||
@@ -130,6 +133,8 @@
|
||||
data-autoplay="False"
|
||||
data-yt-test-timeout="1500"
|
||||
data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/"
|
||||
|
||||
data-autohide-html5="True"
|
||||
>
|
||||
<div class="tc-wrapper">
|
||||
<article class="video-wrapper">
|
||||
|
||||
@@ -1,673 +1,798 @@
|
||||
(function() {
|
||||
describe('VideoCaption', function() {
|
||||
var state, videoPlayer, videoCaption, videoSpeedControl, oldOTBD;
|
||||
(function () {
|
||||
describe('VideoCaption', function () {
|
||||
var state, videoPlayer, videoCaption, videoSpeedControl, oldOTBD;
|
||||
|
||||
function initialize() {
|
||||
loadFixtures('video_all.html');
|
||||
state = new Video('#example');
|
||||
videoPlayer = state.videoPlayer;
|
||||
videoCaption = state.videoCaption;
|
||||
videoSpeedControl = state.videoSpeedControl;
|
||||
videoControl = state.videoControl;
|
||||
}
|
||||
function initialize() {
|
||||
loadFixtures('video_all.html');
|
||||
state = new Video('#example');
|
||||
videoPlayer = state.videoPlayer;
|
||||
videoCaption = state.videoCaption;
|
||||
videoSpeedControl = state.videoSpeedControl;
|
||||
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 () {
|
||||
oldOTBD = window.onTouchBasedDevice;
|
||||
window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice')
|
||||
.andReturn(false);
|
||||
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);
|
||||
afterEach(function () {
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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() {
|
||||
expect(videoCaption.seekPlayer).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
|
||||
@@ -264,15 +264,21 @@ function (VideoPlayer) {
|
||||
// The function set initial configuration and preparation.
|
||||
|
||||
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
|
||||
// element has a CSS class 'fullscreen'.
|
||||
this.isFullScreen = false;
|
||||
|
||||
// The parent element of the video, and the ID.
|
||||
this.el = $(element).find('.video');
|
||||
this.elVideoWrapper = this.el.find('.video-wrapper');
|
||||
this.id = this.el.attr('id').replace(/video_/, '');
|
||||
|
||||
// jQuery .data() return object with keys in lower camelCase format.
|
||||
data = this.el.data();
|
||||
|
||||
console.log(
|
||||
'[Video info]: Initializing video with id "' + this.id + '".'
|
||||
);
|
||||
@@ -283,32 +289,26 @@ function (VideoPlayer) {
|
||||
this.config = {
|
||||
element: element,
|
||||
|
||||
start: this.el.data('start'),
|
||||
end: this.el.data('end'),
|
||||
|
||||
caption_data_dir: this.el.data('caption-data-dir'),
|
||||
caption_asset_path: this.el.data('caption-asset-path'),
|
||||
show_captions: (
|
||||
this.el.data('show-captions')
|
||||
.toString().toLowerCase() === 'true'
|
||||
),
|
||||
youtubeStreams: this.el.data('streams'),
|
||||
|
||||
sub: this.el.data('sub'),
|
||||
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'),
|
||||
|
||||
start: data['start'],
|
||||
end: data['end'],
|
||||
caption_data_dir: data['captionDataDir'],
|
||||
caption_asset_path: data['captionAssetPath'],
|
||||
show_captions: regExp.test(data['showCaptions'].toString()),
|
||||
youtubeStreams: data['streams'],
|
||||
autohideHtml5: regExp.test(data['autohideHtml5'].toString()),
|
||||
sub: data['sub'],
|
||||
mp4Source: data['mp4Source'],
|
||||
webmSource: data['webmSource'],
|
||||
oggSource: data['oggSource'],
|
||||
ytTestUrl: data['ytTestUrl'],
|
||||
fadeOutTimeout: 1400,
|
||||
|
||||
captionsFreezeTime: 10000,
|
||||
availableQualities: ['hd720', 'hd1080', 'highres']
|
||||
};
|
||||
|
||||
// Check if the YT test timeout has been set. If not, or it is in
|
||||
// improper format, then set to default value.
|
||||
tempYtTestTimeout = parseInt(this.el.data('yt-test-timeout'), 10);
|
||||
tempYtTestTimeout = parseInt(data['ytTestTimeout'], 10);
|
||||
if (!isFinite(tempYtTestTimeout)) {
|
||||
tempYtTestTimeout = 1500;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ function () {
|
||||
state.videoControl.play();
|
||||
}
|
||||
|
||||
if (state.videoType === 'html5') {
|
||||
if ((state.videoType === 'html5') && (state.config.autohideHtml5)) {
|
||||
state.videoControl.fadeOutTimeout = state.config.fadeOutTimeout;
|
||||
|
||||
state.videoControl.el.addClass('html5');
|
||||
@@ -81,7 +81,7 @@ function () {
|
||||
state.videoControl.fullScreenEl.on('click', state.videoControl.toggleFullScreen);
|
||||
$(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('keydown', state.videoControl.showControls);
|
||||
}
|
||||
|
||||
@@ -34,46 +34,63 @@ 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.videoCaption.autoShowCaptions = _.bind(autoShowCaptions, state);
|
||||
state.videoCaption.autoHideCaptions = _.bind(autoHideCaptions, state);
|
||||
state.videoCaption.resize = _.bind(resize, state);
|
||||
state.videoCaption.toggle = _.bind(toggle, state);
|
||||
state.videoCaption.onMouseEnter = _.bind(onMouseEnter, state);
|
||||
state.videoCaption.onMouseLeave = _.bind(onMouseLeave, state);
|
||||
state.videoCaption.onMovement = _.bind(onMovement, state);
|
||||
state.videoCaption.renderCaption = _.bind(renderCaption, state);
|
||||
state.videoCaption.captionHeight = _.bind(captionHeight, state);
|
||||
state.videoCaption.topSpacingHeight = _.bind(topSpacingHeight, state);
|
||||
state.videoCaption.bottomSpacingHeight = _.bind(bottomSpacingHeight, state);
|
||||
state.videoCaption.scrollCaption = _.bind(scrollCaption, state);
|
||||
state.videoCaption.search = _.bind(search, state);
|
||||
state.videoCaption.play = _.bind(play, state);
|
||||
state.videoCaption.pause = _.bind(pause, state);
|
||||
state.videoCaption.seekPlayer = _.bind(seekPlayer, state);
|
||||
state.videoCaption.hideCaptions = _.bind(hideCaptions, state);
|
||||
state.videoCaption.calculateOffset = _.bind(calculateOffset, state);
|
||||
state.videoCaption.updatePlayTime = _.bind(updatePlayTime, state);
|
||||
state.videoCaption.setSubtitlesHeight = _.bind(setSubtitlesHeight, state);
|
||||
state.videoCaption.autoShowCaptions = _.bind(
|
||||
autoShowCaptions, state
|
||||
);
|
||||
state.videoCaption.autoHideCaptions = _.bind(
|
||||
autoHideCaptions, state
|
||||
);
|
||||
state.videoCaption.resize = _.bind(resize, state);
|
||||
state.videoCaption.toggle = _.bind(toggle, state);
|
||||
state.videoCaption.onMouseEnter = _.bind(onMouseEnter, state);
|
||||
state.videoCaption.onMouseLeave = _.bind(onMouseLeave, state);
|
||||
state.videoCaption.onMovement = _.bind(onMovement, state);
|
||||
state.videoCaption.renderCaption = _.bind(renderCaption, state);
|
||||
state.videoCaption.captionHeight = _.bind(captionHeight, state);
|
||||
state.videoCaption.topSpacingHeight = _.bind(
|
||||
topSpacingHeight, state
|
||||
);
|
||||
state.videoCaption.bottomSpacingHeight = _.bind(
|
||||
bottomSpacingHeight, state
|
||||
);
|
||||
state.videoCaption.scrollCaption = _.bind(scrollCaption, state);
|
||||
state.videoCaption.search = _.bind(search, state);
|
||||
state.videoCaption.play = _.bind(play, state);
|
||||
state.videoCaption.pause = _.bind(pause, state);
|
||||
state.videoCaption.seekPlayer = _.bind(seekPlayer, state);
|
||||
state.videoCaption.hideCaptions = _.bind(hideCaptions, state);
|
||||
state.videoCaption.calculateOffset = _.bind(
|
||||
calculateOffset, state
|
||||
);
|
||||
state.videoCaption.updatePlayTime = _.bind(updatePlayTime, state);
|
||||
state.videoCaption.setSubtitlesHeight = _.bind(
|
||||
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);
|
||||
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.
|
||||
// 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().
|
||||
// ***************************************************************
|
||||
|
||||
/**
|
||||
@@ -109,10 +126,13 @@ function () {
|
||||
|
||||
// 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() {
|
||||
$(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
|
||||
.on(
|
||||
@@ -132,14 +152,41 @@ function () {
|
||||
this.videoCaption.onMovement
|
||||
);
|
||||
|
||||
if (this.videoType === 'html5') {
|
||||
this.el.on('mousemove', this.videoCaption.autoShowCaptions);
|
||||
this.el.on('keydown', this.videoCaption.autoShowCaptions);
|
||||
if ((this.videoType === 'html5') && (this.config.autohideHtml5)) {
|
||||
this.el.on({
|
||||
mousemove: this.videoCaption.autoShowCaptions,
|
||||
keydown: this.videoCaption.autoShowCaptions
|
||||
});
|
||||
|
||||
// Moving slider on subtitles is not a mouse move,
|
||||
// but captions and controls should be showed.
|
||||
this.videoCaption.subtitlesEl.on('scroll', this.videoCaption.autoShowCaptions);
|
||||
this.videoCaption.subtitlesEl.on('scroll', this.videoControl.showControls);
|
||||
// Moving slider on subtitles is not a mouse move, but captions and
|
||||
// controls should be shown.
|
||||
this.videoCaption.subtitlesEl
|
||||
.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 () {
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -224,13 +272,19 @@ function () {
|
||||
this.videoCaption.subtitlesEl.show();
|
||||
this.captionState = 'visible';
|
||||
} 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';
|
||||
} else if (this.captionState === 'visible') {
|
||||
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;
|
||||
}
|
||||
@@ -249,15 +303,21 @@ function () {
|
||||
|
||||
_this = this;
|
||||
|
||||
this.videoCaption.subtitlesEl.fadeOut(this.videoCaption.fadeOutTimeout, function () {
|
||||
_this.captionState = 'invisible';
|
||||
});
|
||||
this.videoCaption.subtitlesEl
|
||||
.fadeOut(
|
||||
this.videoCaption.fadeOutTimeout,
|
||||
function () {
|
||||
_this.captionState = 'invisible';
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function resize() {
|
||||
this.videoCaption.subtitlesEl
|
||||
.find('.spacing:first').height(this.videoCaption.topSpacingHeight())
|
||||
.find('.spacing:last').height(this.videoCaption.bottomSpacingHeight());
|
||||
.find('.spacing:first')
|
||||
.height(this.videoCaption.topSpacingHeight())
|
||||
.find('.spacing:last')
|
||||
.height(this.videoCaption.bottomSpacingHeight());
|
||||
|
||||
this.videoCaption.scrollCaption();
|
||||
|
||||
@@ -269,7 +329,10 @@ function () {
|
||||
clearTimeout(this.videoCaption.frozen);
|
||||
}
|
||||
|
||||
this.videoCaption.frozen = setTimeout(this.videoCaption.onMouseLeave, 10000);
|
||||
this.videoCaption.frozen = setTimeout(
|
||||
this.videoCaption.onMouseLeave,
|
||||
this.config.captionsFreezeTime
|
||||
);
|
||||
}
|
||||
|
||||
function onMouseLeave() {
|
||||
@@ -285,6 +348,10 @@ function () {
|
||||
}
|
||||
|
||||
function onMovement() {
|
||||
if (!this.config.autohideHtml5) {
|
||||
this.videoCaption.autoShowCaptions();
|
||||
}
|
||||
|
||||
this.videoCaption.onMouseEnter();
|
||||
}
|
||||
|
||||
@@ -292,16 +359,28 @@ function () {
|
||||
var container = $('<ol>'),
|
||||
_this = this;
|
||||
|
||||
this.el.find('.video-wrapper').after(this.videoCaption.subtitlesEl);
|
||||
this.el.find('.video-controls .secondary-controls').append(this.videoCaption.hideSubtitlesEl);
|
||||
this.elVideoWrapper.after(this.videoCaption.subtitlesEl);
|
||||
this.el.find('.video-controls .secondary-controls')
|
||||
.append(this.videoCaption.hideSubtitlesEl);
|
||||
|
||||
this.videoCaption.setSubtitlesHeight();
|
||||
|
||||
if (this.videoType === 'html5') {
|
||||
if ((this.videoType === 'html5') && (this.config.autohideHtml5)) {
|
||||
this.videoCaption.fadeOutTimeout = this.config.fadeOutTimeout;
|
||||
|
||||
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);
|
||||
@@ -322,17 +401,18 @@ function () {
|
||||
container.append(liEl);
|
||||
});
|
||||
|
||||
this.videoCaption.subtitlesEl.html(container.html());
|
||||
|
||||
this.videoCaption.subtitlesEl.find('li[data-index]').on({
|
||||
mouseover: this.videoCaption.captionMouseOverOut,
|
||||
mouseout: this.videoCaption.captionMouseOverOut,
|
||||
mousedown: this.videoCaption.captionMouseDown,
|
||||
click: this.videoCaption.captionClick,
|
||||
focus: this.videoCaption.captionFocus,
|
||||
blur: this.videoCaption.captionBlur,
|
||||
keydown: this.videoCaption.captionKeyDown
|
||||
});
|
||||
this.videoCaption.subtitlesEl
|
||||
.html(container.html())
|
||||
.find('li[data-index]')
|
||||
.on({
|
||||
mouseover: this.videoCaption.captionMouseOverOut,
|
||||
mouseout: this.videoCaption.captionMouseOverOut,
|
||||
mousedown: this.videoCaption.captionMouseDown,
|
||||
click: this.videoCaption.captionClick,
|
||||
focus: this.videoCaption.captionFocus,
|
||||
blur: this.videoCaption.captionBlur,
|
||||
keydown: this.videoCaption.captionKeyDown
|
||||
});
|
||||
|
||||
// Enables or disables automatic scrolling of the captions when the
|
||||
// video is playing. This feature has to be disabled when tabbing
|
||||
@@ -344,16 +424,24 @@ function () {
|
||||
this.videoCaption.autoScrolling = true;
|
||||
// Keeps track of where the focus is situated in the array of captions.
|
||||
// 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
|
||||
// mouseleave. Initially, no caption has the focus, set the index to -1.
|
||||
// outline around a caption has to be hidden or shown on a mouseenter
|
||||
// or mouseleave. Initially, no caption has the focus, set the
|
||||
// index to -1.
|
||||
this.videoCaption.currentCaptionIndex = -1;
|
||||
// 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
|
||||
// outline has to be drawn (tabbing) or not (mouse click).
|
||||
this.videoCaption.isMouseFocus = false;
|
||||
|
||||
this.videoCaption.subtitlesEl.prepend($('<li class="spacing">').height(this.videoCaption.topSpacingHeight()));
|
||||
this.videoCaption.subtitlesEl.append($('<li class="spacing">').height(this.videoCaption.bottomSpacingHeight()));
|
||||
this.videoCaption.subtitlesEl
|
||||
.prepend(
|
||||
$('<li class="spacing">')
|
||||
.height(this.videoCaption.topSpacingHeight())
|
||||
)
|
||||
.append(
|
||||
$('<li class="spacing">')
|
||||
.height(this.videoCaption.bottomSpacingHeight())
|
||||
);
|
||||
|
||||
this.videoCaption.rendered = true;
|
||||
}
|
||||
@@ -403,7 +491,10 @@ function () {
|
||||
caption.addClass('focused');
|
||||
// The second and second to last elements turn automatic scrolling
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@@ -413,13 +504,15 @@ function () {
|
||||
var caption = $(event.target),
|
||||
captionIndex = parseInt(caption.attr('data-index'), 10);
|
||||
caption.removeClass('focused');
|
||||
// If we are on first or last index, we have to turn automatic scroll on
|
||||
// again when losing focus. There is no way to know in what direction we
|
||||
// are tabbing. So we could be on the first element and tabbing back out
|
||||
// of the captions or on the last element and tabbing forward out of the
|
||||
// captions.
|
||||
// If we are on first or last index, we have to turn automatic scroll
|
||||
// on again when losing focus. There is no way to know in what
|
||||
// direction we are tabbing. So we could be on the first element and
|
||||
// tabbing back out of the captions or on the last element and tabbing
|
||||
// forward out of the captions.
|
||||
if (captionIndex === 0 ||
|
||||
captionIndex === this.videoCaption.captions.length-1) {
|
||||
this.videoCaption.autoHideCaptions();
|
||||
|
||||
this.videoCaption.autoScrolling = true;
|
||||
}
|
||||
}
|
||||
@@ -434,9 +527,13 @@ function () {
|
||||
function scrollCaption() {
|
||||
var el = this.videoCaption.subtitlesEl.find('.current:first');
|
||||
|
||||
// Automatic scrolling gets disabled if one of the captions has received
|
||||
// focus through tabbing.
|
||||
if (!this.videoCaption.frozen && el.length && this.videoCaption.autoScrolling) {
|
||||
// Automatic scrolling gets disabled if one of the captions has
|
||||
// received focus through tabbing.
|
||||
if (
|
||||
!this.videoCaption.frozen &&
|
||||
el.length &&
|
||||
this.videoCaption.autoScrolling
|
||||
) {
|
||||
this.videoCaption.subtitlesEl.scrollTo(
|
||||
el,
|
||||
{
|
||||
@@ -565,11 +662,15 @@ function () {
|
||||
}
|
||||
|
||||
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() {
|
||||
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) {
|
||||
@@ -579,11 +680,27 @@ function () {
|
||||
this.videoCaption.hideCaptions(false);
|
||||
} else {
|
||||
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, update_cookie) {
|
||||
var type;
|
||||
var hideSubtitlesEl = this.videoCaption.hideSubtitlesEl,
|
||||
type;
|
||||
|
||||
if (typeof update_cookie === 'undefined') {
|
||||
update_cookie = true;
|
||||
@@ -592,14 +709,20 @@ function () {
|
||||
if (hide_captions) {
|
||||
type = 'hide_transcript';
|
||||
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');
|
||||
} else {
|
||||
type = 'show_transcript';
|
||||
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.videoCaption.scrollCaption();
|
||||
}
|
||||
@@ -621,27 +744,43 @@ function () {
|
||||
}
|
||||
|
||||
function captionHeight() {
|
||||
var paddingTop;
|
||||
|
||||
if (this.isFullScreen) {
|
||||
return $(window).height() - this.el.find('.video-controls').height() -
|
||||
0.5 * this.videoControl.sliderEl.height() -
|
||||
2 * parseInt(this.videoCaption.subtitlesEl.css('padding-top'), 10);
|
||||
paddingTop = parseInt(
|
||||
this.videoCaption.subtitlesEl.css('padding-top'), 10
|
||||
);
|
||||
|
||||
return $(window).height() -
|
||||
this.videoControl.el.height() -
|
||||
0.5 * this.videoControl.sliderEl.height() -
|
||||
2 * paddingTop;
|
||||
} else {
|
||||
return this.el.find('.video-wrapper').height();
|
||||
return this.elVideoWrapper.height();
|
||||
}
|
||||
}
|
||||
|
||||
function setSubtitlesHeight() {
|
||||
var height = 0;
|
||||
if (this.videoType === 'html5'){
|
||||
if (
|
||||
((this.videoType === 'html5') && (this.config.autohideHtml5)) ||
|
||||
(!this.config.autohideHtml5)
|
||||
){
|
||||
// on page load captionHidden = undefined
|
||||
if (
|
||||
(this.captionsHidden === undefined && this.hide_captions === true ) ||
|
||||
(this.captionsHidden === true) ) {
|
||||
// In case of html5 autoshowing subtitles,
|
||||
// we ajdust 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.captionsHidden === undefined &&
|
||||
this.hide_captions === true
|
||||
) ||
|
||||
(this.captionsHidden === true)
|
||||
) {
|
||||
// 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({
|
||||
|
||||
@@ -2,38 +2,45 @@
|
||||
Feature: LMS.Video component
|
||||
As a student, I want to view course videos in LMS.
|
||||
|
||||
# 1
|
||||
Scenario: Video component is fully rendered in the LMS 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
|
||||
And all sources are correct
|
||||
|
||||
# 2
|
||||
# Firefox doesn't have HTML5 (only mp4 - fix here)
|
||||
@skip_firefox
|
||||
Scenario: Autoplay is disabled in LMS for a Video component
|
||||
Given the course has a Video component in HTML5 mode
|
||||
Then when I view the video it does not have autoplay enabled
|
||||
|
||||
# 3
|
||||
# Youtube testing
|
||||
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
|
||||
And the course has a Video component in Youtube_HTML5 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
|
||||
Given youtube server is up and response time is 2 seconds
|
||||
And the course has a Video component in Youtube_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
|
||||
Given youtube server is up and response time is 2 seconds
|
||||
And the course has a Video component 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
|
||||
Given youtube server is up and response time is 2 seconds
|
||||
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
|
||||
|
||||
# 7
|
||||
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
|
||||
Then error message is shown
|
||||
|
||||
@@ -26,6 +26,19 @@
|
||||
data-yt-test-timeout="${yt_test_timeout}"
|
||||
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"
|
||||
>
|
||||
<div class="focus_grabber first"></div>
|
||||
|
||||
Reference in New Issue
Block a user