Commit 2536224b by Valera Rozuvan

Merge pull request #2176 from edx/valera/fix_start-time-end-time_2

Valera/fix start time end time 2
parents 591470df b6e5db02
...@@ -5,6 +5,10 @@ These are notable changes in edx-platform. This is a rolling list of changes, ...@@ -5,6 +5,10 @@ These are notable changes in edx-platform. This is a rolling list of changes,
in roughly chronological order, most recent first. Add your entries at or near in roughly chronological order, most recent first. Add your entries at or near
the top. Include a label indicating the component affected. the top. Include a label indicating the component affected.
Blades: Video player start-end time range is now shown even before Play is
clicked. Video player VCR time shows correct non-zero total time for YouTube
videos even before Play is clicked. BLD-529.
Blades: Adds CookieStorage utility for video player that provides convenient Blades: Adds CookieStorage utility for video player that provides convenient
way to work with cookies. way to work with cookies.
......
...@@ -86,7 +86,7 @@ Feature: CMS.Video Component ...@@ -86,7 +86,7 @@ Feature: CMS.Video Component
# 11 # 11
Scenario: When start end end times are specified, a range on slider is shown Scenario: When start end end times are specified, a range on slider is shown
Given I have created a Video component Given I have created a Video component with subtitles
And Make sure captions are closed And Make sure captions are closed
And I edit the component And I edit the component
And I open tab "Advanced" And I open tab "Advanced"
......
...@@ -24,7 +24,7 @@ class StubYouTubeServiceTest(unittest.TestCase): ...@@ -24,7 +24,7 @@ class StubYouTubeServiceTest(unittest.TestCase):
self.url + 'test_youtube/OEoXaMPEzfM?v=2&alt=jsonc&callback=callback_func' self.url + 'test_youtube/OEoXaMPEzfM?v=2&alt=jsonc&callback=callback_func'
) )
self.assertEqual('callback_func({"message": "I\'m youtube."})', response.content) self.assertEqual('callback_func({"data": {"duration": 60, "message": "I\'m youtube.", "id": "OEoXaMPEzfM"}})', response.content)
def test_transcript_url_equal(self): def test_transcript_url_equal(self):
response = requests.get( response = requests.get(
......
...@@ -6,6 +6,8 @@ from .http import StubHttpRequestHandler, StubHttpService ...@@ -6,6 +6,8 @@ from .http import StubHttpRequestHandler, StubHttpService
import json import json
import time import time
import requests import requests
from urlparse import urlparse
from collections import OrderedDict
class StubYouTubeHandler(StubHttpRequestHandler): class StubYouTubeHandler(StubHttpRequestHandler):
...@@ -54,14 +56,17 @@ class StubYouTubeHandler(StubHttpRequestHandler): ...@@ -54,14 +56,17 @@ class StubYouTubeHandler(StubHttpRequestHandler):
self.send_response(404) self.send_response(404)
elif 'test_youtube' in self.path: elif 'test_youtube' in self.path:
self._send_video_response("I'm youtube.") params = urlparse(self.path)
youtube_id = params.path.split('/').pop()
self._send_video_response(youtube_id, "I'm youtube.")
else: else:
self.send_response( self.send_response(
404, content="Unused url", headers={'Content-type': 'text/plain'} 404, content="Unused url", headers={'Content-type': 'text/plain'}
) )
def _send_video_response(self, message): def _send_video_response(self, youtube_id, message):
""" """
Send message back to the client for video player requests. Send message back to the client for video player requests.
Requires sending back callback id. Requires sending back callback id.
...@@ -71,7 +76,14 @@ class StubYouTubeHandler(StubHttpRequestHandler): ...@@ -71,7 +76,14 @@ class StubYouTubeHandler(StubHttpRequestHandler):
# Construct the response content # Construct the response content
callback = self.get_params['callback'][0] callback = self.get_params['callback'][0]
response = callback + '({})'.format(json.dumps({'message': message})) data = OrderedDict({
'data': OrderedDict({
'id': youtube_id,
'message': message,
'duration': 60,
})
})
response = "{cb}({data})".format(cb=callback, data=json.dumps(data))
self.send_response(200, content=response, headers={'Content-type': 'text/html'}) self.send_response(200, content=response, headers={'Content-type': 'text/html'})
self.log_message("Youtube: sent response {}".format(message)) self.log_message("Youtube: sent response {}".format(message))
......
# Stub Youtube API
window.YT =
Player: ->
PlayerState:
UNSTARTED: -1
ENDED: 0
PLAYING: 1
PAUSED: 2
BUFFERING: 3
CUED: 5
ready: (f) -> f()
window.STATUS = window.YT.PlayerState
oldAjaxWithPrefix = window.jQuery.ajaxWithPrefix
window.onTouchBasedDevice = ->
navigator.userAgent.match /iPhone|iPod|iPad/i
jasmine.stubbedCaption =
end: [3120, 6270, 8490, 21620, 24920, 25750, 27900, 34380, 35550, 40250]
start: [1180, 3120, 6270, 14910, 21620, 24920, 25750, 27900, 34380, 35550]
text: [
"MICHAEL CIMA: So let's do the first one here.",
"Vacancies, where do they come from?",
"Well, imagine a perfect crystal.",
"Now we know at any temperature other than absolute zero there's enough",
"energy going around that some atoms will have more energy",
"than others, right?",
"There's a distribution.",
"If I plot energy here and number, these atoms in the crystal will have a",
"distribution of energy.",
"And some will have quite a bit of energy, just for a moment."
]
# For our purposes, we need to make sure that the function $.ajaxWithPrefix
# does not fail when during tests a captions file is requested.
# It is originally defined in
#
# common/static/coffee/src/ajax_prefix.js
#
# We will replace it with a function that does:
#
# 1.) Return a hard coded captions object if the file name contains 'Z5KLxerq05Y'.
# 2.) Behaves the same a as the origianl in all other cases.
window.jQuery.ajaxWithPrefix = (url, settings) ->
if not settings
settings = url
url = settings.url
success = settings.success
data = settings.data
if url.match(/Z5KLxerq05Y/g) isnt null or url.match(/7tqY6eQzVhE/g) isnt null or url.match(/cogebirgzzM/g) isnt null
if window.jQuery.isFunction(success) is true
success jasmine.stubbedCaption
else if window.jQuery.isFunction(data) is true
data jasmine.stubbedCaption
else
oldAjaxWithPrefix.apply @, arguments
# Time waitsFor() should wait for before failing a test.
window.WAIT_TIMEOUT = 5000
jasmine.getFixtures().fixturesPath += 'fixtures'
jasmine.stubbedMetadata =
'7tqY6eQzVhE':
id: '7tqY6eQzVhE'
duration: 300
'cogebirgzzM':
id: 'cogebirgzzM'
duration: 200
bogus:
duration: 100
jasmine.fireEvent = (el, eventName) ->
if document.createEvent
event = document.createEvent "HTMLEvents"
event.initEvent eventName, true, true
else
event = document.createEventObject()
event.eventType = eventName
event.eventName = eventName
if document.createEvent
el.dispatchEvent(event)
else
el.fireEvent("on" + event.eventType, event)
jasmine.stubbedHtml5Speeds = ['0.75', '1.0', '1.25', '1.50']
jasmine.stubRequests = ->
spyOn($, 'ajax').andCallFake (settings) ->
if match = settings.url.match /youtube\.com\/.+\/videos\/(.+)\?v=2&alt=jsonc/
status = match[1].split('_')
if status and status[0] is 'status'
{
always: (callback) ->
callback.call(window, {}, status[1])
error: (callback) ->
callback.call(window, {}, status[1])
done: (callback) ->
callback.call(window, {}, status[1])
}
else if settings.success
# match[1] - it's video ID
settings.success data: jasmine.stubbedMetadata[match[1]]
else {
always: (callback) ->
callback.call(window, {}, 'success')
done: (callback) ->
callback.call(window, {}, 'success')
}
else if match = settings.url.match /static(\/.*)?\/subs\/(.+)\.srt\.sjson/
settings.success jasmine.stubbedCaption
else if settings.url.match /.+\/problem_get$/
settings.success html: readFixtures('problem_content.html')
else if settings.url == '/calculate' ||
settings.url.match(/.+\/goto_position$/) ||
settings.url.match(/event$/) ||
settings.url.match(/.+\/problem_(check|reset|show|save)$/)
# do nothing
else
throw "External request attempted for #{settings.url}, which is not defined."
jasmine.stubYoutubePlayer = ->
YT.Player = ->
obj = jasmine.createSpyObj 'YT.Player', ['cueVideoById', 'getVideoEmbedCode',
'getCurrentTime', 'getPlayerState', 'getVolume', 'setVolume', 'loadVideoById',
'playVideo', 'pauseVideo', 'seekTo', 'getDuration', 'getAvailablePlaybackRates', 'setPlaybackRate']
obj['getAvailablePlaybackRates'] = jasmine.createSpy('getAvailablePlaybackRates').andReturn [0.75, 1.0, 1.25, 1.5]
obj
jasmine.stubVideoPlayer = (context, enableParts, html5=false) ->
suite = context.suite
currentPartName = suite.description while suite = suite.parentSuite
if html5 == false
loadFixtures 'video.html'
else
loadFixtures 'video_html5.html'
jasmine.stubRequests()
YT.Player = undefined
window.OldVideoPlayer = undefined
jasmine.stubYoutubePlayer()
return new Video '#example', '.75:7tqY6eQzVhE,1.0:cogebirgzzM'
# Add custom matchers
beforeEach ->
@addMatchers
toHaveAttrs: (attrs) ->
element = @.actual
result = true
if $.isEmptyObject attrs
return false
$.each attrs, (name, value) ->
result = result && element.attr(name) == value
return result
toBeInRange: (min, max) ->
return min <= @.actual && @.actual <= max
toBeInArray: (array) ->
return $.inArray(@.actual, array) > -1
@addMatchers imagediff.jasmine
# Stub jQuery.cookie
$.cookie = jasmine.createSpy('jQuery.cookie').andReturn '1.0'
# Stub jQuery.qtip
$.fn.qtip = jasmine.createSpy 'jQuery.qtip'
# Stub jQuery.scrollTo
$.fn.scrollTo = jasmine.createSpy 'jQuery.scrollTo'
(function ($, undefined) {
var oldAjaxWithPrefix = $.ajaxWithPrefix;
// Stub YouTube API.
window.YT = {
Player: function () {
var Player = jasmine.createSpyObj(
'YT.Player',
[
'cueVideoById', 'getVideoEmbedCode', 'getCurrentTime',
'getPlayerState', 'getVolume', 'setVolume',
'loadVideoById', 'getAvailablePlaybackRates', 'playVideo',
'pauseVideo', 'seekTo', 'getDuration', 'setPlaybackRate',
'getPlaybackQuality'
]
);
Player.getDuration.andReturn(60);
Player.getAvailablePlaybackRates.andReturn(['0.50', '1.0', '1.50', '2.0']);
return Player;
},
PlayerState: {
UNSTARTED: -1,
ENDED: 0,
PLAYING: 1,
PAUSED: 2,
BUFFERING: 3,
CUED: 5
},
ready: function (f) {
return f();
}
};
window.STATUS = window.YT.PlayerState;
window.onTouchBasedDevice = function () {
return navigator.userAgent.match(/iPhone|iPod|iPad/i);
};
jasmine.stubbedCaption = {
end: [
3120, 6270, 8490, 21620, 24920, 25750, 27900, 34380, 35550, 40250
],
start: [
1180, 3120, 6270, 14910, 21620, 24920, 25750, 27900, 34380, 35550
],
text: [
'MICHAEL CIMA: So let\'s do the first one here.',
'Vacancies, where do they come from?',
'Well, imagine a perfect crystal.',
'Now we know at any temperature other than absolute zero ' +
'there\'s enough',
'energy going around that some atoms will have more energy',
'than others, right?',
'There\'s a distribution.',
'If I plot energy here and number, these atoms in the crystal ' +
'will have a',
'distribution of energy.',
'And some will have quite a bit of energy, just for a moment.'
]
};
// For our purposes, we need to make sure that the function
// $.ajaxWithPrefix does not fail when during tests a captions file is
// requested. It is originally defined in file:
//
// common/static/coffee/src/ajax_prefix.js
//
// We will replace it with a function that does:
//
// 1.) Return a hard coded captions object if the file name contains
// 'Z5KLxerq05Y'.
// 2.) Behaves the same a as the original function in all other cases.
$.ajaxWithPrefix = function (url, settings) {
var data, success;
if (!settings) {
settings = url;
url = settings.url;
success = settings.success;
data = settings.data;
}
if (
url.match(/Z5KLxerq05Y/g) ||
url.match(/7tqY6eQzVhE/g) ||
url.match(/cogebirgzzM/g)
) {
if ($.isFunction(success)) {
return success(jasmine.stubbedCaption);
} else if ($.isFunction(data)) {
return data(jasmine.stubbedCaption);
}
} else {
return oldAjaxWithPrefix.apply(this, arguments);
}
};
// Time waitsFor() should wait for before failing a test.
window.WAIT_TIMEOUT = 5000;
jasmine.getFixtures().fixturesPath += 'fixtures';
jasmine.stubbedMetadata = {
'7tqY6eQzVhE': {
id: '7tqY6eQzVhE',
duration: 300
},
'cogebirgzzM': {
id: 'cogebirgzzM',
duration: 200
},
bogus: {
duration: 100
}
};
jasmine.fireEvent = function (el, eventName) {
var event;
if (document.createEvent) {
event = document.createEvent('HTMLEvents');
event.initEvent(eventName, true, true);
} else {
event = document.createEventObject();
event.eventType = eventName;
}
event.eventName = eventName;
if (document.createEvent) {
el.dispatchEvent(event);
} else {
el.fireEvent('on' + event.eventType, event);
}
};
jasmine.stubbedHtml5Speeds = ['0.75', '1.0', '1.25', '1.50'];
jasmine.stubRequests = function () {
return spyOn($, 'ajax').andCallFake(function (settings) {
var match, status, callCallback;
if (
match = settings.url
.match(/youtube\.com\/.+\/videos\/(.+)\?v=2&alt=jsonc/)
) {
status = match[1].split('_');
if (status && status[0] === 'status') {
callCallback = function (callback) {
callback.call(window, {}, status[1]);
};
return {
always: callCallback,
error: callCallback,
done: callCallback
};
} else if (settings.success) {
return settings.success({
data: jasmine.stubbedMetadata[match[1]]
});
} else {
return {
always: function (callback) {
return callback.call(window, {}, 'success');
},
done: function (callback) {
return callback.call(window, {}, 'success');
}
};
}
} else if (
match = settings.url
.match(/static(\/.*)?\/subs\/(.+)\.srt\.sjson/)
) {
return settings.success(jasmine.stubbedCaption);
} else if (settings.url.match(/.+\/problem_get$/)) {
return settings.success({
html: readFixtures('problem_content.html')
});
} else if (
settings.url === '/calculate' ||
settings.url.match(/.+\/goto_position$/) ||
settings.url.match(/event$/) ||
settings.url.match(/.+\/problem_(check|reset|show|save)$/)
) {
// Do nothing.
} else {
throw 'External request attempted for ' +
settings.url +
', which is not defined.';
}
});
};
// Add custom Jasmine matchers.
beforeEach(function () {
this.addMatchers({
toHaveAttrs: function (attrs) {
var element = this.actual,
result = true;
if ($.isEmptyObject(attrs)) {
return false;
}
$.each(attrs, function (name, value) {
return result = result && element.attr(name) === value;
});
return result;
},
toBeInRange: function (min, max) {
return min <= this.actual && this.actual <= max;
},
toBeInArray: function (array) {
return $.inArray(this.actual, array) > -1;
}
});
return this.addMatchers(imagediff.jasmine);
});
// Stub jQuery.cookie module.
$.cookie = jasmine.createSpy('jQuery.cookie').andReturn('1.0');
// # Stub jQuery.qtip module.
$.fn.qtip = jasmine.createSpy('jQuery.qtip');
// Stub jQuery.scrollTo module.
$.fn.scrollTo = jasmine.createSpy('jQuery.scrollTo');
jasmine.initializePlayer = function (fixture, params) {
var state;
if (_.isString(fixture)) {
// `fixture` is a name of a fixture file.
loadFixtures(fixture);
} else {
// `fixture` is not a string. The first parameter is an object?
if (_.isObject(fixture)) {
// The first parameter contains attributes for the main video
// DIV element.
params = fixture;
}
// "video_all.html" is the default HTML template for HTML5 video.
loadFixtures('video_all.html');
}
// If `params` is an object, assign it's properties as data attributes
// to the main video DIV element.
if (_.isObject(params)) {
$('#example')
.find('#video_id')
.data(params);
}
state = new Video('#example');
state.resizer = (function () {
var methods = [
'align',
'alignByWidthOnly',
'alignByHeightOnly',
'setParams',
'setMode'
],
obj = {};
$.each(methods, function (index, method) {
obj[method] = jasmine.createSpy(method).andReturn(obj);
});
return obj;
}());
// We return the `state` object of the newly initialized Video.
return state;
};
jasmine.initializePlayerYouTube = function () {
// "video.html" contains HTML template for a YouTube video.
return jasmine.initializePlayer('video.html');
};
}).call(this, window.jQuery);
(function () { (function (undefined) {
describe('VideoPlayer Events', function () { describe('VideoPlayer Events', function () {
var state, videoPlayer, player, videoControl, videoCaption, var state, oldOTBD;
videoProgressSlider, videoSpeedControl, videoVolumeControl,
oldOTBD;
function initialize(fixture, params) {
if (_.isString(fixture)) {
loadFixtures(fixture);
} else {
if (_.isObject(fixture)) {
params = fixture;
}
loadFixtures('video_all.html');
}
if (_.isObject(params)) {
$('#example')
.find('#video_id')
.data(params);
}
state = new Video('#example');
state.videoEl = $('video, iframe');
videoPlayer = state.videoPlayer;
player = videoPlayer.player;
videoControl = state.videoControl;
videoCaption = state.videoCaption;
videoProgressSlider = state.videoProgressSlider;
videoSpeedControl = state.videoSpeedControl;
videoVolumeControl = state.videoVolumeControl;
state.resizer = (function () {
var methods = [
'align',
'alignByWidthOnly',
'alignByHeightOnly',
'setParams',
'setMode'
],
obj = {};
$.each(methods, function (index, method) {
obj[method] = jasmine.createSpy(method).andReturn(obj);
});
return obj; describe('HTML5', function () {
}()); beforeEach(function () {
} oldOTBD = window.onTouchBasedDevice;
window.onTouchBasedDevice = jasmine
function initializeYouTube() { .createSpy('onTouchBasedDevice')
initialize('video.html'); .andReturn(null);
}
beforeEach(function () {
oldOTBD = window.onTouchBasedDevice;
window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice')
.andReturn(null);
this.oldYT = window.YT;
jasmine.stubRequests();
window.YT = {
Player: function () {
return {
getPlaybackQuality: function () {}
};
},
PlayerState: this.oldYT.PlayerState,
ready: function (callback) {
callback();
}
};
});
afterEach(function () { jasmine.stubRequests();
$('source').remove();
window.onTouchBasedDevice = oldOTBD; state = jasmine.initializePlayer();
window.YT = this.oldYT;
});
it('initialize', function(){ state.videoEl = $('video, iframe');
runs(function () {
initialize();
}); });
waitsFor(function () { afterEach(function () {
return state.el.hasClass('is-initialized'); $('source').remove();
}, 'Player is not initialized.', WAIT_TIMEOUT); window.onTouchBasedDevice = oldOTBD;
});
it('initialize', function () {
waitsFor(function () {
return state.el.hasClass('is-initialized');
}, 'Player is not initialized.', WAIT_TIMEOUT);
runs(function () { runs(function () {
expect('initialize').not.toHaveBeenTriggeredOn('.video'); expect('initialize').not.toHaveBeenTriggeredOn('.video');
});
}); });
});
it('ready', function() { it('ready', function () {
runs(function () { waitsFor(function () {
initialize(); return state.el.hasClass('is-initialized');
}, 'Player is not initialized.', WAIT_TIMEOUT);
runs(function () {
expect('ready').not.toHaveBeenTriggeredOn('.video');
});
}); });
waitsFor(function () { it('play', function () {
return state.el.hasClass('is-initialized'); state.videoPlayer.play();
}, 'Player is not initialized.', WAIT_TIMEOUT); expect('play').not.toHaveBeenTriggeredOn('.video');
});
runs(function () { it('pause', function () {
expect('ready').not.toHaveBeenTriggeredOn('.video'); state.videoPlayer.play();
state.videoPlayer.pause();
expect('pause').not.toHaveBeenTriggeredOn('.video');
}); });
});
it('play', function() { it('volumechange', function () {
initialize(); state.videoPlayer.onVolumeChange(60);
videoPlayer.play();
expect('play').not.toHaveBeenTriggeredOn('.video');
});
it('pause', function() { expect('volumechange').not.toHaveBeenTriggeredOn('.video');
initialize(); });
videoPlayer.play();
videoPlayer.pause();
expect('pause').not.toHaveBeenTriggeredOn('.video');
});
it('volumechange', function() { it('speedchange', function () {
initialize(); state.videoPlayer.onSpeedChange('2.0');
videoPlayer.onVolumeChange(60);
expect('volumechange').not.toHaveBeenTriggeredOn('.video'); expect('speedchange').not.toHaveBeenTriggeredOn('.video');
}); });
it('speedchange', function() { it('seek', function () {
initialize(); state.videoPlayer.onCaptionSeek({
videoPlayer.onSpeedChange('2.0'); time: 1,
type: 'any'
});
expect('speedchange').not.toHaveBeenTriggeredOn('.video'); expect('seek').not.toHaveBeenTriggeredOn('.video');
}); });
it('qualitychange', function() { it('ended', function () {
initializeYouTube(); state.videoPlayer.onEnded();
videoPlayer.onPlaybackQualityChange();
expect('qualitychange').not.toHaveBeenTriggeredOn('.video'); expect('ended').not.toHaveBeenTriggeredOn('.video');
});
}); });
it('seek', function() { describe('YouTube', function () {
initialize(); beforeEach(function () {
videoPlayer.onCaptionSeek({ oldOTBD = window.onTouchBasedDevice;
time: 1, window.onTouchBasedDevice = jasmine
type: 'any' .createSpy('onTouchBasedDevice')
.andReturn(null);
jasmine.stubRequests();
state = jasmine.initializePlayerYouTube();
}); });
expect('seek').not.toHaveBeenTriggeredOn('.video'); afterEach(function () {
}); $('source').remove();
window.onTouchBasedDevice = oldOTBD;
});
it('ended', function() { it('qualitychange', function () {
initialize(); state.videoPlayer.onPlaybackQualityChange();
videoPlayer.onEnded();
expect('ended').not.toHaveBeenTriggeredOn('.video'); expect('qualitychange').not.toHaveBeenTriggeredOn('.video');
});
}); });
}); });
}).call(this); }).call(this);
(function () { (function (undefined) {
describe('Video', function () { describe('Video', function () {
var oldOTBD; var oldOTBD;
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
}); });
afterEach(function () { afterEach(function () {
window.OldVideoPlayer = undefined;
$('source').remove(); $('source').remove();
}); });
...@@ -30,10 +29,6 @@ ...@@ -30,10 +29,6 @@
expect(this.state.videoType).toEqual('youtube'); expect(this.state.videoType).toEqual('youtube');
}); });
it('reset the current video player', function () {
expect(window.OldVideoPlayer).toBeUndefined();
});
it('set the elements', function () { it('set the elements', function () {
expect(this.state.el).toBe('#video_id'); expect(this.state.el).toBe('#video_id');
}); });
...@@ -76,10 +71,6 @@ ...@@ -76,10 +71,6 @@
expect(state.videoType).toEqual('html5'); expect(state.videoType).toEqual('html5');
}); });
it('reset the current video player', function () {
expect(window.OldVideoPlayer).toBeUndefined();
});
it('set the elements', function () { it('set the elements', function () {
expect(state.el).toBe('#video_id'); expect(state.el).toBe('#video_id');
}); });
......
(function () { (function (undefined) {
describe('Video HTML5Video', function () { describe('Video HTML5Video', function () {
var state, player, oldOTBD, playbackRates = [0.75, 1.0, 1.25, 1.5]; var state, oldOTBD, playbackRates = [0.75, 1.0, 1.25, 1.5];
function initialize() {
loadFixtures('video_html5.html');
state = new Video('#example');
player = state.videoPlayer.player;
}
beforeEach(function () { beforeEach(function () {
oldOTBD = window.onTouchBasedDevice; oldOTBD = window.onTouchBasedDevice;
...@@ -14,7 +8,7 @@ ...@@ -14,7 +8,7 @@
.createSpy('onTouchBasedDevice').andReturn(null); .createSpy('onTouchBasedDevice').andReturn(null);
}); });
afterEach(function() { afterEach(function () {
state = undefined; state = undefined;
$.fn.scrollTo.reset(); $.fn.scrollTo.reset();
$('.subtitles').remove(); $('.subtitles').remove();
...@@ -24,48 +18,46 @@ ...@@ -24,48 +18,46 @@
describe('on non-Touch devices', function () { describe('on non-Touch devices', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer('video_html5.html');
player.config.events.onReady = jasmine.createSpy('onReady');
state.videoPlayer.player.config.events.onReady = jasmine.createSpy('onReady');
}); });
describe('events:', function () { describe('events:', function () {
beforeEach(function () { beforeEach(function () {
spyOn(player, 'callStateChangeCallback').andCallThrough(); spyOn(state.videoPlayer.player, 'callStateChangeCallback').andCallThrough();
}); });
describe('[click]', function () { describe('[click]', function () {
describe('when player is paused', function () { describe('when player is paused', function () {
beforeEach(function () { beforeEach(function () {
spyOn(player.video, 'play').andCallThrough(); spyOn(state.videoPlayer.player.video, 'play').andCallThrough();
player.playerState = STATUS.PAUSED; state.videoPlayer.player.playerState = STATUS.PAUSED;
$(player.videoEl).trigger('click'); $(state.videoPlayer.player.videoEl).trigger('click');
}); });
it('native play event was called', function () { it('native play event was called', function () {
expect(player.video.play).toHaveBeenCalled(); expect(state.videoPlayer.player.video.play).toHaveBeenCalled();
}); });
it('player state was changed', function () { it('player state was changed', function () {
waitsFor(function () { waitsFor(function () {
return player.getPlayerState() !== STATUS.PAUSED; return state.videoPlayer.player.getPlayerState() !== STATUS.PAUSED;
}, 'Player state should be changed', WAIT_TIMEOUT); }, 'Player state should be changed', WAIT_TIMEOUT);
runs(function () { runs(function () {
expect(player.getPlayerState()) expect(state.videoPlayer.player.getPlayerState())
.toBe(STATUS.PLAYING); .toBe(STATUS.PLAYING);
}); });
}); });
it('callback was called', function () { it('callback was called', function () {
waitsFor(function () { waitsFor(function () {
var stateStatus = state.videoPlayer.player return state.videoPlayer.player.getPlayerState() !== STATUS.PAUSED;
.getPlayerState();
return stateStatus !== STATUS.PAUSED;
}, 'Player state should be changed', WAIT_TIMEOUT); }, 'Player state should be changed', WAIT_TIMEOUT);
runs(function () { runs(function () {
expect(player.callStateChangeCallback) expect(state.videoPlayer.player.callStateChangeCallback)
.toHaveBeenCalled(); .toHaveBeenCalled();
}); });
}); });
...@@ -73,33 +65,33 @@ ...@@ -73,33 +65,33 @@
describe('[player is playing]', function () { describe('[player is playing]', function () {
beforeEach(function () { beforeEach(function () {
spyOn(player.video, 'pause').andCallThrough(); spyOn(state.videoPlayer.player.video, 'pause').andCallThrough();
player.playerState = STATUS.PLAYING; state.videoPlayer.player.playerState = STATUS.PLAYING;
$(player.videoEl).trigger('click'); $(state.videoPlayer.player.videoEl).trigger('click');
}); });
it('native event was called', function () { it('native event was called', function () {
expect(player.video.pause).toHaveBeenCalled(); expect(state.videoPlayer.player.video.pause).toHaveBeenCalled();
}); });
it('player state was changed', function () { it('player state was changed', function () {
waitsFor(function () { waitsFor(function () {
return player.getPlayerState() !== STATUS.PLAYING; return state.videoPlayer.player.getPlayerState() !== STATUS.PLAYING;
}, 'Player state should be changed', WAIT_TIMEOUT); }, 'Player state should be changed', WAIT_TIMEOUT);
runs(function () { runs(function () {
expect(player.getPlayerState()) expect(state.videoPlayer.player.getPlayerState())
.toBe(STATUS.PAUSED); .toBe(STATUS.PAUSED);
}); });
}); });
it('callback was called', function () { it('callback was called', function () {
waitsFor(function () { waitsFor(function () {
return player.getPlayerState() !== STATUS.PLAYING; return state.videoPlayer.player.getPlayerState() !== STATUS.PLAYING;
}, 'Player state should be changed', WAIT_TIMEOUT); }, 'Player state should be changed', WAIT_TIMEOUT);
runs(function () { runs(function () {
expect(player.callStateChangeCallback) expect(state.videoPlayer.player.callStateChangeCallback)
.toHaveBeenCalled(); .toHaveBeenCalled();
}); });
}); });
...@@ -108,37 +100,34 @@ ...@@ -108,37 +100,34 @@
describe('[play]', function () { describe('[play]', function () {
beforeEach(function () { beforeEach(function () {
spyOn(player.video, 'play').andCallThrough(); spyOn(state.videoPlayer.player.video, 'play').andCallThrough();
player.playerState = STATUS.PAUSED; state.videoPlayer.player.playerState = STATUS.PAUSED;
player.playVideo(); state.videoPlayer.player.playVideo();
}); });
it('native event was called', function () { it('native event was called', function () {
expect(player.video.play).toHaveBeenCalled(); expect(state.videoPlayer.player.video.play).toHaveBeenCalled();
}); });
it('player state was changed', function () { it('player state was changed', function () {
waitsFor(function () { waitsFor(function () {
var state = player.getPlayerState(); return state.videoPlayer.player.getPlayerState() !== STATUS.PAUSED;
return state !== STATUS.PAUSED;
}, 'Player state should be changed', WAIT_TIMEOUT); }, 'Player state should be changed', WAIT_TIMEOUT);
runs(function () { runs(function () {
expect(player.getPlayerState()).toBe(STATUS.PLAYING); expect(state.videoPlayer.player.getPlayerState())
.toBe(STATUS.PLAYING);
}); });
}); });
it('callback was called', function () { it('callback was called', function () {
waitsFor(function () { waitsFor(function () {
var state = player.getPlayerState(); return state.videoPlayer.player.getPlayerState() !== STATUS.PAUSED;
return state !== STATUS.PAUSED;
}, 'Player state should be changed', WAIT_TIMEOUT); }, 'Player state should be changed', WAIT_TIMEOUT);
runs(function () { runs(function () {
expect(player.callStateChangeCallback) expect(state.videoPlayer.player.callStateChangeCallback)
.toHaveBeenCalled(); .toHaveBeenCalled();
}); });
}); });
...@@ -146,35 +135,36 @@ ...@@ -146,35 +135,36 @@
describe('[pause]', function () { describe('[pause]', function () {
beforeEach(function () { beforeEach(function () {
spyOn(player.video, 'pause').andCallThrough(); spyOn(state.videoPlayer.player.video, 'pause').andCallThrough();
player.playerState = STATUS.UNSTARTED; state.videoPlayer.player.playerState = STATUS.UNSTARTED;
player.playVideo(); state.videoPlayer.player.playVideo();
waitsFor(function () { waitsFor(function () {
return player.getPlayerState() !== STATUS.UNSTARTED; return state.videoPlayer.player.getPlayerState() !== STATUS.UNSTARTED;
}, 'Video never started playing', WAIT_TIMEOUT); }, 'Video never started playing', WAIT_TIMEOUT);
player.pauseVideo(); state.videoPlayer.player.pauseVideo();
}); });
it('native event was called', function () { it('native event was called', function () {
expect(player.video.pause).toHaveBeenCalled(); expect(state.videoPlayer.player.video.pause).toHaveBeenCalled();
}); });
it('player state was changed', function () { it('player state was changed', function () {
waitsFor(function () { waitsFor(function () {
return player.getPlayerState() !== STATUS.PLAYING; return state.videoPlayer.player.getPlayerState() !== STATUS.PLAYING;
}, 'Player state should be changed', WAIT_TIMEOUT); }, 'Player state should be changed', WAIT_TIMEOUT);
runs(function () { runs(function () {
expect(player.getPlayerState()).toBe(STATUS.PAUSED); expect(state.videoPlayer.player.getPlayerState())
.toBe(STATUS.PAUSED);
}); });
}); });
it('callback was called', function () { it('callback was called', function () {
waitsFor(function () { waitsFor(function () {
return player.getPlayerState() !== STATUS.PLAYING; return state.videoPlayer.player.getPlayerState() !== STATUS.PLAYING;
}, 'Player state should be changed', WAIT_TIMEOUT); }, 'Player state should be changed', WAIT_TIMEOUT);
runs(function () { runs(function () {
expect(player.callStateChangeCallback) expect(state.videoPlayer.player.callStateChangeCallback)
.toHaveBeenCalled(); .toHaveBeenCalled();
}); });
}); });
...@@ -186,13 +176,14 @@ ...@@ -186,13 +176,14 @@
'onReady called', function () 'onReady called', function ()
{ {
waitsFor(function () { waitsFor(function () {
return player.getPlayerState() !== STATUS.UNSTARTED; return state.videoPlayer.player.getPlayerState() !== STATUS.UNSTARTED;
}, 'Video cannot be played', WAIT_TIMEOUT); }, 'Video cannot be played', WAIT_TIMEOUT);
runs(function () { runs(function () {
expect(player.getPlayerState()).toBe(STATUS.PAUSED); expect(state.videoPlayer.player.getPlayerState())
expect(player.video.currentTime).toBe(0); .toBe(STATUS.PAUSED);
expect(player.config.events.onReady) expect(state.videoPlayer.player.video.currentTime).toBe(0);
expect(state.videoPlayer.player.config.events.onReady)
.toHaveBeenCalled(); .toHaveBeenCalled();
}); });
}); });
...@@ -201,20 +192,21 @@ ...@@ -201,20 +192,21 @@
describe('[ended]', function () { describe('[ended]', function () {
beforeEach(function () { beforeEach(function () {
waitsFor(function () { waitsFor(function () {
return player.getPlayerState() !== STATUS.UNSTARTED; return state.videoPlayer.player.getPlayerState() !== STATUS.UNSTARTED;
}, 'Video cannot be played', WAIT_TIMEOUT); }, 'Video cannot be played', WAIT_TIMEOUT);
}); });
it('player state was changed', function () { it('player state was changed', function () {
runs(function () { runs(function () {
jasmine.fireEvent(player.video, 'ended'); jasmine.fireEvent(state.videoPlayer.player.video, 'ended');
expect(player.getPlayerState()).toBe(STATUS.ENDED); expect(state.videoPlayer.player.getPlayerState()).toBe(STATUS.ENDED);
}); });
}); });
it('callback was called', function () { it('callback was called', function () {
jasmine.fireEvent(player.video, 'ended'); jasmine.fireEvent(state.videoPlayer.player.video, 'ended');
expect(player.callStateChangeCallback).toHaveBeenCalled(); expect(state.videoPlayer.player.callStateChangeCallback)
.toHaveBeenCalled();
}); });
}); });
}); });
...@@ -224,36 +216,36 @@ ...@@ -224,36 +216,36 @@
beforeEach(function () { beforeEach(function () {
waitsFor(function () { waitsFor(function () {
volume = player.video.volume; volume = state.videoPlayer.player.video.volume;
seek = player.video.currentTime; seek = state.videoPlayer.player.video.currentTime;
return player.playerState === STATUS.PAUSED; return state.videoPlayer.player.playerState === STATUS.PAUSED;
}, 'Video cannot be played', WAIT_TIMEOUT); }, 'Video cannot be played', WAIT_TIMEOUT);
}); });
it('pauseVideo', function () { it('pauseVideo', function () {
runs(function () { runs(function () {
spyOn(player.video, 'pause').andCallThrough(); spyOn(state.videoPlayer.player.video, 'pause').andCallThrough();
player.pauseVideo(); state.videoPlayer.player.pauseVideo();
expect(player.video.pause).toHaveBeenCalled(); expect(state.videoPlayer.player.video.pause).toHaveBeenCalled();
}); });
}); });
describe('seekTo', function () { describe('seekTo', function () {
it('set new correct value', function () { it('set new correct value', function () {
runs(function () { runs(function () {
player.seekTo(2); state.videoPlayer.player.seekTo(2);
expect(player.getCurrentTime()).toBe(2); expect(state.videoPlayer.player.getCurrentTime()).toBe(2);
}); });
}); });
it('set new inccorrect values', function () { it('set new inccorrect values', function () {
runs(function () { runs(function () {
player.seekTo(-50); state.videoPlayer.player.seekTo(-50);
expect(player.getCurrentTime()).toBe(seek); expect(state.videoPlayer.player.getCurrentTime()).toBe(seek);
player.seekTo('5'); state.videoPlayer.player.seekTo('5');
expect(player.getCurrentTime()).toBe(seek); expect(state.videoPlayer.player.getCurrentTime()).toBe(seek);
player.seekTo(500000); state.videoPlayer.player.seekTo(500000);
expect(player.getCurrentTime()).toBe(seek); expect(state.videoPlayer.player.getCurrentTime()).toBe(seek);
}); });
}); });
}); });
...@@ -261,88 +253,89 @@ ...@@ -261,88 +253,89 @@
describe('setVolume', function () { describe('setVolume', function () {
it('set new correct value', function () { it('set new correct value', function () {
runs(function () { runs(function () {
player.setVolume(50); state.videoPlayer.player.setVolume(50);
expect(player.getVolume()).toBe(50 * 0.01); expect(state.videoPlayer.player.getVolume()).toBe(50 * 0.01);
}); });
}); });
it('set new incorrect values', function () { it('set new incorrect values', function () {
runs(function () { runs(function () {
player.setVolume(-50); state.videoPlayer.player.setVolume(-50);
expect(player.getVolume()).toBe(volume); expect(state.videoPlayer.player.getVolume()).toBe(volume);
player.setVolume('5'); state.videoPlayer.player.setVolume('5');
expect(player.getVolume()).toBe(volume); expect(state.videoPlayer.player.getVolume()).toBe(volume);
player.setVolume(500000); state.videoPlayer.player.setVolume(500000);
expect(player.getVolume()).toBe(volume); expect(state.videoPlayer.player.getVolume()).toBe(volume);
}); });
}); });
}); });
it('getCurrentTime', function () { it('getCurrentTime', function () {
runs(function () { runs(function () {
player.video.currentTime = 3; state.videoPlayer.player.video.currentTime = 3;
expect(player.getCurrentTime()) expect(state.videoPlayer.player.getCurrentTime())
.toBe(player.video.currentTime); .toBe(state.videoPlayer.player.video.currentTime);
}); });
}); });
it('playVideo', function () { it('playVideo', function () {
runs(function () { runs(function () {
spyOn(player.video, 'play').andCallThrough(); spyOn(state.videoPlayer.player.video, 'play').andCallThrough();
player.playVideo(); state.videoPlayer.player.playVideo();
expect(player.video.play).toHaveBeenCalled(); expect(state.videoPlayer.player.video.play).toHaveBeenCalled();
}); });
}); });
it('getPlayerState', function () { it('getPlayerState', function () {
runs(function () { runs(function () {
player.playerState = STATUS.PLAYING; state.videoPlayer.player.playerState = STATUS.PLAYING;
expect(player.getPlayerState()).toBe(STATUS.PLAYING); expect(state.videoPlayer.player.getPlayerState()).toBe(STATUS.PLAYING);
player.playerState = STATUS.ENDED; state.videoPlayer.player.playerState = STATUS.ENDED;
expect(player.getPlayerState()).toBe(STATUS.ENDED); expect(state.videoPlayer.player.getPlayerState()).toBe(STATUS.ENDED);
}); });
}); });
it('getVolume', function () { it('getVolume', function () {
runs(function () { runs(function () {
volume = player.video.volume = 0.5; volume = state.videoPlayer.player.video.volume = 0.5;
expect(player.getVolume()).toBe(volume); expect(state.videoPlayer.player.getVolume()).toBe(volume);
}); });
}); });
it('getDuration', function () { it('getDuration', function () {
runs(function () { runs(function () {
duration = player.video.duration; duration = state.videoPlayer.player.video.duration;
expect(player.getDuration()).toBe(duration); expect(state.videoPlayer.player.getDuration()).toBe(duration);
}); });
}); });
describe('setPlaybackRate', function () { describe('setPlaybackRate', function () {
it('set a correct value', function () { it('set a correct value', function () {
playbackRate = 1.5; playbackRate = 1.5;
player.setPlaybackRate(playbackRate); state.videoPlayer.player.setPlaybackRate(playbackRate);
expect(player.video.playbackRate).toBe(playbackRate); expect(state.videoPlayer.player.video.playbackRate).toBe(playbackRate);
}); });
it('set NaN value', function () { it('set NaN value', function () {
var oldPlaybackRate = player.video.playbackRate; var oldPlaybackRate = state.videoPlayer.player.video.playbackRate;
// When we try setting the playback rate to some // When we try setting the playback rate to some
// non-numerical value, nothing should happen. // non-numerical value, nothing should happen.
playbackRate = NaN; playbackRate = NaN;
player.setPlaybackRate(playbackRate); state.videoPlayer.player.setPlaybackRate(playbackRate);
expect(player.video.playbackRate).toBe(oldPlaybackRate); expect(state.videoPlayer.player.video.playbackRate)
.toBe(oldPlaybackRate);
}); });
}); });
it('getAvailablePlaybackRates', function () { it('getAvailablePlaybackRates', function () {
expect(player.getAvailablePlaybackRates()) expect(state.videoPlayer.player.getAvailablePlaybackRates())
.toEqual(playbackRates); .toEqual(playbackRates);
}); });
it('_getLogs', function () { it('_getLogs', function () {
runs(function () { runs(function () {
var logs = player._getLogs(); var logs = state.videoPlayer.player._getLogs();
expect(logs).toEqual(jasmine.any(Array)); expect(logs).toEqual(jasmine.any(Array));
expect(logs.length).toBeGreaterThan(0); expect(logs.length).toBeGreaterThan(0);
}); });
...@@ -352,8 +345,10 @@ ...@@ -352,8 +345,10 @@
it('native controls are used on iPhone', function () { it('native controls are used on iPhone', function () {
window.onTouchBasedDevice.andReturn(['iPhone']); window.onTouchBasedDevice.andReturn(['iPhone']);
initialize();
player.config.events.onReady = jasmine.createSpy('onReady'); state = jasmine.initializePlayer('video_html5.html');
state.videoPlayer.player.config.events.onReady = jasmine.createSpy('onReady');
expect($('video')).toHaveAttr('controls'); expect($('video')).toHaveAttr('controls');
}); });
......
(function (requirejs, require, define) { (function (requirejs, require, define, undefined) {
require( require(
['video/00_resizer.js'], ['video/00_resizer.js'],
...@@ -104,7 +104,7 @@ function (Resizer) { ...@@ -104,7 +104,7 @@ function (Resizer) {
beforeEach(function () { beforeEach(function () {
var spiesCount = _.range(3); var spiesCount = _.range(3);
spiesList = $.map(spiesCount, function() { spiesList = $.map(spiesCount, function () {
return jasmine.createSpy(); return jasmine.createSpy();
}); });
...@@ -113,13 +113,13 @@ function (Resizer) { ...@@ -113,13 +113,13 @@ function (Resizer) {
it('callbacks are called', function () { it('callbacks are called', function () {
$.each(spiesList, function(index, spy) { $.each(spiesList, function (index, spy) {
resizer.callbacks.add(spy); resizer.callbacks.add(spy);
}); });
resizer.align(); resizer.align();
$.each(spiesList, function(index, spy) { $.each(spiesList, function (index, spy) {
expect(spy).toHaveBeenCalled(); expect(spy).toHaveBeenCalled();
}); });
}); });
...@@ -135,20 +135,20 @@ function (Resizer) { ...@@ -135,20 +135,20 @@ function (Resizer) {
}); });
it('All callbacks are removed', function () { it('All callbacks are removed', function () {
$.each(spiesList, function(index, spy) { $.each(spiesList, function (index, spy) {
resizer.callbacks.add(spy); resizer.callbacks.add(spy);
}); });
resizer.callbacks.removeAll(); resizer.callbacks.removeAll();
resizer.align(); resizer.align();
$.each(spiesList, function(index, spy) { $.each(spiesList, function (index, spy) {
expect(spy).not.toHaveBeenCalled(); expect(spy).not.toHaveBeenCalled();
}); });
}); });
it('Specific callback is removed', function () { it('Specific callback is removed', function () {
$.each(spiesList, function(index, spy) { $.each(spiesList, function (index, spy) {
resizer.callbacks.add(spy); resizer.callbacks.add(spy);
}); });
...@@ -158,14 +158,17 @@ function (Resizer) { ...@@ -158,14 +158,17 @@ function (Resizer) {
expect(spiesList[1]).not.toHaveBeenCalled(); expect(spiesList[1]).not.toHaveBeenCalled();
}); });
it('Error message is shown when wrong argument type is passed', function () { it(
'Error message is shown when wrong argument type is passed',
function ()
{
var methods = ['add', 'once'], var methods = ['add', 'once'],
errorMessage = 'TypeError: Argument is not a function.', errorMessage = 'TypeError: Argument is not a function.',
arg = {}; arg = {};
spyOn(console, 'error'); spyOn(console, 'error');
$.each(methods, function(index, methodName) { $.each(methods, function (index, methodName) {
resizer.callbacks[methodName](arg); resizer.callbacks[methodName](arg);
expect(console.error).toHaveBeenCalledWith(errorMessage); expect(console.error).toHaveBeenCalledWith(errorMessage);
//reset spy //reset spy
......
(function () { (function (undefined) {
describe('VideoCaption', function () { describe('VideoCaption', function () {
var state, videoPlayer, videoCaption, videoSpeedControl, oldOTBD; var state, oldOTBD;
function initialize() {
loadFixtures('video_all.html');
state = new Video('#example');
videoPlayer = state.videoPlayer;
videoCaption = state.videoCaption;
videoSpeedControl = state.videoSpeedControl;
videoControl = state.videoControl;
$.fn.scrollTo.reset();
}
beforeEach(function () { beforeEach(function () {
oldOTBD = window.onTouchBasedDevice; oldOTBD = window.onTouchBasedDevice;
window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice') window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice')
.andReturn(null); .andReturn(null);
initialize();
state = jasmine.initializePlayer();
videoControl = state.videoControl;
$.fn.scrollTo.reset();
}); });
afterEach(function () { afterEach(function () {
YT.Player = undefined;
$('.subtitles').remove(); $('.subtitles').remove();
// `source` tags should be removed to avoid memory leak bug that we // `source` tags should be removed to avoid memory leak bug that we
...@@ -35,7 +29,12 @@ ...@@ -35,7 +29,12 @@
describe('always', function () { describe('always', function () {
beforeEach(function () { beforeEach(function () {
spyOn($, 'ajaxWithPrefix').andCallThrough(); spyOn($, 'ajaxWithPrefix').andCallThrough();
initialize();
state = jasmine.initializePlayer();
videoControl = state.videoControl;
$.fn.scrollTo.reset();
}); });
it('create the caption element', function () { it('create the caption element', function () {
...@@ -57,7 +56,7 @@ ...@@ -57,7 +56,7 @@
it('fetch the caption', function () { it('fetch the caption', function () {
waitsFor(function () { waitsFor(function () {
if (videoCaption.loaded === true) { if (state.videoCaption.loaded === true) {
return true; return true;
} }
...@@ -66,7 +65,7 @@ ...@@ -66,7 +65,7 @@
runs(function () { runs(function () {
expect($.ajaxWithPrefix).toHaveBeenCalledWith({ expect($.ajaxWithPrefix).toHaveBeenCalledWith({
url: videoCaption.captionURL(), url: state.videoCaption.captionURL(),
notifyOnError: false, notifyOnError: false,
success: jasmine.any(Function), success: jasmine.any(Function),
error: jasmine.any(Function) error: jasmine.any(Function)
...@@ -76,37 +75,37 @@ ...@@ -76,37 +75,37 @@
it('bind window resize event', function () { it('bind window resize event', function () {
expect($(window)).toHandleWith( expect($(window)).toHandleWith(
'resize', videoCaption.resize 'resize', state.videoCaption.resize
); );
}); });
it('bind the hide caption button', function () { it('bind the hide caption button', function () {
expect($('.hide-subtitles')).toHandleWith( expect($('.hide-subtitles')).toHandleWith(
'click', videoCaption.toggle 'click', state.videoCaption.toggle
); );
}); });
it('bind the mouse movement', function () { it('bind the mouse movement', function () {
expect($('.subtitles')).toHandleWith( expect($('.subtitles')).toHandleWith(
'mouseover', videoCaption.onMouseEnter 'mouseover', state.videoCaption.onMouseEnter
); );
expect($('.subtitles')).toHandleWith( expect($('.subtitles')).toHandleWith(
'mouseout', videoCaption.onMouseLeave 'mouseout', state.videoCaption.onMouseLeave
); );
expect($('.subtitles')).toHandleWith( expect($('.subtitles')).toHandleWith(
'mousemove', videoCaption.onMovement 'mousemove', state.videoCaption.onMovement
); );
expect($('.subtitles')).toHandleWith( expect($('.subtitles')).toHandleWith(
'mousewheel', videoCaption.onMovement 'mousewheel', state.videoCaption.onMovement
); );
expect($('.subtitles')).toHandleWith( expect($('.subtitles')).toHandleWith(
'DOMMouseScroll', videoCaption.onMovement 'DOMMouseScroll', state.videoCaption.onMovement
); );
}); });
it('bind the scroll', function () { it('bind the scroll', function () {
expect($('.subtitles')) expect($('.subtitles'))
.toHandleWith('scroll', videoCaption.autoShowCaptions); .toHandleWith('scroll', state.videoCaption.autoShowCaptions);
expect($('.subtitles')) expect($('.subtitles'))
.toHandleWith('scroll', videoControl.showControls); .toHandleWith('scroll', videoControl.showControls);
}); });
...@@ -114,7 +113,11 @@ ...@@ -114,7 +113,11 @@
describe('when on a non touch-based device', function () { describe('when on a non touch-based device', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
videoControl = state.videoControl;
$.fn.scrollTo.reset();
}); });
it('render the caption', function () { it('render the caption', function () {
...@@ -145,38 +148,43 @@ ...@@ -145,38 +148,43 @@
function (index, link) { function (index, link) {
expect($(link)).toHandleWith( expect($(link)).toHandleWith(
'mouseover', videoCaption.captionMouseOverOut 'mouseover', state.videoCaption.captionMouseOverOut
); );
expect($(link)).toHandleWith( expect($(link)).toHandleWith(
'mouseout', videoCaption.captionMouseOverOut 'mouseout', state.videoCaption.captionMouseOverOut
); );
expect($(link)).toHandleWith( expect($(link)).toHandleWith(
'mousedown', videoCaption.captionMouseDown 'mousedown', state.videoCaption.captionMouseDown
); );
expect($(link)).toHandleWith( expect($(link)).toHandleWith(
'click', videoCaption.captionClick 'click', state.videoCaption.captionClick
); );
expect($(link)).toHandleWith( expect($(link)).toHandleWith(
'focus', videoCaption.captionFocus 'focus', state.videoCaption.captionFocus
); );
expect($(link)).toHandleWith( expect($(link)).toHandleWith(
'blur', videoCaption.captionBlur 'blur', state.videoCaption.captionBlur
); );
expect($(link)).toHandleWith( expect($(link)).toHandleWith(
'keydown', videoCaption.captionKeyDown 'keydown', state.videoCaption.captionKeyDown
); );
}); });
}); });
it('set rendered to true', function () { it('set rendered to true', function () {
expect(videoCaption.rendered).toBeTruthy(); expect(state.videoCaption.rendered).toBeTruthy();
}); });
}); });
describe('when on a touch-based device', function () { describe('when on a touch-based device', function () {
beforeEach(function () { beforeEach(function () {
window.onTouchBasedDevice.andReturn(['iPad']); window.onTouchBasedDevice.andReturn(['iPad']);
initialize();
state = jasmine.initializePlayer();
videoControl = state.videoControl;
$.fn.scrollTo.reset();
}); });
it('show explaination message', function () { it('show explaination message', function () {
...@@ -187,7 +195,7 @@ ...@@ -187,7 +195,7 @@
}); });
it('does not set rendered to true', function () { it('does not set rendered to true', function () {
expect(videoCaption.rendered).toBeFalsy(); expect(state.videoCaption.rendered).toBeFalsy();
}); });
}); });
...@@ -199,11 +207,10 @@ ...@@ -199,11 +207,10 @@
$('#example').find('#video_id').data('sub', ''); $('#example').find('#video_id').data('sub', '');
state = new Video('#example'); state = new Video('#example');
videoCaption = state.videoCaption;
}); });
it('captions panel is not shown', function () { it('captions panel is not shown', function () {
expect(videoCaption.hideSubtitlesEl).toBeHidden(); expect(state.videoCaption.hideSubtitlesEl).toBeHidden();
}); });
}); });
}); });
...@@ -221,20 +228,20 @@ ...@@ -221,20 +228,20 @@
}); });
it('does not set freezing timeout', function () { it('does not set freezing timeout', function () {
expect(videoCaption.frozen).toBeFalsy(); expect(state.videoCaption.frozen).toBeFalsy();
}); });
}); });
describe('when cursor is in the caption box', function () { describe('when cursor is in the caption box', function () {
beforeEach(function () { beforeEach(function () {
spyOn(videoCaption, 'onMouseLeave'); spyOn(state.videoCaption, 'onMouseLeave');
$('.subtitles').trigger(jQuery.Event('mouseenter')); $('.subtitles').trigger(jQuery.Event('mouseenter'));
jasmine.Clock.tick(state.config.captionsFreezeTime); jasmine.Clock.tick(state.config.captionsFreezeTime);
}); });
it('set the freezing timeout', function () { it('set the freezing timeout', function () {
expect(videoCaption.frozen).not.toBeFalsy(); expect(state.videoCaption.frozen).not.toBeFalsy();
expect(videoCaption.onMouseLeave).toHaveBeenCalled(); expect(state.videoCaption.onMouseLeave).toHaveBeenCalled();
}); });
describe('when the cursor is moving', function () { describe('when the cursor is moving', function () {
...@@ -263,7 +270,7 @@ ...@@ -263,7 +270,7 @@
function () { function () {
beforeEach(function () { beforeEach(function () {
videoCaption.frozen = 100; state.videoCaption.frozen = 100;
$.fn.scrollTo.reset(); $.fn.scrollTo.reset();
}); });
...@@ -277,13 +284,13 @@ ...@@ -277,13 +284,13 @@
}); });
it('unfreeze the caption', function () { it('unfreeze the caption', function () {
expect(videoCaption.frozen).toBeNull(); expect(state.videoCaption.frozen).toBeNull();
}); });
}); });
describe('when the player is playing', function () { describe('when the player is playing', function () {
beforeEach(function () { beforeEach(function () {
videoCaption.playing = true; state.videoCaption.playing = true;
$('.subtitles li[data-index]:first') $('.subtitles li[data-index]:first')
.addClass('current'); .addClass('current');
$('.subtitles').trigger(jQuery.Event('mouseout')); $('.subtitles').trigger(jQuery.Event('mouseout'));
...@@ -296,7 +303,7 @@ ...@@ -296,7 +303,7 @@
describe('when the player is not playing', function () { describe('when the player is not playing', function () {
beforeEach(function () { beforeEach(function () {
videoCaption.playing = false; state.videoCaption.playing = false;
$('.subtitles').trigger(jQuery.Event('mouseout')); $('.subtitles').trigger(jQuery.Event('mouseout'));
}); });
...@@ -309,12 +316,12 @@ ...@@ -309,12 +316,12 @@
describe('search', function () { describe('search', function () {
it('return a correct caption index', function () { it('return a correct caption index', function () {
expect(videoCaption.search(0)).toEqual(-1); expect(state.videoCaption.search(0)).toEqual(-1);
expect(videoCaption.search(3120)).toEqual(1); expect(state.videoCaption.search(3120)).toEqual(1);
expect(videoCaption.search(6270)).toEqual(2); expect(state.videoCaption.search(6270)).toEqual(2);
expect(videoCaption.search(8490)).toEqual(2); expect(state.videoCaption.search(8490)).toEqual(2);
expect(videoCaption.search(21620)).toEqual(4); expect(state.videoCaption.search(21620)).toEqual(4);
expect(videoCaption.search(24920)).toEqual(5); expect(state.videoCaption.search(24920)).toEqual(5);
}); });
}); });
...@@ -322,8 +329,14 @@ ...@@ -322,8 +329,14 @@
describe('when the caption was not rendered', function () { describe('when the caption was not rendered', function () {
beforeEach(function () { beforeEach(function () {
window.onTouchBasedDevice.andReturn(['iPad']); window.onTouchBasedDevice.andReturn(['iPad']);
initialize();
videoCaption.play(); state = jasmine.initializePlayer();
videoControl = state.videoControl;
$.fn.scrollTo.reset();
state.videoCaption.play();
}); });
it('render the caption', function () { it('render the caption', function () {
...@@ -352,78 +365,78 @@ ...@@ -352,78 +365,78 @@
function (index, link) { function (index, link) {
expect($(link)).toHandleWith( expect($(link)).toHandleWith(
'mouseover', videoCaption.captionMouseOverOut 'mouseover', state.videoCaption.captionMouseOverOut
); );
expect($(link)).toHandleWith( expect($(link)).toHandleWith(
'mouseout', videoCaption.captionMouseOverOut 'mouseout', state.videoCaption.captionMouseOverOut
); );
expect($(link)).toHandleWith( expect($(link)).toHandleWith(
'mousedown', videoCaption.captionMouseDown 'mousedown', state.videoCaption.captionMouseDown
); );
expect($(link)).toHandleWith( expect($(link)).toHandleWith(
'click', videoCaption.captionClick 'click', state.videoCaption.captionClick
); );
expect($(link)).toHandleWith( expect($(link)).toHandleWith(
'focus', videoCaption.captionFocus 'focus', state.videoCaption.captionFocus
); );
expect($(link)).toHandleWith( expect($(link)).toHandleWith(
'blur', videoCaption.captionBlur 'blur', state.videoCaption.captionBlur
); );
expect($(link)).toHandleWith( expect($(link)).toHandleWith(
'keydown', videoCaption.captionKeyDown 'keydown', state.videoCaption.captionKeyDown
); );
}); });
}); });
it('set rendered to true', function () { it('set rendered to true', function () {
expect(videoCaption.rendered).toBeTruthy(); expect(state.videoCaption.rendered).toBeTruthy();
}); });
it('set playing to true', function () { it('set playing to true', function () {
expect(videoCaption.playing).toBeTruthy(); expect(state.videoCaption.playing).toBeTruthy();
}); });
}); });
}); });
describe('pause', function () { describe('pause', function () {
beforeEach(function () { beforeEach(function () {
videoCaption.playing = true; state.videoCaption.playing = true;
videoCaption.pause(); state.videoCaption.pause();
}); });
it('set playing to false', function () { it('set playing to false', function () {
expect(videoCaption.playing).toBeFalsy(); expect(state.videoCaption.playing).toBeFalsy();
}); });
}); });
describe('updatePlayTime', function () { describe('updatePlayTime', function () {
describe('when the video speed is 1.0x', function () { describe('when the video speed is 1.0x', function () {
beforeEach(function () { beforeEach(function () {
videoSpeedControl.currentSpeed = '1.0'; state.videoSpeedControl.currentSpeed = '1.0';
videoCaption.updatePlayTime(25.000); state.videoCaption.updatePlayTime(25.000);
}); });
it('search the caption based on time', function () { it('search the caption based on time', function () {
expect(videoCaption.currentIndex).toEqual(5); expect(state.videoCaption.currentIndex).toEqual(5);
}); });
}); });
describe('when the video speed is not 1.0x', function () { describe('when the video speed is not 1.0x', function () {
beforeEach(function () { beforeEach(function () {
videoSpeedControl.currentSpeed = '0.75'; state.videoSpeedControl.currentSpeed = '0.75';
videoCaption.updatePlayTime(25.000); state.videoCaption.updatePlayTime(25.000);
}); });
it('search the caption based on 1.0x speed', function () { it('search the caption based on 1.0x speed', function () {
expect(videoCaption.currentIndex).toEqual(5); expect(state.videoCaption.currentIndex).toEqual(5);
}); });
}); });
describe('when the index is not the same', function () { describe('when the index is not the same', function () {
beforeEach(function () { beforeEach(function () {
videoCaption.currentIndex = 1; state.videoCaption.currentIndex = 1;
$('.subtitles li[data-index=5]').addClass('current'); $('.subtitles li[data-index=5]').addClass('current');
videoCaption.updatePlayTime(25.000); state.videoCaption.updatePlayTime(25.000);
}); });
it('deactivate the previous caption', function () { it('deactivate the previous caption', function () {
...@@ -437,7 +450,7 @@ ...@@ -437,7 +450,7 @@
}); });
it('save new index', function () { it('save new index', function () {
expect(videoCaption.currentIndex).toEqual(5); expect(state.videoCaption.currentIndex).toEqual(5);
}); });
// Disabled 11/25/13 due to flakiness in master // Disabled 11/25/13 due to flakiness in master
...@@ -448,9 +461,9 @@ ...@@ -448,9 +461,9 @@
describe('when the index is the same', function () { describe('when the index is the same', function () {
beforeEach(function () { beforeEach(function () {
videoCaption.currentIndex = 1; state.videoCaption.currentIndex = 1;
$('.subtitles li[data-index=3]').addClass('current'); $('.subtitles li[data-index=3]').addClass('current');
videoCaption.updatePlayTime(15.000); state.videoCaption.updatePlayTime(15.000);
}); });
it('does not change current subtitle', function () { it('does not change current subtitle', function () {
...@@ -462,9 +475,14 @@ ...@@ -462,9 +475,14 @@
describe('resize', function () { describe('resize', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
videoControl = state.videoControl;
$.fn.scrollTo.reset();
$('.subtitles li[data-index=1]').addClass('current'); $('.subtitles li[data-index=1]').addClass('current');
videoCaption.resize(); state.videoCaption.resize();
}); });
describe('set the height of caption container', function () { describe('set the height of caption container', function () {
...@@ -484,7 +502,7 @@ ...@@ -484,7 +502,7 @@
controlHeight, shouldBeHeight; controlHeight, shouldBeHeight;
state.captionsHidden = true; state.captionsHidden = true;
videoCaption.setSubtitlesHeight(); state.videoCaption.setSubtitlesHeight();
realHeight = parseInt( realHeight = parseInt(
$('.subtitles').css('maxHeight'), 10 $('.subtitles').css('maxHeight'), 10
...@@ -510,9 +528,9 @@ ...@@ -510,9 +528,9 @@
$('.subtitles .spacing:last').css('height'), 10 $('.subtitles .spacing:last').css('height'), 10
)); ));
expect(firstSpacing - videoCaption.topSpacingHeight()) expect(firstSpacing - state.videoCaption.topSpacingHeight())
.toBeLessThan(1); .toBeLessThan(1);
expect(lastSpacing - videoCaption.bottomSpacingHeight()) expect(lastSpacing - state.videoCaption.bottomSpacingHeight())
.toBeLessThan(1); .toBeLessThan(1);
}); });
...@@ -524,14 +542,18 @@ ...@@ -524,14 +542,18 @@
// Disabled 11/25/13 due to flakiness in master // Disabled 11/25/13 due to flakiness in master
xdescribe('scrollCaption', function () { xdescribe('scrollCaption', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
videoControl = state.videoControl;
$.fn.scrollTo.reset();
}); });
describe('when frozen', function () { describe('when frozen', function () {
beforeEach(function () { beforeEach(function () {
videoCaption.frozen = true; state.videoCaption.frozen = true;
$('.subtitles li[data-index=1]').addClass('current'); $('.subtitles li[data-index=1]').addClass('current');
videoCaption.scrollCaption(); state.videoCaption.scrollCaption();
}); });
it('does not scroll the caption', function () { it('does not scroll the caption', function () {
...@@ -541,12 +563,12 @@ ...@@ -541,12 +563,12 @@
describe('when not frozen', function () { describe('when not frozen', function () {
beforeEach(function () { beforeEach(function () {
videoCaption.frozen = false; state.videoCaption.frozen = false;
}); });
describe('when there is no current caption', function () { describe('when there is no current caption', function () {
beforeEach(function () { beforeEach(function () {
videoCaption.scrollCaption(); state.videoCaption.scrollCaption();
}); });
it('does not scroll the caption', function () { it('does not scroll the caption', function () {
...@@ -557,7 +579,7 @@ ...@@ -557,7 +579,7 @@
describe('when there is a current caption', function () { describe('when there is a current caption', function () {
beforeEach(function () { beforeEach(function () {
$('.subtitles li[data-index=1]').addClass('current'); $('.subtitles li[data-index=1]').addClass('current');
videoCaption.scrollCaption(); state.videoCaption.scrollCaption();
}); });
it('scroll to current caption', function () { it('scroll to current caption', function () {
...@@ -571,24 +593,29 @@ ...@@ -571,24 +593,29 @@
xdescribe('seekPlayer', function () { xdescribe('seekPlayer', function () {
describe('when the video speed is 1.0x', function () { describe('when the video speed is 1.0x', function () {
beforeEach(function () { beforeEach(function () {
videoSpeedControl.currentSpeed = '1.0'; state.videoSpeedControl.currentSpeed = '1.0';
$('.subtitles li[data-start="14910"]').trigger('click'); $('.subtitles li[data-start="14910"]').trigger('click');
}); });
it('trigger seek event with the correct time', function () { it('trigger seek event with the correct time', function () {
expect(videoPlayer.currentTime).toEqual(14.91); expect(state.videoPlayer.currentTime).toEqual(14.91);
}); });
}); });
describe('when the video speed is not 1.0x', function () { describe('when the video speed is not 1.0x', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
videoSpeedControl.currentSpeed = '0.75';
videoControl = state.videoControl;
$.fn.scrollTo.reset();
state.videoSpeedControl.currentSpeed = '0.75';
$('.subtitles li[data-start="14910"]').trigger('click'); $('.subtitles li[data-start="14910"]').trigger('click');
}); });
it('trigger seek event with the correct time', function () { it('trigger seek event with the correct time', function () {
expect(videoPlayer.currentTime).toEqual(14.91); expect(state.videoPlayer.currentTime).toEqual(14.91);
}); });
}); });
...@@ -596,36 +623,46 @@ ...@@ -596,36 +623,46 @@
function () { function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
videoSpeedControl.currentSpeed = '0.75';
videoControl = state.videoControl;
$.fn.scrollTo.reset();
state.videoSpeedControl.currentSpeed = '0.75';
state.currentPlayerMode = 'flash'; state.currentPlayerMode = 'flash';
$('.subtitles li[data-start="14910"]').trigger('click'); $('.subtitles li[data-start="14910"]').trigger('click');
}); });
it('trigger seek event with the correct time', function () { it('trigger seek event with the correct time', function () {
expect(videoPlayer.currentTime).toEqual(15); expect(state.videoPlayer.currentTime).toEqual(15);
}); });
}); });
}); });
describe('toggle', function () { describe('toggle', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
spyOn(videoPlayer, 'log');
videoControl = state.videoControl;
$.fn.scrollTo.reset();
spyOn(state.videoPlayer, 'log');
$('.subtitles li[data-index=1]').addClass('current'); $('.subtitles li[data-index=1]').addClass('current');
}); });
describe('when the caption is visible', function () { describe('when the caption is visible', function () {
beforeEach(function () { beforeEach(function () {
state.el.removeClass('closed'); state.el.removeClass('closed');
videoCaption.toggle(jQuery.Event('click')); state.videoCaption.toggle(jQuery.Event('click'));
}); });
it('log the hide_transcript event', function () { it('log the hide_transcript event', function () {
expect(videoPlayer.log).toHaveBeenCalledWith( expect(state.videoPlayer.log).toHaveBeenCalledWith(
'hide_transcript', 'hide_transcript',
{ {
currentTime: videoPlayer.currentTime currentTime: state.videoPlayer.currentTime
} }
); );
}); });
...@@ -643,16 +680,16 @@ ...@@ -643,16 +680,16 @@
describe('when the caption is hidden', function () { describe('when the caption is hidden', function () {
beforeEach(function () { beforeEach(function () {
state.el.addClass('closed'); state.el.addClass('closed');
videoCaption.toggle(jQuery.Event('click')); state.videoCaption.toggle(jQuery.Event('click'));
jasmine.Clock.useMock(); jasmine.Clock.useMock();
}); });
it('log the show_transcript event', function () { it('log the show_transcript event', function () {
expect(videoPlayer.log).toHaveBeenCalledWith( expect(state.videoPlayer.log).toHaveBeenCalledWith(
'show_transcript', 'show_transcript',
{ {
currentTime: videoPlayer.currentTime currentTime: state.videoPlayer.currentTime
} }
); );
}); });
...@@ -685,12 +722,16 @@ ...@@ -685,12 +722,16 @@
describe('caption accessibility', function () { describe('caption accessibility', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
videoControl = state.videoControl;
$.fn.scrollTo.reset();
}); });
describe('when getting focus through TAB key', function () { describe('when getting focus through TAB key', function () {
beforeEach(function () { beforeEach(function () {
videoCaption.isMouseFocus = false; state.videoCaption.isMouseFocus = false;
$('.subtitles li[data-index=0]').trigger( $('.subtitles li[data-index=0]').trigger(
jQuery.Event('focus') jQuery.Event('focus')
); );
...@@ -702,7 +743,7 @@ ...@@ -702,7 +743,7 @@
}); });
it('has automatic scrolling disabled', function () { it('has automatic scrolling disabled', function () {
expect(videoCaption.autoScrolling).toBe(false); expect(state.videoCaption.autoScrolling).toBe(false);
}); });
}); });
...@@ -719,7 +760,7 @@ ...@@ -719,7 +760,7 @@
}); });
it('has automatic scrolling enabled', function () { it('has automatic scrolling enabled', function () {
expect(videoCaption.autoScrolling).toBe(true); expect(state.videoCaption.autoScrolling).toBe(true);
}); });
}); });
...@@ -729,7 +770,7 @@ ...@@ -729,7 +770,7 @@
function () { function () {
beforeEach(function () { beforeEach(function () {
videoCaption.isMouseFocus = false; state.videoCaption.isMouseFocus = false;
$('.subtitles li[data-index=0]') $('.subtitles li[data-index=0]')
.trigger(jQuery.Event('focus')); .trigger(jQuery.Event('focus'));
$('.subtitles li[data-index=0]') $('.subtitles li[data-index=0]')
...@@ -742,7 +783,7 @@ ...@@ -742,7 +783,7 @@
}); });
it('has automatic scrolling enabled', function () { it('has automatic scrolling enabled', function () {
expect(videoCaption.autoScrolling).toBe(true); expect(state.videoCaption.autoScrolling).toBe(true);
}); });
}); });
...@@ -757,12 +798,12 @@ ...@@ -757,12 +798,12 @@
subDataLiIdx__0 = $('.subtitles li[data-index=0]'); subDataLiIdx__0 = $('.subtitles li[data-index=0]');
subDataLiIdx__1 = $('.subtitles li[data-index=1]'); subDataLiIdx__1 = $('.subtitles li[data-index=1]');
videoCaption.isMouseFocus = false; state.videoCaption.isMouseFocus = false;
subDataLiIdx__0.trigger(jQuery.Event('focus')); subDataLiIdx__0.trigger(jQuery.Event('focus'));
subDataLiIdx__0.trigger(jQuery.Event('blur')); subDataLiIdx__0.trigger(jQuery.Event('blur'));
videoCaption.isMouseFocus = true; state.videoCaption.isMouseFocus = true;
subDataLiIdx__1.trigger(jQuery.Event('mousedown')); subDataLiIdx__1.trigger(jQuery.Event('mousedown'));
}); });
...@@ -776,7 +817,7 @@ ...@@ -776,7 +817,7 @@
}); });
it('has automatic scrolling enabled', function () { it('has automatic scrolling enabled', function () {
expect(videoCaption.autoScrolling).toBe(true); expect(state.videoCaption.autoScrolling).toBe(true);
}); });
}); });
}); });
......
(function() { (function (undefined) {
describe('VideoControl', function() { describe('VideoControl', function () {
var state, videoControl, oldOTBD; var state, oldOTBD;
function initialize(fixture) { beforeEach(function () {
if (fixture) { oldOTBD = window.onTouchBasedDevice;
loadFixtures(fixture); window.onTouchBasedDevice = jasmine
} else { .createSpy('onTouchBasedDevice').andReturn(null);
loadFixtures('video_all.html');
}
state = new Video('#example');
videoControl = state.videoControl;
}
function initializeYouTube() {
initialize('video.html');
}
beforeEach(function(){
oldOTBD = window.onTouchBasedDevice;
window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn(null);
});
afterEach(function() {
$('source').remove();
window.onTouchBasedDevice = oldOTBD;
});
describe('constructor', function() {
beforeEach(function() {
initialize();
});
it('render the video controls', function() {
expect($('.video-controls')).toContain(
['.slider', 'ul.vcr', 'a.play', '.vidtime', '.add-fullscreen'].join(',')
);
expect($('.video-controls').find('.vidtime')).toHaveText('0:00 / 0:00');
});
it('add ARIA attributes to time control', function () {
var timeControl = $('div.slider>a');
expect(timeControl).toHaveAttrs({
'role': 'slider',
'title': 'video position',
'aria-disabled': 'false'
});
expect(timeControl).toHaveAttr('aria-valuetext');
});
it('add ARIA attributes to play control', function () {
var playControl = $('ul.vcr a');
expect(playControl).toHaveAttrs({
'role': 'button',
'title': 'Play',
'aria-disabled': 'false'
});
});
it('add ARIA attributes to fullscreen control', function () {
var fullScreenControl = $('a.add-fullscreen');
expect(fullScreenControl).toHaveAttrs({
'role': 'button',
'title': 'Fill browser',
'aria-disabled': 'false'
});
});
it('bind the playback button', function() {
expect($('.video_control')).toHandleWith('click', videoControl.togglePlayback);
});
describe('when on a non-touch based device', function() {
beforeEach(function() {
initialize();
});
it('add the play class to video control', function() {
expect($('.video_control')).toHaveClass('play');
expect($('.video_control')).toHaveAttr('title', 'Play');
});
});
describe('when on a touch based device', function() {
beforeEach(function() {
window.onTouchBasedDevice.andReturn(['iPad']);
initialize();
});
it('does not add the play class to video control', function() {
expect($('.video_control')).toHaveClass('play');
expect($('.video_control')).toHaveAttr('title', 'Play');
});
});
});
describe('play', function() {
beforeEach(function() {
initialize();
videoControl.play();
});
it('switch playback button to play state', function() {
expect($('.video_control')).not.toHaveClass('play');
expect($('.video_control')).toHaveClass('pause');
expect($('.video_control')).toHaveAttr('title', 'Pause');
});
});
describe('pause', function() {
beforeEach(function() {
initialize();
videoControl.pause();
});
it('switch playback button to pause state', function() {
expect($('.video_control')).not.toHaveClass('pause');
expect($('.video_control')).toHaveClass('play');
expect($('.video_control')).toHaveAttr('title', 'Play');
});
});
describe('togglePlayback', function() {
beforeEach(function() {
initialize();
});
describe('when the control does not have play or pause class', function() {
beforeEach(function() {
$('.video_control').removeClass('play').removeClass('pause');
}); });
describe('when the video is playing', function() { afterEach(function () {
beforeEach(function() { $('source').remove();
$('.video_control').addClass('play'); window.onTouchBasedDevice = oldOTBD;
spyOnEvent(videoControl, 'pause');
videoControl.togglePlayback(jQuery.Event('click'));
});
it('does not trigger the pause event', function() {
expect('pause').not.toHaveBeenTriggeredOn(videoControl);
});
}); });
describe('when the video is paused', function() { describe('constructor', function () {
beforeEach(function() { beforeEach(function () {
$('.video_control').addClass('pause'); state = jasmine.initializePlayer();
spyOnEvent(videoControl, 'play'); });
videoControl.togglePlayback(jQuery.Event('click'));
}); it('render the video controls', function () {
expect($('.video-controls')).toContain(
it('does not trigger the play event', function() { [
expect('play').not.toHaveBeenTriggeredOn(videoControl); '.slider',
}); 'ul.vcr',
'a.play',
'.vidtime',
'.add-fullscreen'
].join(',')
);
expect($('.video-controls').find('.vidtime'))
.toHaveText('0:00 / 0:00');
});
it('add ARIA attributes to time control', function () {
var timeControl = $('div.slider>a');
expect(timeControl).toHaveAttrs({
'role': 'slider',
'title': 'video position',
'aria-disabled': 'false'
});
expect(timeControl).toHaveAttr('aria-valuetext');
});
it('add ARIA attributes to play control', function () {
var playControl = $('ul.vcr a');
expect(playControl).toHaveAttrs({
'role': 'button',
'title': 'Play',
'aria-disabled': 'false'
});
});
it('add ARIA attributes to fullscreen control', function () {
var fullScreenControl = $('a.add-fullscreen');
expect(fullScreenControl).toHaveAttrs({
'role': 'button',
'title': 'Fill browser',
'aria-disabled': 'false'
});
});
it('bind the playback button', function () {
expect($('.video_control')).toHandleWith(
'click',
state.videoControl.togglePlayback
);
});
describe('when on a non-touch based device', function () {
beforeEach(function () {
state = jasmine.initializePlayer();
});
it('add the play class to video control', function () {
expect($('.video_control')).toHaveClass('play');
expect($('.video_control')).toHaveAttr(
'title', 'Play'
);
});
});
describe('when on a touch based device', function () {
beforeEach(function () {
window.onTouchBasedDevice.andReturn(['iPad']);
state = jasmine.initializePlayer();
});
it(
'does not add the play class to video control',
function ()
{
expect($('.video_control')).toHaveClass('play');
expect($('.video_control')).toHaveAttr(
'title', 'Play'
);
});
});
}); });
});
});
describe('Play placeholder', function () {
beforeEach(function () {
this.oldYT = window.YT;
jasmine.stubRequests();
window.YT = {
Player: function () { },
PlayerState: this.oldYT.PlayerState,
ready: function (callback) {
callback();
}
};
spyOn(window.YT, 'Player');
});
afterEach(function () {
window.YT = this.oldYT;
});
it ('works correctly on calling proper methods', function () {
initialize();
var btnPlay = state.el.find('.btn-play');
videoControl.showPlayPlaceholder(); describe('play', function () {
beforeEach(function () {
expect(btnPlay).not.toHaveClass('is-hidden'); state = jasmine.initializePlayer();
expect(btnPlay).toHaveAttrs({ state.videoControl.play();
'aria-hidden': 'false', });
'tabindex': 0
it('switch playback button to play state', function () {
expect($('.video_control')).not.toHaveClass('play');
expect($('.video_control')).toHaveClass('pause');
expect($('.video_control')).toHaveAttr('title', 'Pause');
});
}); });
videoControl.hidePlayPlaceholder(); describe('pause', function () {
beforeEach(function () {
expect(btnPlay).toHaveClass('is-hidden'); state = jasmine.initializePlayer();
expect(btnPlay).toHaveAttrs({ state.videoControl.pause();
'aria-hidden': 'true', });
'tabindex': -1
}); it('switch playback button to pause state', function () {
}); expect($('.video_control')).not.toHaveClass('pause');
expect($('.video_control')).toHaveClass('play');
var cases = [ expect($('.video_control')).toHaveAttr('title', 'Play');
{ });
name: 'PC',
isShown: false,
isTouch: null
},
{
name: 'iPad',
isShown: true,
isTouch: ['iPad']
},
{
name: 'Android',
isShown: true,
isTouch: ['Android']
},
{
name: 'iPhone',
isShown: false,
isTouch: ['iPhone']
}
];
$.each(cases, function(index, data) {
var message = [
(data.isShown) ? 'is' : 'is not',
' shown on',
data.name
].join('');
it(message, function () {
window.onTouchBasedDevice.andReturn(data.isTouch);
initialize();
var btnPlay = state.el.find('.btn-play');
if (data.isShown) {
expect(btnPlay).not.toHaveClass('is-hidden');
} else {
expect(btnPlay).toHaveClass('is-hidden');
}
}); });
});
$.each(['iPad', 'Android'], function(index, device) { describe('togglePlayback', function () {
it('is shown on paused video on '+ device +' in HTML5 player', function () { beforeEach(function () {
window.onTouchBasedDevice.andReturn([device]); state = jasmine.initializePlayer();
initialize(); });
var btnPlay = state.el.find('.btn-play');
describe(
videoControl.play(); 'when the control does not have play or pause class',
videoControl.pause(); function ()
{
expect(btnPlay).not.toHaveClass('is-hidden'); beforeEach(function () {
$('.video_control').removeClass('play')
.removeClass('pause');
});
describe('when the video is playing', function () {
beforeEach(function () {
$('.video_control').addClass('play');
spyOnEvent(state.videoControl, 'pause');
state.videoControl.togglePlayback(jQuery.Event('click'));
});
it('does not trigger the pause event', function () {
expect('pause').not
.toHaveBeenTriggeredOn(state.videoControl);
});
});
describe('when the video is paused', function () {
beforeEach(function () {
$('.video_control').addClass('pause');
spyOnEvent(state.videoControl, 'play');
state.videoControl.togglePlayback(jQuery.Event('click'));
});
it('does not trigger the play event', function () {
expect('play').not
.toHaveBeenTriggeredOn(state.videoControl);
});
});
});
}); });
it('is hidden on playing video on '+ device +' in HTML5 player', function () { describe('Play placeholder', function () {
window.onTouchBasedDevice.andReturn([device]); var cases = [
initialize(); {
var btnPlay = state.el.find('.btn-play'); name: 'PC',
isShown: false,
videoControl.play(); isTouch: null
}, {
expect(btnPlay).toHaveClass('is-hidden'); name: 'iPad',
isShown: true,
isTouch: ['iPad']
}, {
name: 'Android',
isShown: true,
isTouch: ['Android']
}, {
name: 'iPhone',
isShown: false,
isTouch: ['iPhone']
}
];
beforeEach(function () {
jasmine.stubRequests();
spyOn(window.YT, 'Player').andCallThrough();
});
it ('works correctly on calling proper methods', function () {
var btnPlay;
state = jasmine.initializePlayer();
btnPlay = state.el.find('.btn-play');
state.videoControl.showPlayPlaceholder();
expect(btnPlay).not.toHaveClass('is-hidden');
expect(btnPlay).toHaveAttrs({
'aria-hidden': 'false',
'tabindex': 0
});
state.videoControl.hidePlayPlaceholder();
expect(btnPlay).toHaveClass('is-hidden');
expect(btnPlay).toHaveAttrs({
'aria-hidden': 'true',
'tabindex': -1
});
});
$.each(cases, function (index, data) {
var message = [
(data.isShown) ? 'is' : 'is not',
' shown on',
data.name
].join('');
it(message, function () {
var btnPlay;
window.onTouchBasedDevice.andReturn(data.isTouch);
state = jasmine.initializePlayer();
btnPlay = state.el.find('.btn-play');
if (data.isShown) {
expect(btnPlay).not.toHaveClass('is-hidden');
} else {
expect(btnPlay).toHaveClass('is-hidden');
}
});
});
$.each(['iPad', 'Android'], function (index, device) {
it(
'is shown on paused video on ' + device +
' in HTML5 player',
function ()
{
var btnPlay;
window.onTouchBasedDevice.andReturn([device]);
state = jasmine.initializePlayer();
btnPlay = state.el.find('.btn-play');
state.videoControl.play();
state.videoControl.pause();
expect(btnPlay).not.toHaveClass('is-hidden');
});
it(
'is hidden on playing video on ' + device +
' in HTML5 player',
function ()
{
var btnPlay;
window.onTouchBasedDevice.andReturn([device]);
state = jasmine.initializePlayer();
btnPlay = state.el.find('.btn-play');
state.videoControl.play();
expect(btnPlay).toHaveClass('is-hidden');
});
it(
'is hidden on paused video on ' + device +
' in YouTube player',
function ()
{
var btnPlay;
window.onTouchBasedDevice.andReturn([device]);
state = jasmine.initializePlayerYouTube();
btnPlay = state.el.find('.btn-play');
state.videoControl.play();
state.videoControl.pause();
expect(btnPlay).toHaveClass('is-hidden');
});
});
}); });
it('is hidden on paused video on '+ device +' in YouTube player', function () { it('show', function () {
window.onTouchBasedDevice.andReturn([device]); var controls;
initializeYouTube();
var btnPlay = state.el.find('.btn-play');
videoControl.play(); state = jasmine.initializePlayer();
videoControl.pause(); controls = state.el.find('.video-controls');
controls.addClass('is-hidden');
expect(btnPlay).toHaveClass('is-hidden'); state.videoControl.show();
expect(controls).not.toHaveClass('is-hidden');
}); });
});
}); });
it('show', function () {
initialize();
var controls = state.el.find('.video-controls');
controls.addClass('is-hidden');
videoControl.show();
expect(controls).not.toHaveClass('is-hidden');
});
});
}).call(this); }).call(this);
(function () { (function (undefined) {
describe('Video FocusGrabber', function () { describe('Video FocusGrabber', function () {
var state; var state;
......
(function () { (function (undefined) {
describe('VideoPlayer', function () { describe('VideoPlayer', function () {
var state, videoPlayer, player, videoControl, videoCaption, var state, oldOTBD;
videoProgressSlider, videoSpeedControl, videoVolumeControl,
oldOTBD;
function initialize(fixture, params) {
if (_.isString(fixture)) {
loadFixtures(fixture);
} else {
if (_.isObject(fixture)) {
params = fixture;
}
loadFixtures('video_all.html');
}
if (_.isObject(params)) {
$('#example')
.find('#video_id')
.data(params);
}
state = new Video('#example');
state.videoEl = $('video, iframe');
videoPlayer = state.videoPlayer;
player = videoPlayer.player;
videoControl = state.videoControl;
videoCaption = state.videoCaption;
videoProgressSlider = state.videoProgressSlider;
videoSpeedControl = state.videoSpeedControl;
videoVolumeControl = state.videoVolumeControl;
state.resizer = (function () {
var methods = [
'align',
'alignByWidthOnly',
'alignByHeightOnly',
'setParams',
'setMode'
],
obj = {};
$.each(methods, function (index, method) {
obj[method] = jasmine.createSpy(method).andReturn(obj);
});
return obj;
}());
}
function initializeYouTube() {
initialize('video.html');
}
beforeEach(function () { beforeEach(function () {
oldOTBD = window.onTouchBasedDevice; oldOTBD = window.onTouchBasedDevice;
...@@ -68,11 +16,13 @@ ...@@ -68,11 +16,13 @@
describe('constructor', function () { describe('constructor', function () {
describe('always', function () { describe('always', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
state.videoEl = $('video, iframe');
}); });
it('instanticate current time to zero', function () { it('instanticate current time to zero', function () {
expect(videoPlayer.currentTime).toEqual(0); expect(state.videoPlayer.currentTime).toEqual(0);
}); });
it('set the element', function () { it('set the element', function () {
...@@ -80,12 +30,12 @@ ...@@ -80,12 +30,12 @@
}); });
it('create video control', function () { it('create video control', function () {
expect(videoControl).toBeDefined(); expect(state.videoControl).toBeDefined();
expect(videoControl.el).toHaveClass('video-controls'); expect(state.videoControl.el).toHaveClass('video-controls');
}); });
it('create video caption', function () { it('create video caption', function () {
expect(videoCaption).toBeDefined(); expect(state.videoCaption).toBeDefined();
expect(state.youtubeId()).toEqual('Z5KLxerq05Y'); expect(state.youtubeId()).toEqual('Z5KLxerq05Y');
expect(state.speed).toEqual('1.0'); expect(state.speed).toEqual('1.0');
expect(state.config.caption_asset_path) expect(state.config.caption_asset_path)
...@@ -93,16 +43,16 @@ ...@@ -93,16 +43,16 @@
}); });
it('create video speed control', function () { it('create video speed control', function () {
expect(videoSpeedControl).toBeDefined(); expect(state.videoSpeedControl).toBeDefined();
expect(videoSpeedControl.el).toHaveClass('speeds'); expect(state.videoSpeedControl.el).toHaveClass('speeds');
expect(videoSpeedControl.speeds) expect(state.videoSpeedControl.speeds)
.toEqual([ '0.75', '1.0', '1.25', '1.50' ]); .toEqual([ '0.75', '1.0', '1.25', '1.50' ]);
expect(state.speed).toEqual('1.0'); expect(state.speed).toEqual('1.0');
}); });
it('create video progress slider', function () { it('create video progress slider', function () {
expect(videoProgressSlider).toBeDefined(); expect(state.videoProgressSlider).toBeDefined();
expect(videoProgressSlider.el).toHaveClass('slider'); expect(state.videoProgressSlider.el).toHaveClass('slider');
}); });
// All the toHandleWith() expect tests are not necessary for // All the toHandleWith() expect tests are not necessary for
...@@ -112,26 +62,20 @@ ...@@ -112,26 +62,20 @@
}); });
it('create Youtube player', function () { it('create Youtube player', function () {
var oldYT = window.YT, events; var events;
jasmine.stubRequests(); jasmine.stubRequests();
window.YT = { spyOn(window.YT, 'Player').andCallThrough();
Player: function () { },
PlayerState: oldYT.PlayerState,
ready: function (callback) {
callback();
}
};
spyOn(window.YT, 'Player'); state = jasmine.initializePlayerYouTube();
initializeYouTube(); state.videoEl = $('video, iframe');
events = { events = {
onReady: videoPlayer.onReady, onReady: state.videoPlayer.onReady,
onStateChange: videoPlayer.onStateChange, onStateChange: state.videoPlayer.onStateChange,
onPlaybackQualityChange: videoPlayer onPlaybackQualityChange: state.videoPlayer
.onPlaybackQualityChange .onPlaybackQualityChange
}; };
...@@ -148,8 +92,6 @@ ...@@ -148,8 +92,6 @@
videoId: 'cogebirgzzM', videoId: 'cogebirgzzM',
events: events events: events
}); });
window.YT = oldYT;
}); });
// We can't test the invocation of HTML5Video because it is not // We can't test the invocation of HTML5Video because it is not
...@@ -157,11 +99,14 @@ ...@@ -157,11 +99,14 @@
// JS. // JS.
describe('when on a touch based device', function () { describe('when on a touch based device', function () {
$.each(['iPad', 'Android'], function(index, device) { $.each(['iPad', 'Android'], function (index, device) {
it('create video volume control on' + device, function() { it('create video volume control on' + device, function () {
window.onTouchBasedDevice.andReturn([device]); window.onTouchBasedDevice.andReturn([device]);
initialize(); state = jasmine.initializePlayer();
expect(videoVolumeControl).toBeUndefined();
state.videoEl = $('video, iframe');
expect(state.videoVolumeControl).toBeUndefined();
expect(state.el.find('div.volume')).not.toExist(); expect(state.el.find('div.volume')).not.toExist();
}); });
}); });
...@@ -171,52 +116,58 @@ ...@@ -171,52 +116,58 @@
var oldOTBD; var oldOTBD;
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
state.videoEl = $('video, iframe');
}); });
it('controls are in paused state', function () { it('controls are in paused state', function () {
expect(videoControl.isPlaying).toBe(false); expect(state.videoControl.isPlaying).toBe(false);
}); });
}); });
}); });
describe('onReady', function () { describe('onReady', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
state.videoEl = $('video, iframe');
spyOn(videoPlayer, 'log').andCallThrough(); spyOn(state.videoPlayer, 'log').andCallThrough();
spyOn(videoPlayer, 'play').andCallThrough(); spyOn(state.videoPlayer, 'play').andCallThrough();
videoPlayer.onReady(); state.videoPlayer.onReady();
}); });
it('log the load_video event', function () { it('log the load_video event', function () {
expect(videoPlayer.log).toHaveBeenCalledWith('load_video'); expect(state.videoPlayer.log).toHaveBeenCalledWith('load_video');
}); });
it('autoplay the first video', function () { it('autoplay the first video', function () {
expect(videoPlayer.play).not.toHaveBeenCalled(); expect(state.videoPlayer.play).not.toHaveBeenCalled();
}); });
}); });
describe('onStateChange', function () { describe('onStateChange', function () {
describe('when the video is unstarted', function () { describe('when the video is unstarted', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
spyOn(videoControl, 'pause').andCallThrough(); state.videoEl = $('video, iframe');
spyOn(videoCaption, 'pause').andCallThrough();
videoPlayer.onStateChange({ spyOn(state.videoControl, 'pause').andCallThrough();
data: YT.PlayerState.PAUSED spyOn(state.videoCaption, 'pause').andCallThrough();
state.videoPlayer.onStateChange({
data: YT.PlayerState.PAUSED
}); });
}); });
it('pause the video control', function () { it('pause the video control', function () {
expect(videoControl.pause).toHaveBeenCalled(); expect(state.videoControl.pause).toHaveBeenCalled();
}); });
it('pause the video caption', function () { it('pause the video caption', function () {
expect(videoCaption.pause).toHaveBeenCalled(); expect(state.videoCaption.pause).toHaveBeenCalled();
}); });
}); });
...@@ -225,26 +176,28 @@ ...@@ -225,26 +176,28 @@
beforeEach(function () { beforeEach(function () {
// Create the first instance of the player. // Create the first instance of the player.
initialize(); state = jasmine.initializePlayer();
oldState = state; oldState = state;
spyOn(oldState.videoPlayer, 'onPause').andCallThrough(); spyOn(oldState.videoPlayer, 'onPause').andCallThrough();
// Now initialize a second instance. // Now initialize a second instance.
initialize(); state = jasmine.initializePlayer();
spyOn(videoPlayer, 'log').andCallThrough(); state.videoEl = $('video, iframe');
spyOn(state.videoPlayer, 'log').andCallThrough();
spyOn(window, 'setInterval').andReturn(100); spyOn(window, 'setInterval').andReturn(100);
spyOn(videoControl, 'play'); spyOn(state.videoControl, 'play');
spyOn(videoCaption, 'play'); spyOn(state.videoCaption, 'play');
videoPlayer.onStateChange({ state.videoPlayer.onStateChange({
data: YT.PlayerState.PLAYING data: YT.PlayerState.PLAYING
}); });
}); });
it('log the play_video event', function () { it('log the play_video event', function () {
expect(videoPlayer.log).toHaveBeenCalledWith( expect(state.videoPlayer.log).toHaveBeenCalledWith(
'play_video', { currentTime: 0 } 'play_video', { currentTime: 0 }
); );
}); });
...@@ -255,17 +208,17 @@ ...@@ -255,17 +208,17 @@
it('set update interval', function () { it('set update interval', function () {
expect(window.setInterval).toHaveBeenCalledWith( expect(window.setInterval).toHaveBeenCalledWith(
videoPlayer.update, 200 state.videoPlayer.update, 200
); );
expect(videoPlayer.updateInterval).toEqual(100); expect(state.videoPlayer.updateInterval).toEqual(100);
}); });
it('play the video control', function () { it('play the video control', function () {
expect(videoControl.play).toHaveBeenCalled(); expect(state.videoControl.play).toHaveBeenCalled();
}); });
it('play the video caption', function () { it('play the video caption', function () {
expect(videoCaption.play).toHaveBeenCalled(); expect(state.videoCaption.play).toHaveBeenCalled();
}); });
}); });
...@@ -273,90 +226,96 @@ ...@@ -273,90 +226,96 @@
var currentUpdateIntrval; var currentUpdateIntrval;
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
state.videoEl = $('video, iframe');
spyOn(videoPlayer, 'log').andCallThrough(); spyOn(state.videoPlayer, 'log').andCallThrough();
spyOn(videoControl, 'pause').andCallThrough(); spyOn(state.videoControl, 'pause').andCallThrough();
spyOn(videoCaption, 'pause').andCallThrough(); spyOn(state.videoCaption, 'pause').andCallThrough();
videoPlayer.onStateChange({ state.videoPlayer.onStateChange({
data: YT.PlayerState.PLAYING data: YT.PlayerState.PLAYING
}); });
currentUpdateIntrval = videoPlayer.updateInterval; currentUpdateIntrval = state.videoPlayer.updateInterval;
videoPlayer.onStateChange({ state.videoPlayer.onStateChange({
data: YT.PlayerState.PAUSED data: YT.PlayerState.PAUSED
}); });
}); });
it('log the pause_video event', function () { it('log the pause_video event', function () {
expect(videoPlayer.log).toHaveBeenCalledWith( expect(state.videoPlayer.log).toHaveBeenCalledWith(
'pause_video', { currentTime: 0 } 'pause_video', { currentTime: 0 }
); );
}); });
it('clear update interval', function () { it('clear update interval', function () {
expect(videoPlayer.updateInterval).toBeUndefined(); expect(state.videoPlayer.updateInterval).toBeUndefined();
}); });
it('pause the video control', function () { it('pause the video control', function () {
expect(videoControl.pause).toHaveBeenCalled(); expect(state.videoControl.pause).toHaveBeenCalled();
}); });
it('pause the video caption', function () { it('pause the video caption', function () {
expect(videoCaption.pause).toHaveBeenCalled(); expect(state.videoCaption.pause).toHaveBeenCalled();
}); });
}); });
describe('when the video is ended', function () { describe('when the video is ended', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
state.videoEl = $('video, iframe');
spyOn(videoControl, 'pause').andCallThrough(); spyOn(state.videoControl, 'pause').andCallThrough();
spyOn(videoCaption, 'pause').andCallThrough(); spyOn(state.videoCaption, 'pause').andCallThrough();
videoPlayer.onStateChange({ state.videoPlayer.onStateChange({
data: YT.PlayerState.ENDED data: YT.PlayerState.ENDED
}); });
}); });
it('pause the video control', function () { it('pause the video control', function () {
expect(videoControl.pause).toHaveBeenCalled(); expect(state.videoControl.pause).toHaveBeenCalled();
}); });
it('pause the video caption', function () { it('pause the video caption', function () {
expect(videoCaption.pause).toHaveBeenCalled(); expect(state.videoCaption.pause).toHaveBeenCalled();
}); });
}); });
}); });
describe('onSeek', function () { describe('onSeek', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
state.videoEl = $('video, iframe');
runs(function () { runs(function () {
state.videoPlayer.play(); state.videoPlayer.play();
}); });
waitsFor(function () { waitsFor(function () {
duration = videoPlayer.duration(); duration = state.videoPlayer.duration();
return duration > 0 && videoPlayer.isPlaying(); return duration > 0 && state.videoPlayer.isPlaying();
}, 'video begins playing', WAIT_TIMEOUT); }, 'video begins playing', WAIT_TIMEOUT);
}); });
it('Slider event causes log update', function () { it('Slider event causes log update', function () {
runs(function () { runs(function () {
var currentTime = videoPlayer.currentTime; var currentTime = state.videoPlayer.currentTime;
spyOn(videoPlayer, 'log'); spyOn(state.videoPlayer, 'log');
videoProgressSlider.onSlide( state.videoProgressSlider.onSlide(
jQuery.Event('slide'), { value: 2 } jQuery.Event('slide'), { value: 2 }
); );
expect(videoPlayer.log).toHaveBeenCalledWith( expect(state.videoPlayer.log).toHaveBeenCalledWith(
'seek_video', 'seek_video',
{ {
old_time: currentTime, old_time: currentTime,
...@@ -369,24 +328,24 @@ ...@@ -369,24 +328,24 @@
it('seek the player', function () { it('seek the player', function () {
runs(function () { runs(function () {
spyOn(videoPlayer.player, 'seekTo'); spyOn(state.videoPlayer.player, 'seekTo');
videoProgressSlider.onSlide( state.videoProgressSlider.onSlide(
jQuery.Event('slide'), { value: 60 } jQuery.Event('slide'), { value: 60 }
); );
expect(videoPlayer.player.seekTo) expect(state.videoPlayer.player.seekTo)
.toHaveBeenCalledWith(60, true); .toHaveBeenCalledWith(60, true);
}); });
}); });
it('call updatePlayTime on player', function () { it('call updatePlayTime on player', function () {
runs(function () { runs(function () {
spyOn(videoPlayer, 'updatePlayTime'); spyOn(state.videoPlayer, 'updatePlayTime');
videoProgressSlider.onSlide( state.videoProgressSlider.onSlide(
jQuery.Event('slide'), { value: 60 } jQuery.Event('slide'), { value: 60 }
); );
expect(videoPlayer.updatePlayTime) expect(state.videoPlayer.updatePlayTime)
.toHaveBeenCalledWith(60); .toHaveBeenCalledWith(60);
}); });
}); });
...@@ -397,16 +356,16 @@ ...@@ -397,16 +356,16 @@
function () function ()
{ {
runs(function () { runs(function () {
videoProgressSlider.onSlide( state.videoProgressSlider.onSlide(
jQuery.Event('slide'), { value: 20 } jQuery.Event('slide'), { value: 20 }
); );
videoPlayer.pause(); state.videoPlayer.pause();
videoProgressSlider.onSlide( state.videoProgressSlider.onSlide(
jQuery.Event('slide'), { value: 10 } jQuery.Event('slide'), { value: 10 }
); );
waitsFor(function () { waitsFor(function () {
return Math.round(videoPlayer.currentTime) === 10; return Math.round(state.videoPlayer.currentTime) === 10;
}, 'currentTime got updated', 10000); }, 'currentTime got updated', 10000);
}); });
}); });
...@@ -414,26 +373,28 @@ ...@@ -414,26 +373,28 @@
describe('onSpeedChange', function () { describe('onSpeedChange', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
state.videoEl = $('video, iframe');
spyOn(videoPlayer, 'updatePlayTime').andCallThrough(); spyOn(state.videoPlayer, 'updatePlayTime').andCallThrough();
spyOn(state, 'setSpeed').andCallThrough(); spyOn(state, 'setSpeed').andCallThrough();
spyOn(videoPlayer, 'log').andCallThrough(); spyOn(state.videoPlayer, 'log').andCallThrough();
spyOn(videoPlayer.player, 'setPlaybackRate').andCallThrough(); spyOn(state.videoPlayer.player, 'setPlaybackRate').andCallThrough();
}); });
describe('always', function () { describe('always', function () {
beforeEach(function () { beforeEach(function () {
videoPlayer.currentTime = 60; state.videoPlayer.currentTime = 60;
videoPlayer.onSpeedChange('0.75', false); state.videoPlayer.onSpeedChange('0.75', false);
}); });
it('check if speed_change_video is logged', function () { it('check if speed_change_video is logged', function () {
expect(videoPlayer.log).toHaveBeenCalledWith( expect(state.videoPlayer.log).toHaveBeenCalledWith(
'speed_change_video', 'speed_change_video',
{ {
current_time: videoPlayer.currentTime, current_time: state.videoPlayer.currentTime,
old_speed: '1.0', old_speed: '1.0',
new_speed: '0.75' new_speed: '0.75'
} }
...@@ -441,7 +402,7 @@ ...@@ -441,7 +402,7 @@
}); });
it('convert the current time to the new speed', function () { it('convert the current time to the new speed', function () {
expect(videoPlayer.currentTime).toEqual(60); expect(state.videoPlayer.currentTime).toEqual(60);
}); });
it('set video speed to the new speed', function () { it('set video speed to the new speed', function () {
...@@ -451,75 +412,82 @@ ...@@ -451,75 +412,82 @@
describe('when the video is playing', function () { describe('when the video is playing', function () {
beforeEach(function () { beforeEach(function () {
videoPlayer.currentTime = 60; state.videoPlayer.currentTime = 60;
videoPlayer.play(); state.videoPlayer.play();
videoPlayer.onSpeedChange('0.75', false); state.videoPlayer.onSpeedChange('0.75', false);
}); });
it('trigger updatePlayTime event', function () { it('trigger updatePlayTime event', function () {
expect(videoPlayer.player.setPlaybackRate) expect(state.videoPlayer.player.setPlaybackRate)
.toHaveBeenCalledWith('0.75'); .toHaveBeenCalledWith('0.75');
}); });
}); });
describe('when the video is not playing', function () { describe('when the video is not playing', function () {
beforeEach(function () { beforeEach(function () {
videoPlayer.onSpeedChange('0.75', false); state.videoPlayer.onSpeedChange('0.75', false);
}); });
it('trigger updatePlayTime event', function () { it('trigger updatePlayTime event', function () {
expect(videoPlayer.player.setPlaybackRate) expect(state.videoPlayer.player.setPlaybackRate)
.toHaveBeenCalledWith('0.75'); .toHaveBeenCalledWith('0.75');
}); });
it('video has a correct speed', function () { it('video has a correct speed', function () {
spyOn(videoPlayer, 'onSpeedChange'); spyOn(state.videoPlayer, 'onSpeedChange');
state.speed = '2.0'; state.speed = '2.0';
videoPlayer.onPlay(); state.videoPlayer.onPlay();
expect(videoPlayer.onSpeedChange).toHaveBeenCalledWith('2.0'); expect(state.videoPlayer.onSpeedChange)
videoPlayer.onPlay(); .toHaveBeenCalledWith('2.0');
expect(videoPlayer.onSpeedChange.calls.length).toEqual(1); state.videoPlayer.onPlay();
expect(state.videoPlayer.onSpeedChange.calls.length).toEqual(1);
}); });
it('video has a correct volume', function () { it('video has a correct volume', function () {
spyOn(videoPlayer.player, 'setVolume'); spyOn(state.videoPlayer.player, 'setVolume');
state.currentVolume = '0.26'; state.currentVolume = '0.26';
videoPlayer.onPlay(); state.videoPlayer.onPlay();
expect(videoPlayer.player.setVolume).toHaveBeenCalledWith('0.26'); expect(state.videoPlayer.player.setVolume)
.toHaveBeenCalledWith('0.26');
}); });
}); });
}); });
describe('onVolumeChange', function () { describe('onVolumeChange', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
state.videoEl = $('video, iframe');
}); });
it('set the volume on player', function () { it('set the volume on player', function () {
spyOn(videoPlayer.player, 'setVolume'); spyOn(state.videoPlayer.player, 'setVolume');
videoPlayer.onVolumeChange(60); state.videoPlayer.onVolumeChange(60);
expect(videoPlayer.player.setVolume).toHaveBeenCalledWith(60); expect(state.videoPlayer.player.setVolume).toHaveBeenCalledWith(60);
}); });
describe('when the video is not playing', function () { describe('when the video is not playing', function () {
beforeEach(function () { beforeEach(function () {
videoPlayer.player.setVolume('1'); state.videoPlayer.player.setVolume('1');
}); });
it('video has a correct volume', function () { it('video has a correct volume', function () {
spyOn(videoPlayer.player, 'setVolume'); spyOn(state.videoPlayer.player, 'setVolume');
state.currentVolume = '0.26'; state.currentVolume = '0.26';
videoPlayer.onPlay(); state.videoPlayer.onPlay();
expect(videoPlayer.player.setVolume).toHaveBeenCalledWith('0.26'); expect(state.videoPlayer.player.setVolume)
.toHaveBeenCalledWith('0.26');
}); });
}); });
}); });
describe('update', function () { describe('update', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
state.videoEl = $('video, iframe');
spyOn(videoPlayer, 'updatePlayTime').andCallThrough(); spyOn(state.videoPlayer, 'updatePlayTime').andCallThrough();
}); });
describe( describe(
...@@ -527,14 +495,14 @@ ...@@ -527,14 +495,14 @@
function () function ()
{ {
beforeEach(function () { beforeEach(function () {
videoPlayer.player.getCurrentTime = function () { state.videoPlayer.player.getCurrentTime = function () {
return NaN; return NaN;
}; };
videoPlayer.update(); state.videoPlayer.update();
}); });
it('does not trigger updatePlayTime event', function () { it('does not trigger updatePlayTime event', function () {
expect(videoPlayer.updatePlayTime).not.toHaveBeenCalled(); expect(state.videoPlayer.updatePlayTime).not.toHaveBeenCalled();
}); });
}); });
...@@ -543,14 +511,14 @@ ...@@ -543,14 +511,14 @@
function () function ()
{ {
beforeEach(function () { beforeEach(function () {
videoPlayer.player.getCurrentTime = function () { state.videoPlayer.player.getCurrentTime = function () {
return 60; return 60;
}; };
videoPlayer.update(); state.videoPlayer.update();
}); });
it('trigger updatePlayTime event', function () { it('trigger updatePlayTime event', function () {
expect(videoPlayer.updatePlayTime) expect(state.videoPlayer.updatePlayTime)
.toHaveBeenCalledWith(60); .toHaveBeenCalledWith(60);
}); });
}); });
...@@ -561,34 +529,44 @@ ...@@ -561,34 +529,44 @@
var START_TIME = 1, END_TIME = 2; var START_TIME = 1, END_TIME = 2;
beforeEach(function () { beforeEach(function () {
initialize({start: START_TIME, end: END_TIME}); state = jasmine.initializePlayer(
{
start: START_TIME,
end: END_TIME
}
);
state.videoEl = $('video, iframe');
spyOn(videoPlayer, 'update').andCallThrough(); spyOn(state.videoPlayer, 'update').andCallThrough();
spyOn(videoPlayer, 'pause').andCallThrough(); spyOn(state.videoPlayer, 'pause').andCallThrough();
spyOn(videoProgressSlider, 'notifyThroughHandleEnd') spyOn(state.videoProgressSlider, 'notifyThroughHandleEnd')
.andCallThrough(); .andCallThrough();
}); });
it('video is paused on first endTime, start & end time are reset', function () { it(
'video is paused on first endTime, start & end time are reset',
function ()
{
var duration; var duration;
videoProgressSlider.notifyThroughHandleEnd.reset(); state.videoProgressSlider.notifyThroughHandleEnd.reset();
videoPlayer.pause.reset(); state.videoPlayer.pause.reset();
videoPlayer.play(); state.videoPlayer.play();
waitsFor(function () { waitsFor(function () {
duration = Math.round(videoPlayer.currentTime); duration = Math.round(state.videoPlayer.currentTime);
return videoPlayer.pause.calls.length === 1; return state.videoPlayer.pause.calls.length === 1;
}, 'pause() has been called', WAIT_TIMEOUT); }, 'pause() has been called', WAIT_TIMEOUT);
runs(function () { runs(function () {
expect(videoPlayer.startTime).toBe(0); expect(state.videoPlayer.startTime).toBe(0);
expect(videoPlayer.endTime).toBe(null); expect(state.videoPlayer.endTime).toBe(null);
expect(duration).toBe(END_TIME); expect(duration).toBe(END_TIME);
expect(videoProgressSlider.notifyThroughHandleEnd) expect(state.videoProgressSlider.notifyThroughHandleEnd)
.toHaveBeenCalledWith({end: true}); .toHaveBeenCalledWith({end: true});
}); });
}); });
...@@ -596,17 +574,19 @@ ...@@ -596,17 +574,19 @@
describe('updatePlayTime', function () { describe('updatePlayTime', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
state.videoEl = $('video, iframe');
spyOn(videoCaption, 'updatePlayTime').andCallThrough(); spyOn(state.videoCaption, 'updatePlayTime').andCallThrough();
spyOn(videoProgressSlider, 'updatePlayTime').andCallThrough(); spyOn(state.videoProgressSlider, 'updatePlayTime').andCallThrough();
}); });
it('update the video playback time', function () { it('update the video playback time', function () {
var duration = 0; var duration = 0;
waitsFor(function () { waitsFor(function () {
duration = videoPlayer.duration(); duration = state.videoPlayer.duration();
if (duration > 0) { if (duration > 0) {
return true; return true;
...@@ -618,7 +598,7 @@ ...@@ -618,7 +598,7 @@
runs(function () { runs(function () {
var htmlStr; var htmlStr;
videoPlayer.updatePlayTime(60); state.videoPlayer.updatePlayTime(60);
htmlStr = $('.vidtime').html(); htmlStr = $('.vidtime').html();
...@@ -641,13 +621,13 @@ ...@@ -641,13 +621,13 @@
it('update the playback time on caption', function () { it('update the playback time on caption', function () {
waitsFor(function () { waitsFor(function () {
return videoPlayer.duration() > 0; return state.videoPlayer.duration() > 0;
}, 'Video is fully loaded.', WAIT_TIMEOUT); }, 'Video is fully loaded.', WAIT_TIMEOUT);
runs(function () { runs(function () {
videoPlayer.updatePlayTime(60); state.videoPlayer.updatePlayTime(60);
expect(videoCaption.updatePlayTime) expect(state.videoCaption.updatePlayTime)
.toHaveBeenCalledWith(60); .toHaveBeenCalledWith(60);
}); });
}); });
...@@ -656,15 +636,15 @@ ...@@ -656,15 +636,15 @@
var duration = 0; var duration = 0;
waitsFor(function () { waitsFor(function () {
duration = videoPlayer.duration(); duration = state.videoPlayer.duration();
return duration > 0; return duration > 0;
}, 'Video is fully loaded.', WAIT_TIMEOUT); }, 'Video is fully loaded.', WAIT_TIMEOUT);
runs(function () { runs(function () {
videoPlayer.updatePlayTime(60); state.videoPlayer.updatePlayTime(60);
expect(videoProgressSlider.updatePlayTime) expect(state.videoProgressSlider.updatePlayTime)
.toHaveBeenCalledWith({ .toHaveBeenCalledWith({
time: 60, time: 60,
duration: duration duration: duration
...@@ -674,68 +654,93 @@ ...@@ -674,68 +654,93 @@
}); });
// Disabled 1/13/14 due to flakiness observed in master // Disabled 1/13/14 due to flakiness observed in master
xdescribe('updatePlayTime when start & end times are defined', function () { xdescribe(
'updatePlayTime when start & end times are defined',
function ()
{
var START_TIME = 1, var START_TIME = 1,
END_TIME = 2; END_TIME = 2;
beforeEach(function () { beforeEach(function () {
initialize({start: START_TIME, end: END_TIME}); state = jasmine.initializePlayer(
{
start: START_TIME,
end: END_TIME
}
);
state.videoEl = $('video, iframe');
spyOn(videoPlayer, 'updatePlayTime').andCallThrough(); spyOn(state.videoPlayer, 'updatePlayTime').andCallThrough();
spyOn(videoPlayer.player, 'seekTo').andCallThrough(); spyOn(state.videoPlayer.player, 'seekTo').andCallThrough();
spyOn(videoProgressSlider, 'updateStartEndTimeRegion') spyOn(state.videoProgressSlider, 'updateStartEndTimeRegion')
.andCallThrough(); .andCallThrough();
}); });
it('when duration becomes available, updatePlayTime() is called', function () { it(
'when duration becomes available, updatePlayTime() is called',
function ()
{
var duration; var duration;
expect(videoPlayer.initialSeekToStartTime).toBeTruthy(); expect(state.videoPlayer.initialSeekToStartTime).toBeTruthy();
expect(videoPlayer.seekToStartTimeOldSpeed).toBe('void'); expect(state.videoPlayer.seekToStartTimeOldSpeed).toBe('void');
videoPlayer.play(); state.videoPlayer.play();
waitsFor(function () { waitsFor(function () {
duration = videoPlayer.duration(); duration = state.videoPlayer.duration();
return videoPlayer.isPlaying() && return state.videoPlayer.isPlaying() &&
videoPlayer.initialSeekToStartTime === false; state.videoPlayer.initialSeekToStartTime === false;
}, 'duration becomes available', WAIT_TIMEOUT); }, 'duration becomes available', WAIT_TIMEOUT);
runs(function () { runs(function () {
expect(videoPlayer.startTime).toBe(START_TIME); expect(state.videoPlayer.startTime).toBe(START_TIME);
expect(videoPlayer.endTime).toBe(END_TIME); expect(state.videoPlayer.endTime).toBe(END_TIME);
expect(videoPlayer.player.seekTo).toHaveBeenCalledWith(START_TIME); expect(state.videoPlayer.player.seekTo)
.toHaveBeenCalledWith(START_TIME);
expect(videoProgressSlider.updateStartEndTimeRegion) expect(state.videoProgressSlider.updateStartEndTimeRegion)
.toHaveBeenCalledWith({duration: duration}); .toHaveBeenCalledWith({duration: duration});
expect(videoPlayer.seekToStartTimeOldSpeed).toBe(state.speed); expect(state.videoPlayer.seekToStartTimeOldSpeed)
.toBe(state.speed);
}); });
}); });
}); });
describe('updatePlayTime with invalid endTime', function () { describe('updatePlayTime with invalid endTime', function () {
beforeEach(function () { beforeEach(function () {
initialize({end: 100000}); state = jasmine.initializePlayer(
{
end: 100000
}
);
spyOn(videoPlayer, 'updatePlayTime').andCallThrough(); state.videoEl = $('video, iframe');
spyOn(state.videoPlayer, 'updatePlayTime').andCallThrough();
}); });
it('invalid endTime is reset to null', function () { it('invalid endTime is reset to null', function () {
var duration; var duration;
videoPlayer.updatePlayTime.reset(); state.videoPlayer.updatePlayTime.reset();
videoPlayer.play(); state.videoPlayer.play();
waitsFor(function () { waitsFor(
return videoPlayer.isPlaying() && function () {
videoPlayer.initialSeekToStartTime === false; return state.videoPlayer.isPlaying() &&
}, 'updatePlayTime was invoked and duration is set', WAIT_TIMEOUT); state.videoPlayer.initialSeekToStartTime === false;
},
'updatePlayTime was invoked and duration is set',
WAIT_TIMEOUT
);
runs(function () { runs(function () {
expect(videoPlayer.endTime).toBe(null); expect(state.videoPlayer.endTime).toBe(null);
}); });
}); });
}); });
...@@ -743,9 +748,12 @@ ...@@ -743,9 +748,12 @@
describe('toggleFullScreen', function () { describe('toggleFullScreen', function () {
describe('when the video player is not full screen', function () { describe('when the video player is not full screen', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
spyOn(videoCaption, 'resize').andCallThrough();
videoControl.toggleFullScreen(jQuery.Event('click')); state.videoEl = $('video, iframe');
spyOn(state.videoCaption, 'resize').andCallThrough();
state.videoControl.toggleFullScreen(jQuery.Event('click'));
}); });
it('replace the full screen button tooltip', function () { it('replace the full screen button tooltip', function () {
...@@ -758,22 +766,25 @@ ...@@ -758,22 +766,25 @@
}); });
it('tell VideoCaption to resize', function () { it('tell VideoCaption to resize', function () {
expect(videoCaption.resize).toHaveBeenCalled(); expect(state.videoCaption.resize).toHaveBeenCalled();
expect(state.resizer.setMode).toHaveBeenCalled(); expect(state.resizer.setMode).toHaveBeenCalled();
}); });
}); });
describe('when the video player already full screen', function () { describe('when the video player already full screen', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
spyOn(videoCaption, 'resize').andCallThrough();
state.videoEl = $('video, iframe');
spyOn(state.videoCaption, 'resize').andCallThrough();
state.el.addClass('video-fullscreen'); state.el.addClass('video-fullscreen');
videoControl.fullScreenState = true; state.videoControl.fullScreenState = true;
isFullScreen = true; isFullScreen = true;
videoControl.fullScreenEl.attr('title', 'Exit-fullscreen'); state.videoControl.fullScreenEl.attr('title', 'Exit-fullscreen');
videoControl.toggleFullScreen(jQuery.Event('click')); state.videoControl.toggleFullScreen(jQuery.Event('click'));
}); });
it('replace the full screen button tooltip', function () { it('replace the full screen button tooltip', function () {
...@@ -786,7 +797,7 @@ ...@@ -786,7 +797,7 @@
}); });
it('tell VideoCaption to resize', function () { it('tell VideoCaption to resize', function () {
expect(videoCaption.resize).toHaveBeenCalled(); expect(state.videoCaption.resize).toHaveBeenCalled();
expect(state.resizer.setMode) expect(state.resizer.setMode)
.toHaveBeenCalledWith('width'); .toHaveBeenCalledWith('width');
}); });
...@@ -795,107 +806,152 @@ ...@@ -795,107 +806,152 @@
describe('play', function () { describe('play', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
spyOn(player, 'playVideo').andCallThrough();
state.videoEl = $('video, iframe');
spyOn(state.videoPlayer.player, 'playVideo').andCallThrough();
}); });
describe('when the player is not ready', function () { describe('when the player is not ready', function () {
beforeEach(function () { beforeEach(function () {
player.playVideo = void 0; state.videoPlayer.player.playVideo = void 0;
videoPlayer.play(); state.videoPlayer.play();
}); });
it('does nothing', function () { it('does nothing', function () {
expect(player.playVideo).toBeUndefined(); expect(state.videoPlayer.player.playVideo).toBeUndefined();
}); });
}); });
describe('when the player is ready', function () { describe('when the player is ready', function () {
beforeEach(function () { beforeEach(function () {
player.playVideo.andReturn(true); state.videoPlayer.player.playVideo.andReturn(true);
videoPlayer.play(); state.videoPlayer.play();
}); });
it('delegate to the player', function () { it('delegate to the player', function () {
expect(player.playVideo).toHaveBeenCalled(); expect(state.videoPlayer.player.playVideo).toHaveBeenCalled();
}); });
}); });
}); });
describe('isPlaying', function () { describe('isPlaying', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
spyOn(player, 'getPlayerState').andCallThrough();
state.videoEl = $('video, iframe');
spyOn(state.videoPlayer.player, 'getPlayerState').andCallThrough();
}); });
describe('when the video is playing', function () { describe('when the video is playing', function () {
beforeEach(function () { beforeEach(function () {
player.getPlayerState.andReturn(YT.PlayerState.PLAYING); state.videoPlayer.player.getPlayerState.andReturn(YT.PlayerState.PLAYING);
}); });
it('return true', function () { it('return true', function () {
expect(videoPlayer.isPlaying()).toBeTruthy(); expect(state.videoPlayer.isPlaying()).toBeTruthy();
}); });
}); });
describe('when the video is not playing', function () { describe('when the video is not playing', function () {
beforeEach(function () { beforeEach(function () {
player.getPlayerState.andReturn(YT.PlayerState.PAUSED); state.videoPlayer.player.getPlayerState.andReturn(YT.PlayerState.PAUSED);
}); });
it('return false', function () { it('return false', function () {
expect(videoPlayer.isPlaying()).toBeFalsy(); expect(state.videoPlayer.isPlaying()).toBeFalsy();
}); });
}); });
}); });
describe('pause', function () { describe('pause', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
spyOn(player, 'pauseVideo').andCallThrough();
videoPlayer.pause(); state.videoEl = $('video, iframe');
spyOn(state.videoPlayer.player, 'pauseVideo').andCallThrough();
state.videoPlayer.pause();
}); });
it('delegate to the player', function () { it('delegate to the player', function () {
expect(player.pauseVideo).toHaveBeenCalled(); expect(state.videoPlayer.player.pauseVideo).toHaveBeenCalled();
}); });
}); });
describe('duration', function () { describe('duration', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
spyOn(player, 'getDuration').andCallThrough();
videoPlayer.duration(); state.videoEl = $('video, iframe');
spyOn(state.videoPlayer.player, 'getDuration').andCallThrough();
state.videoPlayer.duration();
}); });
it('delegate to the player', function () { it('delegate to the player', function () {
expect(player.getDuration).toHaveBeenCalled(); expect(state.videoPlayer.player.getDuration).toHaveBeenCalled();
});
});
describe('getDuration', function () {
beforeEach(function () {
// We need to make sure that metadata is returned via an AJAX
// request. Without the jasmine.stubRequests() below we will
// get the error:
//
// this.metadata[this.youtubeId(...)] is undefined
jasmine.stubRequests();
state = jasmine.initializePlayerYouTube();
state.videoEl = $('video, iframe');
spyOn(state, 'getDuration').andCallThrough();
// When `state.videoPlayer.player.getDuration()` returns a `0`,
// the fall-back function `state.getDuration()` will be called.
state.videoPlayer.player.getDuration.andReturn(0);
});
it('getDuration is called as a fall-back', function () {
state.videoPlayer.duration();
expect(state.getDuration).toHaveBeenCalled();
}); });
}); });
describe('playback rate', function () { describe('playback rate', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
player.setPlaybackRate(1.5);
state.videoEl = $('video, iframe');
state.videoPlayer.player.setPlaybackRate(1.5);
}); });
it('set the player playback rate', function () { it('set the player playback rate', function () {
expect(player.video.playbackRate).toEqual(1.5); expect(state.videoPlayer.player.video.playbackRate).toEqual(1.5);
}); });
}); });
describe('volume', function () { describe('volume', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
spyOn(player, 'getVolume').andCallThrough();
state.videoEl = $('video, iframe');
spyOn(state.videoPlayer.player, 'getVolume').andCallThrough();
}); });
it('set the player volume', function () { it('set the player volume', function () {
var expectedValue = 60, var expectedValue = 60,
realValue; realValue;
player.setVolume(60); state.videoPlayer.player.setVolume(60);
realValue = Math.round(player.getVolume()*100); realValue = Math.round(state.videoPlayer.player.getVolume()*100);
expect(realValue).toEqual(expectedValue); expect(realValue).toEqual(expectedValue);
}); });
...@@ -903,9 +959,14 @@ ...@@ -903,9 +959,14 @@
describe('on Touch devices', function () { describe('on Touch devices', function () {
it('`is-touch` class name is added to container', function () { it('`is-touch` class name is added to container', function () {
$.each(['iPad', 'Android', 'iPhone'], function(index, device) { $.each(
['iPad', 'Android', 'iPhone'],
function (index, device)
{
window.onTouchBasedDevice.andReturn([device]); window.onTouchBasedDevice.andReturn([device]);
initialize(); state = jasmine.initializePlayer();
state.videoEl = $('video, iframe');
expect(state.el).toHaveClass('is-touch'); expect(state.el).toHaveClass('is-touch');
}); });
...@@ -913,11 +974,13 @@ ...@@ -913,11 +974,13 @@
it('modules are not initialized on iPhone', function () { it('modules are not initialized on iPhone', function () {
window.onTouchBasedDevice.andReturn(['iPhone']); window.onTouchBasedDevice.andReturn(['iPhone']);
initialize(); state = jasmine.initializePlayer();
state.videoEl = $('video, iframe');
var modules = [ var modules = [
videoControl, videoCaption, videoProgressSlider, state.videoControl, state.videoCaption, state.videoProgressSlider,
videoSpeedControl, videoVolumeControl state.videoSpeedControl, state.videoVolumeControl
]; ];
$.each(modules, function (index, module) { $.each(modules, function (index, module) {
...@@ -925,37 +988,42 @@ ...@@ -925,37 +988,42 @@
}); });
}); });
$.each(['iPad', 'Android'], function(index, device) { $.each(['iPad', 'Android'], function (index, device) {
var message = 'controls become visible after playing starts on ' + var message = 'controls become visible after playing starts ' +
device; 'on ' + device;
it(message, function() {
var controls;
window.onTouchBasedDevice.andReturn([device]);
runs(function () { it(message, function () {
initialize(); var controls;
controls = state.el.find('.video-controls');
});
waitsFor(function () { window.onTouchBasedDevice.andReturn([device]);
return state.el.hasClass('is-initialized');
},'Video is not initialized.' , WAIT_TIMEOUT);
runs(function () { runs(function () {
expect(controls).toHaveClass('is-hidden'); state = jasmine.initializePlayer();
videoPlayer.play();
});
waitsFor(function () { state.videoEl = $('video, iframe');
duration = videoPlayer.duration();
return duration > 0 && videoPlayer.isPlaying(); controls = state.el.find('.video-controls');
},'Video does not play.' , WAIT_TIMEOUT); });
runs(function () { waitsFor(function () {
expect(controls).not.toHaveClass('is-hidden'); return state.el.hasClass('is-initialized');
},'Video is not initialized.' , WAIT_TIMEOUT);
runs(function () {
expect(controls).toHaveClass('is-hidden');
state.videoPlayer.play();
});
waitsFor(function () {
duration = state.videoPlayer.duration();
return duration > 0 && state.videoPlayer.isPlaying();
},'Video does not play.' , WAIT_TIMEOUT);
runs(function () {
expect(controls).not.toHaveClass('is-hidden');
});
}); });
});
}); });
}); });
}); });
......
(function() { (function (undefined) {
describe('VideoProgressSlider', function() { describe('VideoProgressSlider', function () {
var state, videoPlayer, videoProgressSlider, oldOTBD; var state, oldOTBD;
function initialize() { beforeEach(function () {
loadFixtures('video_all.html');
state = new Video('#example');
videoPlayer = state.videoPlayer;
videoProgressSlider = state.videoProgressSlider;
}
beforeEach(function() {
oldOTBD = window.onTouchBasedDevice; oldOTBD = window.onTouchBasedDevice;
window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice') window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice')
.andReturn(null); .andReturn(null);
}); });
afterEach(function() { afterEach(function () {
$('source').remove(); $('source').remove();
window.onTouchBasedDevice = oldOTBD; window.onTouchBasedDevice = oldOTBD;
}); });
describe('constructor', function() { describe('constructor', function () {
describe('on a non-touch based device', function() { describe('on a non-touch based device', function () {
beforeEach(function() { beforeEach(function () {
spyOn($.fn, 'slider').andCallThrough(); spyOn($.fn, 'slider').andCallThrough();
initialize();
state = jasmine.initializePlayer();
}); });
it('build the slider', function() { it('build the slider', function () {
expect(videoProgressSlider.slider).toBe('.slider'); expect(state.videoProgressSlider.slider).toBe('.slider');
expect($.fn.slider).toHaveBeenCalledWith({ expect($.fn.slider).toHaveBeenCalledWith({
range: 'min', range: 'min',
change: videoProgressSlider.onChange, change: state.videoProgressSlider.onChange,
slide: videoProgressSlider.onSlide, slide: state.videoProgressSlider.onSlide,
stop: videoProgressSlider.onStop stop: state.videoProgressSlider.onStop
}); });
}); });
it('build the seek handle', function() { it('build the seek handle', function () {
expect(videoProgressSlider.handle) expect(state.videoProgressSlider.handle)
.toBe('.slider .ui-slider-handle'); .toBe('.slider .ui-slider-handle');
}); });
}); });
describe('on a touch-based device', function() { describe('on a touch-based device', function () {
it('does not build the slider on iPhone', function() { it('does not build the slider on iPhone', function () {
window.onTouchBasedDevice.andReturn(['iPhone']); window.onTouchBasedDevice.andReturn(['iPhone']);
initialize();
expect(videoProgressSlider).toBeUndefined(); state = jasmine.initializePlayer();
expect(state.videoProgressSlider).toBeUndefined();
// We can't expect $.fn.slider not to have been called, // We can't expect $.fn.slider not to have been called,
// because sliders are used in other parts of Video. // because sliders are used in other parts of Video.
}); });
$.each(['iPad', 'Android'], function(index, device) { $.each(['iPad', 'Android'], function (index, device) {
it('build the slider on ' + device, function() { it('build the slider on ' + device, function () {
window.onTouchBasedDevice.andReturn([device]); window.onTouchBasedDevice.andReturn([device]);
initialize();
expect(videoProgressSlider.slider).toBeDefined(); state = jasmine.initializePlayer();
expect(state.videoProgressSlider.slider).toBeDefined();
}); });
}); });
}); });
}); });
describe('play', function() { describe('play', function () {
beforeEach(function() { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
}); });
describe('when the slider was already built', function() { describe('when the slider was already built', function () {
var spy; var spy;
beforeEach(function() { beforeEach(function () {
spy = spyOn(videoProgressSlider, 'buildSlider'); spy = spyOn(state.videoProgressSlider, 'buildSlider');
spy.andCallThrough(); spy.andCallThrough();
videoPlayer.play(); state.videoPlayer.play();
}); });
it('does not build the slider', function() { it('does not build the slider', function () {
expect(spy.callCount).toEqual(0); expect(spy.callCount).toEqual(0);
}); });
}); });
...@@ -86,40 +83,40 @@ ...@@ -86,40 +83,40 @@
// Currently, the slider is not rebuilt if it does not exist. // Currently, the slider is not rebuilt if it does not exist.
}); });
describe('updatePlayTime', function() { describe('updatePlayTime', function () {
beforeEach(function() { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
}); });
describe('when frozen', function() { describe('when frozen', function () {
beforeEach(function() { beforeEach(function () {
spyOn($.fn, 'slider').andCallThrough(); spyOn($.fn, 'slider').andCallThrough();
videoProgressSlider.frozen = true; state.videoProgressSlider.frozen = true;
videoProgressSlider.updatePlayTime(20, 120); state.videoProgressSlider.updatePlayTime(20, 120);
}); });
it('does not update the slider', function() { it('does not update the slider', function () {
expect($.fn.slider).not.toHaveBeenCalled(); expect($.fn.slider).not.toHaveBeenCalled();
}); });
}); });
describe('when not frozen', function() { describe('when not frozen', function () {
beforeEach(function() { beforeEach(function () {
spyOn($.fn, 'slider').andCallThrough(); spyOn($.fn, 'slider').andCallThrough();
videoProgressSlider.frozen = false; state.videoProgressSlider.frozen = false;
videoProgressSlider.updatePlayTime({ state.videoProgressSlider.updatePlayTime({
time: 20, time: 20,
duration: 120 duration: 120
}); });
}); });
it('update the max value of the slider', function() { it('update the max value of the slider', function () {
expect($.fn.slider).toHaveBeenCalledWith( expect($.fn.slider).toHaveBeenCalledWith(
'option', 'max', 120 'option', 'max', 120
); );
}); });
it('update current value of the slider', function() { it('update current value of the slider', function () {
expect($.fn.slider).toHaveBeenCalledWith( expect($.fn.slider).toHaveBeenCalledWith(
'option', 'value', 20 'option', 'value', 20
); );
...@@ -127,72 +124,74 @@ ...@@ -127,72 +124,74 @@
}); });
}); });
describe('onSlide', function() { describe('onSlide', function () {
beforeEach(function() { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
spyOn($.fn, 'slider').andCallThrough(); spyOn($.fn, 'slider').andCallThrough();
spyOn(videoPlayer, 'onSlideSeek').andCallThrough(); spyOn(state.videoPlayer, 'onSlideSeek').andCallThrough();
}); });
// Disabled 12/30/13 due to flakiness in master // Disabled 12/30/13 due to flakiness in master
xit('freeze the slider', function() { xit('freeze the slider', function () {
videoProgressSlider.onSlide( state.videoProgressSlider.onSlide(
jQuery.Event('slide'), { value: 20 } jQuery.Event('slide'), { value: 20 }
); );
expect(videoProgressSlider.frozen).toBeTruthy(); expect(state.videoProgressSlider.frozen).toBeTruthy();
}); });
// Disabled 12/30/13 due to flakiness in master // Disabled 12/30/13 due to flakiness in master
xit('trigger seek event', function() { xit('trigger seek event', function () {
videoProgressSlider.onSlide( state.videoProgressSlider.onSlide(
jQuery.Event('slide'), { value: 20 } jQuery.Event('slide'), { value: 20 }
); );
expect(videoPlayer.onSlideSeek).toHaveBeenCalled(); expect(state.videoPlayer.onSlideSeek).toHaveBeenCalled();
}); });
}); });
describe('onStop', function() { describe('onStop', function () {
beforeEach(function() { beforeEach(function () {
jasmine.Clock.useMock(); jasmine.Clock.useMock();
initialize(); state = jasmine.initializePlayer();
spyOn(videoPlayer, 'onSlideSeek').andCallThrough();
spyOn(state.videoPlayer, 'onSlideSeek').andCallThrough();
}); });
// Disabled 12/30/13 due to flakiness in master // Disabled 12/30/13 due to flakiness in master
xit('freeze the slider', function() { xit('freeze the slider', function () {
videoProgressSlider.onStop( state.videoProgressSlider.onStop(
jQuery.Event('stop'), { value: 20 } jQuery.Event('stop'), { value: 20 }
); );
expect(videoProgressSlider.frozen).toBeTruthy(); expect(state.videoProgressSlider.frozen).toBeTruthy();
}); });
// Disabled 12/30/13 due to flakiness in master // Disabled 12/30/13 due to flakiness in master
xit('trigger seek event', function() { xit('trigger seek event', function () {
videoProgressSlider.onStop( state.videoProgressSlider.onStop(
jQuery.Event('stop'), { value: 20 } jQuery.Event('stop'), { value: 20 }
); );
expect(videoPlayer.onSlideSeek).toHaveBeenCalled(); expect(state.videoPlayer.onSlideSeek).toHaveBeenCalled();
}); });
// Disabled 12/30/13 due to flakiness in master // Disabled 12/30/13 due to flakiness in master
xit('set timeout to unfreeze the slider', function() { xit('set timeout to unfreeze the slider', function () {
videoProgressSlider.onStop( state.videoProgressSlider.onStop(
jQuery.Event('stop'), { value: 20 } jQuery.Event('stop'), { value: 20 }
); );
jasmine.Clock.tick(200); jasmine.Clock.tick(200);
expect(videoProgressSlider.frozen).toBeFalsy(); expect(state.videoProgressSlider.frozen).toBeFalsy();
}); });
}); });
it('getRangeParams' , function() { it('getRangeParams' , function () {
var testCases = [ var testCases = [
{ {
startTime: 10, startTime: 10,
...@@ -211,9 +210,9 @@ ...@@ -211,9 +210,9 @@
} }
]; ];
initialize(); state = jasmine.initializePlayer();
$.each(testCases, function(index, testCase) { $.each(testCases, function (index, testCase) {
var step = 100/testCase.duration, var step = 100/testCase.duration,
left = testCase.startTime*step, left = testCase.startTime*step,
width = testCase.endTime*step - left, width = testCase.endTime*step - left,
...@@ -221,7 +220,7 @@ ...@@ -221,7 +220,7 @@
left: left + '%', left: left + '%',
width: width + '%' width: width + '%'
}, },
params = videoProgressSlider.getRangeParams( params = state.videoProgressSlider.getRangeParams(
testCase.startTime, testCase.endTime, testCase.duration testCase.startTime, testCase.endTime, testCase.duration
); );
...@@ -231,40 +230,44 @@ ...@@ -231,40 +230,44 @@
describe('notifyThroughHandleEnd', function () { describe('notifyThroughHandleEnd', function () {
beforeEach(function () { beforeEach(function () {
initialize(); state = jasmine.initializePlayer();
spyOnEvent(videoProgressSlider.handle, 'focus'); spyOnEvent(state.videoProgressSlider.handle, 'focus');
spyOn(videoProgressSlider, 'notifyThroughHandleEnd') spyOn(state.videoProgressSlider, 'notifyThroughHandleEnd')
.andCallThrough(); .andCallThrough();
}); });
it('params.end = true', function () { it('params.end = true', function () {
videoProgressSlider.notifyThroughHandleEnd({end: true}); state.videoProgressSlider.notifyThroughHandleEnd({end: true});
expect(videoProgressSlider.handle.attr('title')) expect(state.videoProgressSlider.handle.attr('title'))
.toBe('video ended'); .toBe('video ended');
expect('focus').toHaveBeenTriggeredOn(videoProgressSlider.handle); expect('focus').toHaveBeenTriggeredOn(
state.videoProgressSlider.handle
);
}); });
it('params.end = false', function () { it('params.end = false', function () {
videoProgressSlider.notifyThroughHandleEnd({end: false}); state.videoProgressSlider.notifyThroughHandleEnd({end: false});
expect(videoProgressSlider.handle.attr('title')) expect(state.videoProgressSlider.handle.attr('title'))
.toBe('video position'); .toBe('video position');
expect('focus').not.toHaveBeenTriggeredOn(videoProgressSlider.handle); expect('focus').not.toHaveBeenTriggeredOn(
state.videoProgressSlider.handle
);
}); });
it('is called when video plays', function () { it('is called when video plays', function () {
videoPlayer.play(); state.videoPlayer.play();
waitsFor(function () { waitsFor(function () {
return videoPlayer.isPlaying(); return state.videoPlayer.isPlaying();
}, 'duration is set, video is playing', 5000); }, 'duration is set, video is playing', 5000);
runs(function () { runs(function () {
expect(videoProgressSlider.notifyThroughHandleEnd) expect(state.videoProgressSlider.notifyThroughHandleEnd)
.toHaveBeenCalledWith({end: false}); .toHaveBeenCalledWith({end: false});
}); });
}); });
......
(function() { (function (undefined) {
describe('VideoQualityControl', function() { describe('VideoQualityControl', function () {
var state, videoControl, videoQualityControl, oldOTBD; var state, oldOTBD;
function initialize() { beforeEach(function () {
loadFixtures('video.html'); oldOTBD = window.onTouchBasedDevice;
state = new Video('#example'); window.onTouchBasedDevice = jasmine
videoControl = state.videoControl; .createSpy('onTouchBasedDevice')
videoQualityControl = state.videoQualityControl; .andReturn(null);
} });
beforeEach(function() {
oldOTBD = window.onTouchBasedDevice;
window.onTouchBasedDevice = jasmine
.createSpy('onTouchBasedDevice')
.andReturn(null);
});
afterEach(function() {
$('source').remove();
window.onTouchBasedDevice = oldOTBD;
});
describe('constructor', function() { afterEach(function () {
var oldYT = window.YT; $('source').remove();
window.onTouchBasedDevice = oldOTBD;
beforeEach(function() {
window.YT = {
Player: function () { },
PlayerState: oldYT.PlayerState,
ready: function(f){f();}
};
initialize();
});
afterEach(function () {
window.YT = oldYT;
});
it('render the quality control', function() {
var container = videoControl.secondaryControlsEl;
expect(container).toContain('a.quality_control');
});
it('add ARIA attributes to quality control', function () {
var qualityControl = $('a.quality_control');
expect(qualityControl).toHaveAttrs({
'role': 'button',
'title': 'HD off',
'aria-disabled': 'false'
}); });
});
it('bind the quality control', function() { describe('constructor', function () {
var handler = videoQualityControl.toggleQuality; beforeEach(function () {
expect($('a.quality_control')).toHandleWith('click', handler); state = jasmine.initializePlayer('video.html');
}); });
});
}); it('render the quality control', function () {
var container = state.videoControl.secondaryControlsEl;
expect(container).toContain('a.quality_control');
});
it('add ARIA attributes to quality control', function () {
var qualityControl = $('a.quality_control');
expect(qualityControl).toHaveAttrs({
'role': 'button',
'title': 'HD off',
'aria-disabled': 'false'
});
});
it('bind the quality control', function () {
var handler = state.videoQualityControl.toggleQuality;
expect($('a.quality_control')).toHandleWith('click', handler);
});
});
});
}).call(this); }).call(this);
(function() { (function (undefined) {
describe('VideoSpeedControl', function() { describe('VideoSpeedControl', function () {
var state, videoPlayer, videoControl, videoSpeedControl; var state, oldOTBD;
function initialize() { beforeEach(function () {
loadFixtures('video_all.html'); oldOTBD = window.onTouchBasedDevice;
state = new Video('#example'); window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice')
videoPlayer = state.videoPlayer; .andReturn(null);
videoControl = state.videoControl;
videoSpeedControl = state.videoSpeedControl;
}
beforeEach(function() {
oldOTBD = window.onTouchBasedDevice;
window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn(null);
});
afterEach(function() {
$('source').remove();
window.onTouchBasedDevice = oldOTBD;
});
describe('constructor', function() {
describe('always', function() {
beforeEach(function() {
initialize();
});
it('add the video speed control to player', function() {
var li, secondaryControls;
secondaryControls = $('.secondary-controls');
li = secondaryControls.find('.video_speeds li');
expect(secondaryControls).toContain('.speeds');
expect(secondaryControls).toContain('.video_speeds');
expect(secondaryControls.find('p.active').text()).toBe('1.0x');
expect(li.filter('.active')).toHaveData('speed', videoSpeedControl.currentSpeed);
expect(li.length).toBe(videoSpeedControl.speeds.length);
$.each(li.toArray().reverse(), function(index, link) {
expect($(link)).toHaveData('speed', videoSpeedControl.speeds[index]);
expect($(link).find('a').text()).toBe(videoSpeedControl.speeds[index] + 'x');
});
});
it('add ARIA attributes to speed control', function () {
var speedControl = $('div.speeds>a');
expect(speedControl).toHaveAttrs({
'role': 'button',
'title': 'Speeds',
'aria-disabled': 'false'
});
});
it('bind to change video speed link', function() {
expect($('.video_speeds a')).toHandleWith('click', videoSpeedControl.changeVideoSpeed);
});
});
describe('when running on touch based device', function() {
$.each(['iPad', 'Android'], function(index, device) {
it('is not rendered on' + device, function() {
window.onTouchBasedDevice.andReturn([device]);
initialize();
expect(state.el.find('div.speeds')).not.toExist();
});
});
});
describe('when running on non-touch based device', function() {
beforeEach(function() {
initialize();
}); });
it('open the speed toggle on hover', function() { afterEach(function () {
$('.speeds').mouseenter(); $('source').remove();
expect($('.speeds')).toHaveClass('open'); window.onTouchBasedDevice = oldOTBD;
$('.speeds').mouseleave();
expect($('.speeds')).not.toHaveClass('open');
}); });
it('close the speed toggle on mouse out', function() { describe('constructor', function () {
$('.speeds').mouseenter().mouseleave(); describe('always', function () {
expect($('.speeds')).not.toHaveClass('open'); beforeEach(function () {
state = jasmine.initializePlayer();
});
it('add the video speed control to player', function () {
var li, secondaryControls;
secondaryControls = $('.secondary-controls');
li = secondaryControls.find('.video_speeds li');
expect(secondaryControls).toContain('.speeds');
expect(secondaryControls).toContain('.video_speeds');
expect(secondaryControls.find('p.active').text())
.toBe('1.0x');
expect(li.filter('.active')).toHaveData(
'speed', state.videoSpeedControl.currentSpeed
);
expect(li.length).toBe(state.videoSpeedControl.speeds.length);
$.each(li.toArray().reverse(), function (index, link) {
expect($(link)).toHaveData(
'speed', state.videoSpeedControl.speeds[index]
);
expect($(link).find('a').text()).toBe(
state.videoSpeedControl.speeds[index] + 'x'
);
});
});
it('add ARIA attributes to speed control', function () {
var speedControl = $('div.speeds>a');
expect(speedControl).toHaveAttrs({
'role': 'button',
'title': 'Speeds',
'aria-disabled': 'false'
});
});
it('bind to change video speed link', function () {
expect($('.video_speeds a')).toHandleWith(
'click', state.videoSpeedControl.changeVideoSpeed
);
});
});
describe('when running on touch based device', function () {
$.each(['iPad', 'Android'], function (index, device) {
it('is not rendered on' + device, function () {
window.onTouchBasedDevice.andReturn([device]);
state = jasmine.initializePlayer();
expect(state.el.find('div.speeds')).not.toExist();
});
});
});
describe('when running on non-touch based device', function () {
beforeEach(function () {
state = jasmine.initializePlayer();
});
it('open the speed toggle on hover', function () {
$('.speeds').mouseenter();
expect($('.speeds')).toHaveClass('open');
$('.speeds').mouseleave();
expect($('.speeds')).not.toHaveClass('open');
});
it('close the speed toggle on mouse out', function () {
$('.speeds').mouseenter().mouseleave();
expect($('.speeds')).not.toHaveClass('open');
});
it('close the speed toggle on click', function () {
$('.speeds').mouseenter().click();
expect($('.speeds')).not.toHaveClass('open');
});
// Tabbing depends on the following order:
// 1. Play anchor
// 2. Speed anchor
// 3. A number of speed entry anchors
// 4. Volume anchor
// If another focusable element is inserted or if the order is
// changed, things will malfunction as a flag,
// state.previousFocus, is set in the 1,3,4 elements and is
// used to determine the behavior of foucus() and blur() for
// the speed anchor.
it(
'checks for a certain order in focusable elements in ' +
'video controls',
function ()
{
var foundFirst = false,
playIndex, speedIndex, firstSpeedEntry, lastSpeedEntry,
volumeIndex;
$('.video-controls').find('a, :focusable').each(
function (index)
{
if ($(this).hasClass('video_control')) {
playIndex = index;
} else if ($(this).parent().hasClass('speeds')) {
speedIndex = index;
} else if ($(this).hasClass('speed_link')) {
if (!foundFirst) {
firstSpeedEntry = index;
foundFirst = true;
}
lastSpeedEntry = index;
} else if ($(this).parent().hasClass('volume')) {
volumeIndex = index;
}
});
expect(playIndex+1).toEqual(speedIndex);
expect(speedIndex+1).toEqual(firstSpeedEntry);
expect(lastSpeedEntry+1).toEqual(volumeIndex);
});
});
}); });
it('close the speed toggle on click', function() { describe('changeVideoSpeed', function () {
$('.speeds').mouseenter().click(); // This is an unnecessary test. The internal browser API, and
expect($('.speeds')).not.toHaveClass('open'); // YouTube API detect (and do not do anything) if there is a
}); // request for a speed that is already set.
// Tabbing depends on the following order: //
// 1. Play anchor // describe("when new speed is the same") ...
// 2. Speed anchor
// 3. A number of speed entry anchors describe('when new speed is not the same', function () {
// 4. Volume anchor beforeEach(function () {
// If another focusable element is inserted or if the order is changed, things will state = jasmine.initializePlayer();
// malfunction as a flag, state.previousFocus, is set in the 1,3,4 elements and is state.videoSpeedControl.setSpeed(1.0);
// used to determine the behavior of foucus() and blur() for the speed anchor. spyOn(state.videoPlayer, 'onSpeedChange').andCallThrough();
it('checks for a certain order in focusable elements in video controls', function() {
var playIndex, speedIndex, firstSpeedEntry, lastSpeedEntry, volumeIndex, foundFirst = false; $('li[data-speed="0.75"] a').click();
$('.video-controls').find('a, :focusable').each(function(index) { });
if ($(this).hasClass('video_control')) {
playIndex = index; it('trigger speedChange event', function () {
} expect(state.videoPlayer.onSpeedChange).toHaveBeenCalled();
else if ($(this).parent().hasClass('speeds')) { expect(state.videoSpeedControl.currentSpeed).toEqual(0.75);
speedIndex = index; });
} });
else if ($(this).hasClass('speed_link')) {
if (!foundFirst) { describe(
firstSpeedEntry = index; 'make sure the speed control gets the focus afterwards',
foundFirst = true; function ()
} {
lastSpeedEntry = index; var anchor;
}
else if ($(this).parent().hasClass('volume')) { beforeEach(function () {
volumeIndex = index; state = jasmine.initializePlayer();
} anchor= $('.speeds > a').first();
}); state.videoSpeedControl.setSpeed(1.0);
expect(playIndex+1).toEqual(speedIndex); spyOnEvent(anchor, 'focus');
expect(speedIndex+1).toEqual(firstSpeedEntry); });
expect(lastSpeedEntry+1).toEqual(volumeIndex);
}); it('when the speed is the same', function () {
}); $('li[data-speed="1.0"] a').click();
}); expect('focus').toHaveBeenTriggeredOn(anchor);
});
describe('changeVideoSpeed', function() {
// This is an unnecessary test. The internal browser API, and YouTube API it('when the speed is not the same', function () {
// detect (and do not do anything) if there is a request for a speed that $('li[data-speed="0.75"] a').click();
// is already set. expect('focus').toHaveBeenTriggeredOn(anchor);
// });
// describe("when new speed is the same") ... });
describe('when new speed is not the same', function() {
beforeEach(function() {
initialize();
videoSpeedControl.setSpeed(1.0);
spyOn(videoPlayer, 'onSpeedChange').andCallThrough();
$('li[data-speed="0.75"] a').click();
});
it('trigger speedChange event', function() {
expect(videoPlayer.onSpeedChange).toHaveBeenCalled();
expect(videoSpeedControl.currentSpeed).toEqual(0.75);
});
});
describe('make sure the speed control gets the focus afterwards', function() {
var anchor;
beforeEach(function() {
initialize();
anchor= $('.speeds > a').first();
videoSpeedControl.setSpeed(1.0);
spyOnEvent(anchor, 'focus');
});
it('when the speed is the same', function() {
$('li[data-speed="1.0"] a').click();
expect('focus').toHaveBeenTriggeredOn(anchor);
}); });
it('when the speed is not the same', function() { describe('onSpeedChange', function () {
$('li[data-speed="0.75"] a').click(); beforeEach(function () {
expect('focus').toHaveBeenTriggeredOn(anchor); state = jasmine.initializePlayer();
$('li[data-speed="1.0"] a').addClass('active');
state.videoSpeedControl.setSpeed(0.75);
});
it('set the new speed as active', function () {
expect($('.video_speeds li[data-speed="1.0"]'))
.not.toHaveClass('active');
expect($('.video_speeds li[data-speed="0.75"]'))
.toHaveClass('active');
expect($('.speeds p.active')).toHaveHtml('0.75x');
});
}); });
});
}); });
describe('onSpeedChange', function() {
beforeEach(function() {
initialize();
$('li[data-speed="1.0"] a').addClass('active');
videoSpeedControl.setSpeed(0.75);
});
it('set the new speed as active', function() {
expect($('.video_speeds li[data-speed="1.0"]')).not.toHaveClass('active');
expect($('.video_speeds li[data-speed="0.75"]')).toHaveClass('active');
expect($('.speeds p.active')).toHaveHtml('0.75x');
});
});
});
}).call(this); }).call(this);
(function() { (function (undefined) {
describe('VideoVolumeControl', function() { describe('VideoVolumeControl', function () {
var state, videoControl, videoVolumeControl, oldOTBD; var state, oldOTBD;
function initialize() { beforeEach(function () {
loadFixtures('video_all.html'); oldOTBD = window.onTouchBasedDevice;
state = new Video('#example'); window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice')
videoControl = state.videoControl; .andReturn(null);
videoVolumeControl = state.videoVolumeControl;
} afterEach(function () {
$('source').remove();
beforeEach(function() { window.onTouchBasedDevice = oldOTBD;
oldOTBD = window.onTouchBasedDevice; });
window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn(null);
});
afterEach(function() { describe('constructor', function () {
$('source').remove(); beforeEach(function () {
window.onTouchBasedDevice = oldOTBD; spyOn($.fn, 'slider').andCallThrough();
}); state = jasmine.initializePlayer();
});
describe('constructor', function() { it('initialize currentVolume to 100%', function () {
beforeEach(function() { // Please note that:
spyOn($.fn, 'slider').andCallThrough(); // 0% -> 0
$.cookie.andReturn('75'); // 100% -> 1.0
initialize(); expect(state.videoVolumeControl.currentVolume).toEqual(1);
}); });
it('initialize currentVolume to 75', function() {
expect(state.videoVolumeControl.currentVolume).toEqual(75);
});
it('render the volume control', function() {
expect(videoControl.secondaryControlsEl.html()).toContain("<div class=\"volume\">\n");
});
it('create the slider', function() {
expect($.fn.slider).toHaveBeenCalledWith({
orientation: "vertical",
range: "min",
min: 0,
max: 100,
value: videoVolumeControl.currentVolume,
change: videoVolumeControl.onChange,
slide: videoVolumeControl.onChange
});
});
it('add ARIA attributes to slider handle', function () {
var sliderHandle = $('div.volume-slider>a.ui-slider-handle'),
arr = ['muted', 'very low', 'low', 'average', 'loud', 'very loud',
'maximum'];
expect(sliderHandle).toHaveAttrs({
'role': 'slider',
'title': 'volume',
'aria-disabled': 'false',
'aria-valuemin': '0',
'aria-valuemax': '100'
});
expect(sliderHandle.attr('aria-valuenow')).toBeInRange(0, 100);
expect(sliderHandle.attr('aria-valuetext')).toBeInArray(arr);
}); it('render the volume control', function () {
expect(state.videoControl.secondaryControlsEl.html())
.toContain("<div class=\"volume\">\n");
});
it('add ARIA attributes to volume control', function () { it('create the slider', function () {
var volumeControl = $('div.volume>a'); expect($.fn.slider).toHaveBeenCalledWith({
expect(volumeControl).toHaveAttrs({ orientation: "vertical",
'role': 'button', range: "min",
'title': 'Volume', min: 0,
'aria-disabled': 'false' max: 100,
}); value: state.videoVolumeControl.currentVolume,
}); change: state.videoVolumeControl.onChange,
slide: state.videoVolumeControl.onChange
it('bind the volume control', function() { });
expect($('.volume>a')).toHandleWith('click', videoVolumeControl.toggleMute); });
expect($('.volume')).not.toHaveClass('open');
$('.volume').mouseenter();
expect($('.volume')).toHaveClass('open');
$('.volume').mouseleave();
expect($('.volume')).not.toHaveClass('open');
});
});
describe('onChange', function() { it('add ARIA attributes to slider handle', function () {
beforeEach(function() { var sliderHandle = $('div.volume-slider>a.ui-slider-handle'),
initialize(); arr = [
}); 'muted', 'very low', 'low', 'average', 'loud',
'very loud', 'maximum'
];
expect(sliderHandle).toHaveAttrs({
'role': 'slider',
'title': 'volume',
'aria-disabled': 'false',
'aria-valuemin': '0',
'aria-valuemax': '100'
});
expect(sliderHandle.attr('aria-valuenow')).toBeInRange(0, 100);
expect(sliderHandle.attr('aria-valuetext')).toBeInArray(arr);
});
describe('when the new volume is more than 0', function() { it('add ARIA attributes to volume control', function () {
beforeEach(function() { var volumeControl = $('div.volume>a');
videoVolumeControl.onChange(void 0, {
value: 60
});
});
it('set the player volume', function() { expect(volumeControl).toHaveAttrs({
expect(videoVolumeControl.currentVolume).toEqual(60); 'role': 'button',
}); 'title': 'Volume',
'aria-disabled': 'false'
});
});
it('remote muted class', function() { it('bind the volume control', function () {
expect($('.volume')).not.toHaveClass('muted'); expect($('.volume>a')).toHandleWith(
}); 'click', state.videoVolumeControl.toggleMute
}); );
expect($('.volume')).not.toHaveClass('open');
describe('when the new volume is 0', function() { $('.volume').mouseenter();
beforeEach(function() { expect($('.volume')).toHaveClass('open');
videoVolumeControl.onChange(void 0, {
value: 0
});
});
it('set the player volume', function() { $('.volume').mouseleave();
expect(videoVolumeControl.currentVolume).toEqual(0); expect($('.volume')).not.toHaveClass('open');
});
}); });
it('add muted class', function() { describe('onChange', function () {
expect($('.volume')).toHaveClass('muted'); var initialData = [{
}); range: 'muted',
}); value: 0,
expectation: 'muted'
var initialData = [ }, {
{ range: 'in ]0,20]',
range: 'muted', value: 10,
value: 0, expectation: 'very low'
expectation: 'muted' }, {
}, range: 'in ]20,40]',
{ value: 30,
range: 'in ]0,20]', expectation: 'low'
value: 10, }, {
expectation: 'very low' range: 'in ]40,60]',
}, value: 50,
{ expectation: 'average'
range: 'in ]20,40]', }, {
value: 30, range: 'in ]60,80]',
expectation: 'low' value: 70,
}, expectation: 'loud'
{ }, {
range: 'in ]40,60]', range: 'in ]80,100[',
value: 50, value: 90,
expectation: 'average' expectation: 'very loud'
}, }, {
{ range: 'maximum',
range: 'in ]60,80]', value: 100,
value: 70, expectation: 'maximum'
expectation: 'loud' }];
},
{ beforeEach(function () {
range: 'in ]80,100[', state = jasmine.initializePlayer();
value: 90,
expectation: 'very loud'
},
{
range: 'maximum',
value: 100,
expectation: 'maximum'
}
];
$.each(initialData, function(index, data) {
describe('when the new volume is ' + data.range, function() {
beforeEach(function() {
videoVolumeControl.onChange(void 0, {
value: data.value
}); });
});
it('changes ARIA attributes', function () { describe('when the new volume is more than 0', function () {
var sliderHandle = $('div.volume-slider>a.ui-slider-handle'); beforeEach(function () {
expect(sliderHandle).toHaveAttrs({ state.videoVolumeControl.onChange(void 0, {
'aria-valuenow': data.value.toString(10), value: 60
'aria-valuetext': data.expectation });
});
it('set the player volume', function () {
expect(state.videoVolumeControl.currentVolume).toEqual(60);
});
it('remote muted class', function () {
expect($('.volume')).not.toHaveClass('muted');
});
}); });
});
});
});
});
describe('toggleMute', function() { describe('when the new volume is 0', function () {
beforeEach(function() { beforeEach(function () {
initialize(); state.videoVolumeControl.onChange(void 0, {
}); value: 0
});
});
describe('when the current volume is more than 0', function() { it('set the player volume', function () {
beforeEach(function() { expect(state.videoVolumeControl.currentVolume).toEqual(0);
videoVolumeControl.currentVolume = 60; });
videoVolumeControl.buttonEl.trigger('click');
});
it('save the previous volume', function() { it('add muted class', function () {
expect(videoVolumeControl.previousVolume).toEqual(60); expect($('.volume')).toHaveClass('muted');
}); });
});
it('set the player volume', function() { $.each(initialData, function (index, data) {
expect(videoVolumeControl.currentVolume).toEqual(0); describe('when the new volume is ' + data.range, function () {
beforeEach(function () {
state.videoVolumeControl.onChange(void 0, {
value: data.value
});
});
it('changes ARIA attributes', function () {
var sliderHandle = $(
'div.volume-slider>a.ui-slider-handle'
);
expect(sliderHandle).toHaveAttrs({
'aria-valuenow': data.value.toString(10),
'aria-valuetext': data.expectation
});
});
});
});
}); });
});
describe('when the current volume is 0', function() { describe('toggleMute', function () {
beforeEach(function() { beforeEach(function () {
videoVolumeControl.currentVolume = 0; state = jasmine.initializePlayer();
videoVolumeControl.previousVolume = 60; });
videoVolumeControl.buttonEl.trigger('click');
}); describe('when the current volume is more than 0', function () {
beforeEach(function () {
state.videoVolumeControl.currentVolume = 60;
state.videoVolumeControl.buttonEl.trigger('click');
});
it('set the player volume to previous volume', function() { it('save the previous volume', function () {
expect(videoVolumeControl.currentVolume).toEqual(60); expect(state.videoVolumeControl.previousVolume).toEqual(60);
});
it('set the player volume', function () {
expect(state.videoVolumeControl.currentVolume).toEqual(0);
});
});
describe('when the current volume is 0', function () {
beforeEach(function () {
state.videoVolumeControl.currentVolume = 0;
state.videoVolumeControl.previousVolume = 60;
state.videoVolumeControl.buttonEl.trigger('click');
});
it('set the player volume to previous volume', function () {
expect(state.videoVolumeControl.currentVolume).toEqual(60);
});
});
}); });
});
}); });
});
}).call(this); }).call(this);
...@@ -476,8 +476,7 @@ function (VideoPlayer) { ...@@ -476,8 +476,7 @@ function (VideoPlayer) {
this.config.endTime = parseInt(this.config.endTime, 10); this.config.endTime = parseInt(this.config.endTime, 10);
if ( if (
!isFinite(this.config.endTime) || !isFinite(this.config.endTime) ||
this.config.endTime < this.config.startTime || this.config.endTime <= this.config.startTime
this.config.endTime === 0
) { ) {
this.config.endTime = null; this.config.endTime = null;
} }
...@@ -560,16 +559,32 @@ function (VideoPlayer) { ...@@ -560,16 +559,32 @@ function (VideoPlayer) {
// example the length of the video can be determined from the meta // example the length of the video can be determined from the meta
// data. // data.
function fetchMetadata() { function fetchMetadata() {
var _this = this; var _this = this,
metadataXHRs = [];
this.metadata = {}; this.metadata = {};
$.each(this.videos, function (speed, url) { $.each(this.videos, function (speed, url) {
_this.getVideoMetadata(url, function (data) { var xhr = _this.getVideoMetadata(url, function (data) {
if (data.data) { if (data.data) {
_this.metadata[data.data.id] = data.data; _this.metadata[data.data.id] = data.data;
} }
}); });
metadataXHRs.push(xhr);
});
$.when.apply(this, metadataXHRs).done(function () {
_this.el.trigger('metadata_received');
// Not only do we trigger the "metadata_received" event, we also
// set a flag to notify that metadata has been received. This
// allows for code that will miss the "metadata_received" event
// to know that metadata has been received. This is important in
// cases when some code will subscribe to the "metadata_received"
// event after it has been triggered.
_this.youtubeMetadataReceived = true;
}); });
} }
......
...@@ -60,7 +60,7 @@ function (HTML5Video, Resizer) { ...@@ -60,7 +60,7 @@ function (HTML5Video, Resizer) {
// via the 'state' object. Much easier to work this way - you don't // via the 'state' object. Much easier to work this way - you don't
// have to do repeated jQuery element selects. // have to do repeated jQuery element selects.
function _initialize(state) { function _initialize(state) {
var youTubeId, player; var youTubeId, player, duration;
// The function is called just once to apply pre-defined configurations // The function is called just once to apply pre-defined configurations
// by student before video starts playing. Waits until the video's // by student before video starts playing. Waits until the video's
...@@ -86,7 +86,7 @@ function (HTML5Video, Resizer) { ...@@ -86,7 +86,7 @@ function (HTML5Video, Resizer) {
// At the start, the initial value of the variable // At the start, the initial value of the variable
// `seekToStartTimeOldSpeed` should always differ from the value // `seekToStartTimeOldSpeed` should always differ from the value
// returned by the duration function. // of `state.speed` variable.
state.videoPlayer.seekToStartTimeOldSpeed = 'void'; state.videoPlayer.seekToStartTimeOldSpeed = 'void';
state.videoPlayer.playerVars = { state.videoPlayer.playerVars = {
...@@ -134,11 +134,20 @@ function (HTML5Video, Resizer) { ...@@ -134,11 +134,20 @@ function (HTML5Video, Resizer) {
_resize(state, videoWidth, videoHeight); _resize(state, videoWidth, videoHeight);
duration = state.videoPlayer.duration();
state.trigger( state.trigger(
'videoControl.updateVcrVidTime', 'videoControl.updateVcrVidTime',
{ {
time: 0, time: 0,
duration: state.videoPlayer.duration() duration: duration
}
);
state.trigger(
'videoProgressSlider.updateStartEndTimeRegion',
{
duration: duration
} }
); );
}, false); }, false);
...@@ -166,6 +175,22 @@ function (HTML5Video, Resizer) { ...@@ -166,6 +175,22 @@ function (HTML5Video, Resizer) {
videoHeight = player.attr('height') || player.height(); videoHeight = player.attr('height') || player.height();
_resize(state, videoWidth, videoHeight); _resize(state, videoWidth, videoHeight);
// After initialization, update the VCR with total time.
// At this point only the metadata duration is available (not
// very precise), but it is better than having 00:00:00 for
// total time.
if (state.youtubeMetadataReceived) {
// Metadata was already received, and is available.
_updateVcrAndRegion(state);
} else {
// We wait for metadata to arrive, before we request the update
// of the VCR video time, and of the start-end time region.
// Metadata contains duration of the video.
state.el.on('metadata_received', function () {
_updateVcrAndRegion(state);
});
}
}); });
} }
...@@ -174,7 +199,26 @@ function (HTML5Video, Resizer) { ...@@ -174,7 +199,26 @@ function (HTML5Video, Resizer) {
} }
} }
function _resize (state, videoWidth, videoHeight) { function _updateVcrAndRegion(state) {
var duration = state.videoPlayer.duration();
state.trigger(
'videoControl.updateVcrVidTime',
{
time: 0,
duration: duration
}
);
state.trigger(
'videoProgressSlider.updateStartEndTimeRegion',
{
duration: duration
}
);
}
function _resize(state, videoWidth, videoHeight) {
state.resizer = new Resizer({ state.resizer = new Resizer({
element: state.videoEl, element: state.videoEl,
elementRatio: videoWidth/videoHeight, elementRatio: videoWidth/videoHeight,
...@@ -652,20 +696,12 @@ function (HTML5Video, Resizer) { ...@@ -652,20 +696,12 @@ function (HTML5Video, Resizer) {
} }
} }
// Rebuild the slider start-end range (if it doesn't take up the this.trigger(
// whole slider). Remember that endTime === null means the end time 'videoProgressSlider.updateStartEndTimeRegion',
// is set to the end of video by default. {
if (!( duration: duration
this.videoPlayer.startTime === 0 && }
this.videoPlayer.endTime === null );
)) {
this.trigger(
'videoProgressSlider.updateStartEndTimeRegion',
{
duration: duration
}
);
}
// If this is not a duration change (if it is, we continue playing // If this is not a duration change (if it is, we continue playing
// from current time), then we need to seek the video to the start // from current time), then we need to seek the video to the start
...@@ -735,10 +771,40 @@ function (HTML5Video, Resizer) { ...@@ -735,10 +771,40 @@ function (HTML5Video, Resizer) {
* This instability is internal to the player API (or browser internals). * This instability is internal to the player API (or browser internals).
*/ */
function duration() { function duration() {
var dur = this.videoPlayer.player.getDuration(); var dur;
// Sometimes the YouTube API doesn't finish instantiating all of it's
// methods, but the execution point arrives here.
//
// This happens when you have start and end times set, and click "Edit"
// in Studio, and then "Save". The Video editor dialog closes, the
// video reloads, but the start-end range is not visible.
if (this.videoPlayer.player.getDuration) {
dur = this.videoPlayer.player.getDuration();
}
// For YouTube videos, before the video starts playing, the API
// function player.getDuration() will return 0. This means that the VCR
// will show total time as 0 when the page just loads (before the user
// clicks the Play button).
//
// We can do betterin a case when dur is 0 (or less than 0). We can ask
// the getDuration() function for total time, which will query the
// metadata for a duration.
//
// Be careful! Often the metadata duration is not very precise. It
// might differ by one or two seconds against the actual time as will
// be reported later on by the player.getDuration() API function.
if (!isFinite(dur) || dur <= 0) {
if (this.videoType === 'youtube') {
dur = this.getDuration();
}
}
if (!isFinite(dur)) { // Just in case the metadata is garbled, or something went wrong, we
dur = this.getDuration(); // have a final check.
if (!isFinite(dur) || dur <= 0) {
dur = 0;
} }
return Math.floor(dur); return Math.floor(dur);
......
...@@ -99,6 +99,9 @@ function () { ...@@ -99,6 +99,9 @@ function () {
.find('.ui-slider-range.ui-widget-header.ui-slider-range-min'); .find('.ui-slider-range.ui-widget-header.ui-slider-range-min');
} }
// Rebuild the slider start-end range (if it doesn't take up the
// whole slider). Remember that endTime === null means the end time
// is set to the end of video by default.
function updateStartEndTimeRegion(params) { function updateStartEndTimeRegion(params) {
var left, width, start, end, duration, rangeParams; var left, width, start, end, duration, rangeParams;
...@@ -110,12 +113,33 @@ function () { ...@@ -110,12 +113,33 @@ function () {
duration = params.duration; duration = params.duration;
} }
start = this.videoPlayer.startTime; start = this.config.startTime;
end = this.config.endTime;
// If end is set to null, then we set it to the end of the video. We if (start > duration) {
// know that start is not a the beginning, therefore we must build a start = 0;
// range. } else {
end = this.videoPlayer.endTime || duration; if (this.currentPlayerMode === 'flash') {
start /= Number(this.speed);
}
}
// If end is set to null, or it is greater than the duration of the
// video, then we set it to the end of the video.
if (
end === null || end > duration
) {
end = duration;
} else if (end !== null) {
if (this.currentPlayerMode === 'flash') {
end /= Number(this.speed);
}
}
// Don't build a range if it takes up the whole slider.
if (start === 0 && end === duration) {
return;
}
// Because JavaScript has weird rounding rules when a series of // Because JavaScript has weird rounding rules when a series of
// mathematical operations are performed in a single statement, we will // mathematical operations are performed in a single statement, we will
......
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