\ No newline at end of file
diff --git a/common/lib/xmodule/xmodule/js/spec/.gitignore b/common/lib/xmodule/xmodule/js/spec/.gitignore
index 03534687ca..a8db2aae70 100644
--- a/common/lib/xmodule/xmodule/js/spec/.gitignore
+++ b/common/lib/xmodule/xmodule/js/spec/.gitignore
@@ -1,2 +1,4 @@
*.js
+# Tests for videoalpha are written in pure JavaScript.
+!videoalpha/*.js
diff --git a/common/lib/xmodule/xmodule/js/spec/helper.coffee b/common/lib/xmodule/xmodule/js/spec/helper.coffee
index 5f7fc27be0..e4595e8105 100644
--- a/common/lib/xmodule/xmodule/js/spec/helper.coffee
+++ b/common/lib/xmodule/xmodule/js/spec/helper.coffee
@@ -8,6 +8,26 @@ window.YT =
BUFFERING: 3
CUED: 5
+window.TYPES =
+ 'undefined' : 'undefined'
+ 'number' : 'number'
+ 'boolean' : 'boolean'
+ 'string' : 'string'
+ '[object Function]': 'function'
+ '[object RegExp]' : 'regexp'
+ '[object Array]' : 'array'
+ '[object Date]' : 'date'
+ '[object Error]' : 'error'
+
+window.TOSTRING = Object.prototype.toString
+window.STATUS = window.YT.PlayerState
+
+window.whatType = (o) ->
+ TYPES[typeof o] || TYPES[TOSTRING.call(o)] || (o ? 'object' : 'null');
+
+# Time waitsFor() should wait for before failing a test.
+window.WAIT_TIMEOUT = 1000
+
jasmine.getFixtures().fixturesPath = 'xmodule/js/fixtures'
jasmine.stubbedMetadata =
@@ -78,7 +98,8 @@ jasmine.stubVideoPlayer = (context, enableParts, createPlayer=true) ->
if createPlayer
return new VideoPlayer(video: context.video)
-jasmine.stubVideoPlayerAlpha = (context, enableParts, createPlayer=true, html5=false) ->
+jasmine.stubVideoPlayerAlpha = (context, enableParts, html5=false) ->
+ console.log('stubVideoPlayerAlpha called')
suite = context.suite
currentPartName = suite.description while suite = suite.parentSuite
if html5 == false
@@ -88,10 +109,8 @@ jasmine.stubVideoPlayerAlpha = (context, enableParts, createPlayer=true, html5=f
jasmine.stubRequests()
YT.Player = undefined
window.OldVideoPlayerAlpha = undefined
- context.video = new VideoAlpha '#example', '.75:slowerSpeedYoutubeId,1.0:normalSpeedYoutubeId'
jasmine.stubYoutubePlayer()
- if createPlayer
- return new VideoPlayerAlpha(video: context.video)
+ return new VideoAlpha '#example', '.75:slowerSpeedYoutubeId,1.0:normalSpeedYoutubeId'
# Stub jQuery.cookie
diff --git a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/html5_video.coffee b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/html5_video.coffee
deleted file mode 100644
index 176ceb7827..0000000000
--- a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/html5_video.coffee
+++ /dev/null
@@ -1,311 +0,0 @@
-describe 'VideoAlpha HTML5Video', ->
- playbackRates = [0.75, 1.0, 1.25, 1.5]
- STATUS = window.YT.PlayerState
- playerVars =
- controls: 0
- wmode: 'transparent'
- rel: 0
- showinfo: 0
- enablejsapi: 1
- modestbranding: 1
- html5: 1
- file = window.location.href.replace(/\/common(.*)$/, '') + '/test_root/data/videoalpha/gizmo'
- html5Sources =
- mp4: "#{file}.mp4"
- webm: "#{file}.webm"
- ogg: "#{file}.ogv"
- onReady = jasmine.createSpy 'onReady'
- onStateChange = jasmine.createSpy 'onStateChange'
-
- beforeEach ->
- loadFixtures 'videoalpha_html5.html'
- @el = $('#example').find('.video')
- @player = new window.HTML5Video.Player @el,
- playerVars: playerVars,
- videoSources: html5Sources,
- events:
- onReady: onReady
- onStateChange: onStateChange
-
- @videoEl = @el.find('.video-player video').get(0)
-
- it 'PlayerState', ->
- expect(HTML5Video.PlayerState).toEqual STATUS
-
- describe 'constructor', ->
- it 'create an html5 video element', ->
- expect(@el.find('.video-player div')).toContain 'video'
-
- it 'check if sources are created in correct way', ->
- sources = $(@videoEl).find('source')
- videoTypes = []
- videoSources = []
- $.each html5Sources, (index, source) ->
- videoTypes.push index
- videoSources.push source
- $.each sources, (index, source) ->
- s = $(source)
- expect($.inArray(s.attr('src'), videoSources)).not.toEqual -1
- expect($.inArray(s.attr('type').replace('video/', ''), videoTypes))
- .not.toEqual -1
-
- it 'check if click event is handled on the player', ->
- expect(@videoEl).toHandle 'click'
-
- # NOTE: According to
- #
- # https://github.com/ariya/phantomjs/wiki/Supported-Web-Standards#unsupported-features
- #
- # Video and Audio (due to the nature of PhantomJS) are not supported. After discussion
- # with William Daly, some tests are disabled (Jenkins uses phantomjs for running tests
- # and those tests fail).
- #
- # During code review, please enable the test below (change "xdescribe" to "describe"
- # to enable the test).
- xdescribe 'events:', ->
-
- beforeEach ->
- spyOn(@player, 'callStateChangeCallback').andCallThrough()
-
- describe 'click', ->
- describe 'when player is paused', ->
- beforeEach ->
- spyOn(@videoEl, 'play').andCallThrough()
- @player.playerState = STATUS.PAUSED
- $(@videoEl).trigger('click')
-
- it 'native play event was called', ->
- expect(@videoEl.play).toHaveBeenCalled()
-
- it 'player state was changed', ->
- expect(@player.playerState).toBe STATUS.PLAYING
-
- it 'callback was called', ->
- expect(@player.callStateChangeCallback).toHaveBeenCalled()
-
- describe 'when player is played', ->
-
- beforeEach ->
- spyOn(@videoEl, 'pause').andCallThrough()
- @player.playerState = STATUS.PLAYING
- $(@videoEl).trigger('click')
-
- it 'native pause event was called', ->
- expect(@videoEl.pause).toHaveBeenCalled()
-
- it 'player state was changed', ->
- expect(@player.playerState).toBe STATUS.PAUSED
-
- it 'callback was called', ->
- expect(@player.callStateChangeCallback).toHaveBeenCalled()
-
- describe 'play', ->
-
- beforeEach ->
- spyOn(@videoEl, 'play').andCallThrough()
- @player.playerState = STATUS.PAUSED
- @videoEl.play()
-
- it 'native event was called', ->
- expect(@videoEl.play).toHaveBeenCalled()
-
- it 'player state was changed', ->
- waitsFor ( ->
- @player.playerState != HTML5Video.PlayerState.PAUSED
- ), 'Player state should be changed', 1000
-
- runs ->
- expect(@player.playerState).toBe STATUS.PLAYING
-
- it 'callback was called', ->
- waitsFor ( ->
- @player.playerState != STATUS.PAUSED
- ), 'Player state should be changed', 1000
-
- runs ->
- expect(@player.callStateChangeCallback).toHaveBeenCalled()
-
- describe 'pause', ->
-
- beforeEach ->
- spyOn(@videoEl, 'pause').andCallThrough()
- @videoEl.play()
- @videoEl.pause()
-
- it 'native event was called', ->
- expect(@videoEl.pause).toHaveBeenCalled()
-
- it 'player state was changed', ->
- waitsFor ( ->
- @player.playerState != STATUS.UNSTARTED
- ), 'Player state should be changed', 1000
-
- runs ->
- expect(@player.playerState).toBe STATUS.PAUSED
-
- it 'callback was called', ->
- waitsFor ( ->
- @player.playerState != HTML5Video.PlayerState.UNSTARTED
- ), 'Player state should be changed', 1000
-
- runs ->
- expect(@player.callStateChangeCallback).toHaveBeenCalled()
-
- describe 'canplay', ->
-
- beforeEach ->
- waitsFor ( ->
- @player.playerState != STATUS.UNSTARTED
- ), 'Video cannot be played', 1000
-
- it 'player state was changed', ->
- runs ->
- expect(@player.playerState).toBe STATUS.PAUSED
-
- it 'end property was defined', ->
- runs ->
- expect(@player.end).not.toBeNull()
-
- it 'start position was defined', ->
- runs ->
- expect(@videoEl.currentTime).toBe(@player.start)
-
- it 'callback was called', ->
- runs ->
- expect(@player.config.events.onReady).toHaveBeenCalled()
-
- describe 'ended', ->
- beforeEach ->
- waitsFor ( ->
- @player.playerState != STATUS.UNSTARTED
- ), 'Video cannot be played', 1000
-
- it 'player state was changed', ->
- runs ->
- jasmine.fireEvent @videoEl, "ended"
- expect(@player.playerState).toBe STATUS.ENDED
-
- it 'callback was called', ->
- jasmine.fireEvent @videoEl, "ended"
- expect(@player.callStateChangeCallback).toHaveBeenCalled()
-
- describe 'timeupdate', ->
-
- beforeEach ->
- spyOn(@videoEl, 'pause').andCallThrough()
- waitsFor ( ->
- @player.playerState != STATUS.UNSTARTED
- ), 'Video cannot be played', 1000
-
- it 'player should be paused', ->
- runs ->
- @player.end = 3
- @videoEl.currentTime = 5
- jasmine.fireEvent @videoEl, "timeupdate"
- expect(@videoEl.pause).toHaveBeenCalled()
-
- it 'end param should be re-defined', ->
- runs ->
- @player.end = 3
- @videoEl.currentTime = 5
- jasmine.fireEvent @videoEl, "timeupdate"
- expect(@player.end).toBe @videoEl.duration
-
- # NOTE: According to
- #
- # https://github.com/ariya/phantomjs/wiki/Supported-Web-Standards#unsupported-features
- #
- # Video and Audio (due to the nature of PhantomJS) are not supported. After discussion
- # with William Daly, some tests are disabled (Jenkins uses phantomjs for running tests
- # and those tests fail).
- #
- # During code review, please enable the test below (change "xdescribe" to "describe"
- # to enable the test).
- xdescribe 'methods:', ->
-
- beforeEach ->
- waitsFor ( ->
- @volume = @videoEl.volume
- @seek = @videoEl.currentTime
- @player.playerState == STATUS.PAUSED
- ), 'Video cannot be played', 1000
-
-
- it 'pauseVideo', ->
- spyOn(@videoEl, 'pause').andCallThrough()
- @player.pauseVideo()
- expect(@videoEl.pause).toHaveBeenCalled()
-
- describe 'seekTo', ->
-
- it 'set new correct value', ->
- runs ->
- @player.seekTo(2)
- expect(@videoEl.currentTime).toBe 2
-
- it 'set new inccorrect values', ->
- runs ->
- @player.seekTo(-50)
- expect(@videoEl.currentTime).toBe @seek
- @player.seekTo('5')
- expect(@videoEl.currentTime).toBe @seek
- @player.seekTo(500000)
- expect(@videoEl.currentTime).toBe @seek
-
- describe 'setVolume', ->
-
- it 'set new correct value', ->
- runs ->
- @player.setVolume(50)
- expect(@videoEl.volume).toBe 50*0.01
-
- it 'set new inccorrect values', ->
- runs ->
- @player.setVolume(-50)
- expect(@videoEl.volume).toBe @volume
- @player.setVolume('5')
- expect(@videoEl.volume).toBe @volume
- @player.setVolume(500000)
- expect(@videoEl.volume).toBe @volume
-
- it 'getCurrentTime', ->
- runs ->
- @videoEl.currentTime = 3
- expect(@player.getCurrentTime()).toBe @videoEl.currentTime
-
- it 'playVideo', ->
- runs ->
- spyOn(@videoEl, 'play').andCallThrough()
- @player.playVideo()
- expect(@videoEl.play).toHaveBeenCalled()
-
- it 'getPlayerState', ->
- runs ->
- @player.playerState = STATUS.PLAYING
- expect(@player.getPlayerState()).toBe STATUS.PLAYING
- @player.playerState = STATUS.ENDED
- expect(@player.getPlayerState()).toBe STATUS.ENDED
-
- it 'getVolume', ->
- runs ->
- @volume = @videoEl.volume = 0.5
- expect(@player.getVolume()).toBe @volume
-
- it 'getDuration', ->
- runs ->
- @duration = @videoEl.duration
- expect(@player.getDuration()).toBe @duration
-
- describe 'setPlaybackRate', ->
- it 'set a correct value', ->
- @playbackRate = 1.5
- @player.setPlaybackRate @playbackRate
- expect(@videoEl.playbackRate).toBe @playbackRate
-
- it 'set NaN value', ->
- @playbackRate = NaN
- @player.setPlaybackRate @playbackRate
- expect(@videoEl.playbackRate).toBe 1.0
-
- it 'getAvailablePlaybackRates', ->
- expect(@player.getAvailablePlaybackRates()).toEqual playbackRates
diff --git a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/html5_video.js b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/html5_video.js
new file mode 100644
index 0000000000..7c0ec571aa
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/html5_video.js
@@ -0,0 +1,316 @@
+(function () {
+ describe('VideoAlpha HTML5Video', function () {
+ var state, player, playbackRates = [0.75, 1.0, 1.25, 1.5];
+
+ beforeEach(function () {
+ loadFixtures('videoalpha_html5.html');
+ state = new VideoAlpha('#example');
+ player = state.videoPlayer.player;
+
+ player.config.events.onReady = jasmine.createSpy('onReady');
+ });
+
+ describe('events:', function () {
+ beforeEach(function () {
+ spyOn(player, 'callStateChangeCallback').andCallThrough();
+ });
+
+ describe('click', function () {
+ describe('when player is paused', function () {
+ beforeEach(function () {
+ spyOn(player.video, 'play').andCallThrough();
+ player.playerState = STATUS.PAUSED;
+ $(player.videoEl).trigger('click');
+ });
+
+ it('native play event was called', function () {
+ expect(player.video.play).toHaveBeenCalled();
+ });
+
+ it('player state was changed', function () {
+ waitsFor(function () {
+ return player.getPlayerState() !== STATUS.PAUSED;
+ }, 'Player state should be changed', WAIT_TIMEOUT);
+
+ runs(function () {
+ expect(player.getPlayerState()).toBe(STATUS.PLAYING);
+ });
+ });
+
+ it('callback was called', function () {
+ waitsFor(function () {
+ return state.videoPlayer.player.getPlayerState() !== STATUS.PAUSED;
+ }, 'Player state should be changed', WAIT_TIMEOUT);
+
+ runs(function () {
+ expect(player.callStateChangeCallback).toHaveBeenCalled();
+ });
+ });
+ });
+ });
+
+ describe('when player is played', function () {
+ beforeEach(function () {
+ spyOn(player.video, 'pause').andCallThrough();
+ player.playerState = STATUS.PLAYING;
+ $(player.videoEl).trigger('click');
+ });
+
+ it('native event was called', function () {
+ expect(player.video.pause).toHaveBeenCalled();
+ });
+
+ it('player state was changed', function () {
+ waitsFor(function () {
+ return player.getPlayerState() !== STATUS.PLAYING;
+ }, 'Player state should be changed', WAIT_TIMEOUT);
+
+ runs(function () {
+ expect(player.getPlayerState()).toBe(STATUS.PAUSED);
+ });
+ });
+
+ it('callback was called', function () {
+ waitsFor(function () {
+ return player.getPlayerState() !== STATUS.PLAYING;
+ }, 'Player state should be changed', WAIT_TIMEOUT);
+
+ runs(function () {
+ expect(player.callStateChangeCallback).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('play', function () {
+ beforeEach(function () {
+ spyOn(player.video, 'play').andCallThrough();
+ player.playerState = STATUS.PAUSED;
+ player.playVideo();
+ });
+
+ it('native event was called', function () {
+ expect(player.video.play).toHaveBeenCalled();
+ });
+
+ it('player state was changed', function () {
+ waitsFor(function () {
+ return player.getPlayerState() !== STATUS.PAUSED;
+ }, 'Player state should be changed', WAIT_TIMEOUT);
+
+ runs(function () {
+ expect(player.getPlayerState()).toBe(STATUS.PLAYING);
+ });
+ });
+
+ it('callback was called', function () {
+ waitsFor(function () {
+ return player.getPlayerState() !== STATUS.PAUSED;
+ }, 'Player state should be changed', WAIT_TIMEOUT);
+
+ runs(function () {
+ expect(player.callStateChangeCallback).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('pause', function () {
+ beforeEach(function () {
+ spyOn(player.video, 'pause').andCallThrough();
+ player.playVideo();
+ player.pauseVideo();
+ });
+
+ it('native event was called', function () {
+ expect(player.video.pause).toHaveBeenCalled();
+ });
+
+ it('player state was changed', function () {
+ waitsFor(function () {
+ return player.getPlayerState() !== STATUS.UNSTARTED;
+ }, 'Player state should be changed', WAIT_TIMEOUT);
+
+ runs(function () {
+ expect(player.getPlayerState()).toBe(STATUS.PAUSED);
+ });
+ });
+
+ it('callback was called', function () {
+ waitsFor(function () {
+ return player.getPlayerState() !== STATUS.UNSTARTED;
+ }, 'Player state should be changed', WAIT_TIMEOUT);
+ runs(function () {
+ expect(player.callStateChangeCallback).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('canplay', function () {
+ beforeEach(function () {
+ waitsFor(function () {
+ return player.getPlayerState() !== STATUS.UNSTARTED;
+ }, 'Video cannot be played', WAIT_TIMEOUT);
+ });
+
+ it('player state was changed', function () {
+ runs(function () {
+ expect(player.getPlayerState()).toBe(STATUS.PAUSED);
+ });
+ });
+
+ it('end property was defined', function () {
+ runs(function () {
+ expect(player.end).not.toBeNull();
+ });
+ });
+
+ it('start position was defined', function () {
+ runs(function () {
+ expect(player.video.currentTime).toBe(player.start);
+ });
+ });
+
+ it('onReady callback was called', function () {
+ runs(function () {
+ expect(player.config.events.onReady).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('ended', function () {
+ beforeEach(function () {
+ waitsFor(function () {
+ return player.getPlayerState() !== STATUS.UNSTARTED;
+ }, 'Video cannot be played', WAIT_TIMEOUT);
+ });
+
+ it('player state was changed', function () {
+ runs(function () {
+ jasmine.fireEvent(player.video, 'ended');
+ expect(player.getPlayerState()).toBe(STATUS.ENDED);
+ });
+ });
+
+ it('callback was called', function () {
+ jasmine.fireEvent(player.video, 'ended');
+ expect(player.callStateChangeCallback).toHaveBeenCalled();
+ });
+ });
+ }); // End-of: describe('events:', function () {
+
+ describe('methods', function () {
+ var volume, seek, duration, playbackRate;
+
+ beforeEach(function () {
+ waitsFor(function () {
+ volume = player.video.volume;
+ seek = player.video.currentTime;
+ return player.playerState === STATUS.PAUSED;
+ }, 'Video cannot be played', WAIT_TIMEOUT);
+ });
+
+ it('pauseVideo', function () {
+ runs(function () {
+ spyOn(player.video, 'pause').andCallThrough();
+ player.pauseVideo();
+ expect(player.video.pause).toHaveBeenCalled();
+ });
+ });
+
+ describe('seekTo', function () {
+ it('set new correct value', function () {
+ runs(function () {
+ player.seekTo(2);
+ expect(player.getCurrentTime()).toBe(2);
+ });
+ });
+
+ it('set new inccorrect values', function () {
+ runs(function () {
+ player.seekTo(-50);
+ expect(player.getCurrentTime()).toBe(seek);
+ player.seekTo('5');
+ expect(player.getCurrentTime()).toBe(seek);
+ player.seekTo(500000);
+ expect(player.getCurrentTime()).toBe(seek);
+ });
+ });
+ });
+
+ describe('setVolume', function () {
+ it('set new correct value', function () {
+ runs(function () {
+ player.setVolume(50);
+ expect(player.getVolume()).toBe(50 * 0.01);
+ });
+ });
+
+ it('set new incorrect values', function () {
+ runs(function () {
+ player.setVolume(-50);
+ expect(player.getVolume()).toBe(volume);
+ player.setVolume('5');
+ expect(player.getVolume()).toBe(volume);
+ player.setVolume(500000);
+ expect(player.getVolume()).toBe(volume);
+ });
+ });
+ });
+
+ it('getCurrentTime', function () {
+ runs(function () {
+ player.video.currentTime = 3;
+ expect(player.getCurrentTime()).toBe(player.video.currentTime);
+ });
+ });
+
+ it('playVideo', function () {
+ runs(function () {
+ spyOn(player.video, 'play').andCallThrough();
+ player.playVideo();
+ expect(player.video.play).toHaveBeenCalled();
+ });
+ });
+
+ it('getPlayerState', function () {
+ runs(function () {
+ player.playerState = STATUS.PLAYING;
+ expect(player.getPlayerState()).toBe(STATUS.PLAYING);
+ player.playerState = STATUS.ENDED;
+ expect(player.getPlayerState()).toBe(STATUS.ENDED);
+ });
+ });
+
+ it('getVolume', function () {
+ runs(function () {
+ volume = player.video.volume = 0.5;
+ expect(player.getVolume()).toBe(volume);
+ });
+ });
+
+ it('getDuration', function () {
+ runs(function () {
+ duration = player.video.duration;
+ expect(player.getDuration()).toBe(duration);
+ });
+ });
+
+ describe('setPlaybackRate', function () {
+ it('set a correct value', function () {
+ playbackRate = 1.5;
+ player.setPlaybackRate(playbackRate);
+ expect(player.video.playbackRate).toBe(playbackRate);
+ });
+
+ it('set NaN value', function () {
+ playbackRate = NaN;
+ player.setPlaybackRate(playbackRate);
+ expect(player.video.playbackRate).toBe(1.0);
+ });
+ });
+
+ it('getAvailablePlaybackRates', function () {
+ expect(player.getAvailablePlaybackRates()).toEqual(playbackRates);
+ });
+ }); // End-of: describe('methods', function () {
+ });
+}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_caption_spec.coffee b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_caption_spec.coffee
deleted file mode 100644
index 4bd237b81d..0000000000
--- a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_caption_spec.coffee
+++ /dev/null
@@ -1,373 +0,0 @@
-describe 'VideoCaptionAlpha', ->
-
- beforeEach ->
- spyOn(VideoCaptionAlpha.prototype, 'fetchCaption').andCallThrough()
- spyOn($, 'ajaxWithPrefix').andCallThrough()
- window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn false
-
- afterEach ->
- YT.Player = undefined
- $.fn.scrollTo.reset()
- $('.subtitles').remove()
-
- describe 'constructor', ->
-
- describe 'always', ->
-
- beforeEach ->
- @player = jasmine.stubVideoPlayerAlpha @
- @caption = @player.caption
-
- it 'set the youtube id', ->
- expect(@caption.youtubeId).toEqual 'normalSpeedYoutubeId'
-
- it 'create the caption element', ->
- expect($('.video')).toContain 'ol.subtitles'
-
- it 'add caption control to video player', ->
- expect($('.video')).toContain 'a.hide-subtitles'
-
- it 'fetch the caption', ->
- expect(@caption.loaded).toBeTruthy()
- expect(@caption.fetchCaption).toHaveBeenCalled()
- expect($.ajaxWithPrefix).toHaveBeenCalledWith
- url: @caption.captionURL()
- notifyOnError: false
- success: jasmine.any(Function)
-
- it 'bind window resize event', ->
- expect($(window)).toHandleWith 'resize', @caption.resize
-
- it 'bind the hide caption button', ->
- expect($('.hide-subtitles')).toHandleWith 'click', @caption.toggle
-
- it 'bind the mouse movement', ->
- expect($('.subtitles')).toHandleWith 'mouseover', @caption.onMouseEnter
- expect($('.subtitles')).toHandleWith 'mouseout', @caption.onMouseLeave
- expect($('.subtitles')).toHandleWith 'mousemove', @caption.onMovement
- expect($('.subtitles')).toHandleWith 'mousewheel', @caption.onMovement
- expect($('.subtitles')).toHandleWith 'DOMMouseScroll', @caption.onMovement
-
- describe 'when on a non touch-based device', ->
-
- beforeEach ->
- @player = jasmine.stubVideoPlayerAlpha @
- @caption = @player.caption
-
- it 'render the caption', ->
- captionsData = jasmine.stubbedCaption
- $('.subtitles li[data-index]').each (index, link) =>
- expect($(link)).toHaveData 'index', index
- expect($(link)).toHaveData 'start', captionsData.start[index]
- expect($(link)).toHaveText captionsData.text[index]
-
- it 'add a padding element to caption', ->
- expect($('.subtitles li:first')).toBe '.spacing'
- expect($('.subtitles li:last')).toBe '.spacing'
-
- it 'bind all the caption link', ->
- $('.subtitles li[data-index]').each (index, link) =>
- expect($(link)).toHandleWith 'click', @caption.seekPlayer
-
- it 'set rendered to true', ->
- expect(@caption.rendered).toBeTruthy()
-
- describe 'when on a touch-based device', ->
-
- beforeEach ->
- window.onTouchBasedDevice.andReturn true
- @player = jasmine.stubVideoPlayerAlpha @
- @caption = @player.caption
-
- it 'show explaination message', ->
- expect($('.subtitles li')).toHaveHtml "Caption will be displayed when you start playing the video."
-
- it 'does not set rendered to true', ->
- expect(@caption.rendered).toBeFalsy()
-
- describe 'mouse movement', ->
-
- beforeEach ->
- @player = jasmine.stubVideoPlayerAlpha @
- @caption = @player.caption
- window.setTimeout.andReturn(100)
- spyOn window, 'clearTimeout'
-
- describe 'when cursor is outside of the caption box', ->
-
- beforeEach ->
- $(window).trigger jQuery.Event 'mousemove'
-
- it 'does not set freezing timeout', ->
- expect(@caption.frozen).toBeFalsy()
-
- describe 'when cursor is in the caption box', ->
-
- beforeEach ->
- $('.subtitles').trigger jQuery.Event 'mouseenter'
-
- it 'set the freezing timeout', ->
- expect(@caption.frozen).toEqual 100
-
- describe 'when the cursor is moving', ->
- beforeEach ->
- $('.subtitles').trigger jQuery.Event 'mousemove'
-
- it 'reset the freezing timeout', ->
- expect(window.clearTimeout).toHaveBeenCalledWith 100
-
- describe 'when the mouse is scrolling', ->
- beforeEach ->
- $('.subtitles').trigger jQuery.Event 'mousewheel'
-
- it 'reset the freezing timeout', ->
- expect(window.clearTimeout).toHaveBeenCalledWith 100
-
- describe 'when cursor is moving out of the caption box', ->
- beforeEach ->
- @caption.frozen = 100
- $.fn.scrollTo.reset()
-
- describe 'always', ->
- beforeEach ->
- $('.subtitles').trigger jQuery.Event 'mouseout'
-
- it 'reset the freezing timeout', ->
- expect(window.clearTimeout).toHaveBeenCalledWith 100
-
- it 'unfreeze the caption', ->
- expect(@caption.frozen).toBeNull()
-
- describe 'when the player is playing', ->
- beforeEach ->
- @caption.playing = true
- $('.subtitles li[data-index]:first').addClass 'current'
- $('.subtitles').trigger jQuery.Event 'mouseout'
-
- it 'scroll the caption', ->
- expect($.fn.scrollTo).toHaveBeenCalled()
-
- describe 'when the player is not playing', ->
- beforeEach ->
- @caption.playing = false
- $('.subtitles').trigger jQuery.Event 'mouseout'
-
- it 'does not scroll the caption', ->
- expect($.fn.scrollTo).not.toHaveBeenCalled()
-
- describe 'search', ->
-
- beforeEach ->
- @player = jasmine.stubVideoPlayerAlpha @
- @caption = @player.caption
-
- it 'return a correct caption index', ->
- expect(@caption.search(0)).toEqual 0
- expect(@caption.search(9999)).toEqual 0
- expect(@caption.search(10000)).toEqual 1
- expect(@caption.search(15000)).toEqual 1
- expect(@caption.search(30000)).toEqual 3
- expect(@caption.search(30001)).toEqual 3
-
- describe 'play', ->
- describe 'when the caption was not rendered', ->
- beforeEach ->
- window.onTouchBasedDevice.andReturn true
- @player = jasmine.stubVideoPlayerAlpha @
- @caption = @player.caption
- @caption.play()
-
- it 'render the caption', ->
- captionsData = jasmine.stubbedCaption
- $('.subtitles li[data-index]').each (index, link) =>
- expect($(link)).toHaveData 'index', index
- expect($(link)).toHaveData 'start', captionsData.start[index]
- expect($(link)).toHaveText captionsData.text[index]
-
- it 'add a padding element to caption', ->
- expect($('.subtitles li:first')).toBe '.spacing'
- expect($('.subtitles li:last')).toBe '.spacing'
-
- it 'bind all the caption link', ->
- $('.subtitles li[data-index]').each (index, link) =>
- expect($(link)).toHandleWith 'click', @caption.seekPlayer
-
- it 'set rendered to true', ->
- expect(@caption.rendered).toBeTruthy()
-
- it 'set playing to true', ->
- expect(@caption.playing).toBeTruthy()
-
- describe 'pause', ->
- beforeEach ->
- @player = jasmine.stubVideoPlayerAlpha @
- @caption = @player.caption
- @caption.playing = true
- @caption.pause()
-
- it 'set playing to false', ->
- expect(@caption.playing).toBeFalsy()
-
- describe 'updatePlayTime', ->
-
- beforeEach ->
- @player = jasmine.stubVideoPlayerAlpha @
- @caption = @player.caption
-
- describe 'when the video speed is 1.0x', ->
- beforeEach ->
- @caption.currentSpeed = '1.0'
- @caption.updatePlayTime 25.000
-
- it 'search the caption based on time', ->
- expect(@caption.currentIndex).toEqual 2
-
- describe 'when the video speed is not 1.0x', ->
- beforeEach ->
- @caption.currentSpeed = '0.75'
- @caption.updatePlayTime 25.000
-
- it 'search the caption based on 1.0x speed', ->
- expect(@caption.currentIndex).toEqual 1
-
- describe 'when the index is not the same', ->
- beforeEach ->
- @caption.currentIndex = 1
- $('.subtitles li[data-index=1]').addClass 'current'
- @caption.updatePlayTime 25.000
-
- it 'deactivate the previous caption', ->
- expect($('.subtitles li[data-index=1]')).not.toHaveClass 'current'
-
- it 'activate new caption', ->
- expect($('.subtitles li[data-index=2]')).toHaveClass 'current'
-
- it 'save new index', ->
- expect(@caption.currentIndex).toEqual 2
-
- it 'scroll caption to new position', ->
- expect($.fn.scrollTo).toHaveBeenCalled()
-
- describe 'when the index is the same', ->
- beforeEach ->
- @caption.currentIndex = 1
- $('.subtitles li[data-index=1]').addClass 'current'
- @caption.updatePlayTime 15.000
-
- it 'does not change current subtitle', ->
- expect($('.subtitles li[data-index=1]')).toHaveClass 'current'
-
- describe 'resize', ->
-
- beforeEach ->
- @player = jasmine.stubVideoPlayerAlpha @
- @caption = @player.caption
- $('.subtitles li[data-index=1]').addClass 'current'
- @caption.resize()
-
- it 'set the height of caption container', ->
- expect(parseInt($('.subtitles').css('maxHeight'))).toBeCloseTo $('.video-wrapper').height(), 2
-
- it 'set the height of caption spacing', ->
- firstSpacing = Math.abs(parseInt($('.subtitles .spacing:first').css('height')))
- lastSpacing = Math.abs(parseInt($('.subtitles .spacing:last').css('height')))
-
- expect(firstSpacing - @caption.topSpacingHeight()).toBeLessThan 1
- expect(lastSpacing - @caption.bottomSpacingHeight()).toBeLessThan 1
-
- it 'scroll caption to new position', ->
- expect($.fn.scrollTo).toHaveBeenCalled()
-
- describe 'scrollCaption', ->
-
- beforeEach ->
- @player = jasmine.stubVideoPlayerAlpha @
- @caption = @player.caption
-
- describe 'when frozen', ->
- beforeEach ->
- @caption.frozen = true
- $('.subtitles li[data-index=1]').addClass 'current'
- @caption.scrollCaption()
-
- it 'does not scroll the caption', ->
- expect($.fn.scrollTo).not.toHaveBeenCalled()
-
- describe 'when not frozen', ->
- beforeEach ->
- @caption.frozen = false
-
- describe 'when there is no current caption', ->
- beforeEach ->
- @caption.scrollCaption()
-
- it 'does not scroll the caption', ->
- expect($.fn.scrollTo).not.toHaveBeenCalled()
-
- describe 'when there is a current caption', ->
- beforeEach ->
- $('.subtitles li[data-index=1]').addClass 'current'
- @caption.scrollCaption()
-
- it 'scroll to current caption', ->
- offset = -0.5 * ($('.video-wrapper').height() - $('.subtitles .current:first').height())
-
- expect($.fn.scrollTo).toHaveBeenCalledWith $('.subtitles .current:first', @caption.el),
- offset: offset
-
- describe 'seekPlayer', ->
-
- beforeEach ->
- @player = jasmine.stubVideoPlayerAlpha @
- @caption = @player.caption
- $(@caption).bind 'seek', (event, time) => @time = time
-
- describe 'when the video speed is 1.0x', ->
- beforeEach ->
- @caption.currentSpeed = '1.0'
- $('.subtitles li[data-start="30000"]').trigger('click')
-
- it 'trigger seek event with the correct time', ->
- expect(@player.currentTime).toEqual 30.000
-
- describe 'when the video speed is not 1.0x', ->
- beforeEach ->
- @caption.currentSpeed = '0.75'
- $('.subtitles li[data-start="30000"]').trigger('click')
-
- it 'trigger seek event with the correct time', ->
- expect(@player.currentTime).toEqual 40.000
-
- describe 'toggle', ->
- beforeEach ->
- @player = jasmine.stubVideoPlayerAlpha @
- spyOn @video, 'log'
- @caption = @player.caption
- $('.subtitles li[data-index=1]').addClass 'current'
-
- describe 'when the caption is visible', ->
- beforeEach ->
- @caption.el.removeClass 'closed'
- @caption.toggle jQuery.Event('click')
-
- it 'log the hide_transcript event', ->
- expect(@video.log).toHaveBeenCalledWith 'hide_transcript',
- currentTime: @player.currentTime
-
- it 'hide the caption', ->
- expect(@caption.el).toHaveClass 'closed'
-
- describe 'when the caption is hidden', ->
- beforeEach ->
- @caption.el.addClass 'closed'
- @caption.toggle jQuery.Event('click')
-
- it 'log the show_transcript event', ->
- expect(@video.log).toHaveBeenCalledWith 'show_transcript',
- currentTime: @player.currentTime
-
- it 'show the caption', ->
- expect(@caption.el).not.toHaveClass 'closed'
-
- it 'scroll the caption', ->
- expect($.fn.scrollTo).toHaveBeenCalled()
diff --git a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_caption_spec.js b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_caption_spec.js
new file mode 100644
index 0000000000..2263868483
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_caption_spec.js
@@ -0,0 +1,417 @@
+// Generated by CoffeeScript 1.6.3
+(function() {
+ xdescribe('VideoCaptionAlpha', function() {
+ beforeEach(function() {
+ spyOn(VideoCaptionAlpha.prototype, 'fetchCaption').andCallThrough();
+ spyOn($, 'ajaxWithPrefix').andCallThrough();
+ return window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn(false);
+ });
+ afterEach(function() {
+ YT.Player = void 0;
+ $.fn.scrollTo.reset();
+ return $('.subtitles').remove();
+ });
+ describe('constructor', function() {
+ describe('always', function() {
+ beforeEach(function() {
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ return this.caption = this.player.caption;
+ });
+ it('set the youtube id', function() {
+ return expect(this.caption.youtubeId).toEqual('normalSpeedYoutubeId');
+ });
+ it('create the caption element', function() {
+ return expect($('.video')).toContain('ol.subtitles');
+ });
+ it('add caption control to video player', function() {
+ return expect($('.video')).toContain('a.hide-subtitles');
+ });
+ it('fetch the caption', function() {
+ expect(this.caption.loaded).toBeTruthy();
+ expect(this.caption.fetchCaption).toHaveBeenCalled();
+ return expect($.ajaxWithPrefix).toHaveBeenCalledWith({
+ url: this.caption.captionURL(),
+ notifyOnError: false,
+ success: jasmine.any(Function)
+ });
+ });
+ it('bind window resize event', function() {
+ return expect($(window)).toHandleWith('resize', this.caption.resize);
+ });
+ it('bind the hide caption button', function() {
+ return expect($('.hide-subtitles')).toHandleWith('click', this.caption.toggle);
+ });
+ return it('bind the mouse movement', function() {
+ expect($('.subtitles')).toHandleWith('mouseover', this.caption.onMouseEnter);
+ expect($('.subtitles')).toHandleWith('mouseout', this.caption.onMouseLeave);
+ expect($('.subtitles')).toHandleWith('mousemove', this.caption.onMovement);
+ expect($('.subtitles')).toHandleWith('mousewheel', this.caption.onMovement);
+ return expect($('.subtitles')).toHandleWith('DOMMouseScroll', this.caption.onMovement);
+ });
+ });
+ describe('when on a non touch-based device', function() {
+ beforeEach(function() {
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ return this.caption = this.player.caption;
+ });
+ it('render the caption', function() {
+ var captionsData,
+ _this = this;
+ captionsData = jasmine.stubbedCaption;
+ return $('.subtitles li[data-index]').each(function(index, link) {
+ expect($(link)).toHaveData('index', index);
+ expect($(link)).toHaveData('start', captionsData.start[index]);
+ return expect($(link)).toHaveText(captionsData.text[index]);
+ });
+ });
+ it('add a padding element to caption', function() {
+ expect($('.subtitles li:first')).toBe('.spacing');
+ return expect($('.subtitles li:last')).toBe('.spacing');
+ });
+ it('bind all the caption link', function() {
+ var _this = this;
+ return $('.subtitles li[data-index]').each(function(index, link) {
+ return expect($(link)).toHandleWith('click', _this.caption.seekPlayer);
+ });
+ });
+ return it('set rendered to true', function() {
+ return expect(this.caption.rendered).toBeTruthy();
+ });
+ });
+ return describe('when on a touch-based device', function() {
+ beforeEach(function() {
+ window.onTouchBasedDevice.andReturn(true);
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ return this.caption = this.player.caption;
+ });
+ it('show explaination message', function() {
+ return expect($('.subtitles li')).toHaveHtml("Caption will be displayed when you start playing the video.");
+ });
+ return it('does not set rendered to true', function() {
+ return expect(this.caption.rendered).toBeFalsy();
+ });
+ });
+ });
+ describe('mouse movement', function() {
+ beforeEach(function() {
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ this.caption = this.player.caption;
+ window.setTimeout.andReturn(100);
+ return spyOn(window, 'clearTimeout');
+ });
+ describe('when cursor is outside of the caption box', function() {
+ beforeEach(function() {
+ return $(window).trigger(jQuery.Event('mousemove'));
+ });
+ return it('does not set freezing timeout', function() {
+ return expect(this.caption.frozen).toBeFalsy();
+ });
+ });
+ describe('when cursor is in the caption box', function() {
+ beforeEach(function() {
+ return $('.subtitles').trigger(jQuery.Event('mouseenter'));
+ });
+ it('set the freezing timeout', function() {
+ return expect(this.caption.frozen).toEqual(100);
+ });
+ describe('when the cursor is moving', function() {
+ beforeEach(function() {
+ return $('.subtitles').trigger(jQuery.Event('mousemove'));
+ });
+ return it('reset the freezing timeout', function() {
+ return expect(window.clearTimeout).toHaveBeenCalledWith(100);
+ });
+ });
+ return describe('when the mouse is scrolling', function() {
+ beforeEach(function() {
+ return $('.subtitles').trigger(jQuery.Event('mousewheel'));
+ });
+ return it('reset the freezing timeout', function() {
+ return expect(window.clearTimeout).toHaveBeenCalledWith(100);
+ });
+ });
+ });
+ return describe('when cursor is moving out of the caption box', function() {
+ beforeEach(function() {
+ this.caption.frozen = 100;
+ return $.fn.scrollTo.reset();
+ });
+ describe('always', function() {
+ beforeEach(function() {
+ return $('.subtitles').trigger(jQuery.Event('mouseout'));
+ });
+ it('reset the freezing timeout', function() {
+ return expect(window.clearTimeout).toHaveBeenCalledWith(100);
+ });
+ return it('unfreeze the caption', function() {
+ return expect(this.caption.frozen).toBeNull();
+ });
+ });
+ describe('when the player is playing', function() {
+ beforeEach(function() {
+ this.caption.playing = true;
+ $('.subtitles li[data-index]:first').addClass('current');
+ return $('.subtitles').trigger(jQuery.Event('mouseout'));
+ });
+ return it('scroll the caption', function() {
+ return expect($.fn.scrollTo).toHaveBeenCalled();
+ });
+ });
+ return describe('when the player is not playing', function() {
+ beforeEach(function() {
+ this.caption.playing = false;
+ return $('.subtitles').trigger(jQuery.Event('mouseout'));
+ });
+ return it('does not scroll the caption', function() {
+ return expect($.fn.scrollTo).not.toHaveBeenCalled();
+ });
+ });
+ });
+ });
+ describe('search', function() {
+ beforeEach(function() {
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ return this.caption = this.player.caption;
+ });
+ return it('return a correct caption index', function() {
+ expect(this.caption.search(0)).toEqual(0);
+ expect(this.caption.search(9999)).toEqual(0);
+ expect(this.caption.search(10000)).toEqual(1);
+ expect(this.caption.search(15000)).toEqual(1);
+ expect(this.caption.search(30000)).toEqual(3);
+ return expect(this.caption.search(30001)).toEqual(3);
+ });
+ });
+ describe('play', function() {
+ return describe('when the caption was not rendered', function() {
+ beforeEach(function() {
+ window.onTouchBasedDevice.andReturn(true);
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ this.caption = this.player.caption;
+ return this.caption.play();
+ });
+ it('render the caption', function() {
+ var captionsData,
+ _this = this;
+ captionsData = jasmine.stubbedCaption;
+ return $('.subtitles li[data-index]').each(function(index, link) {
+ expect($(link)).toHaveData('index', index);
+ expect($(link)).toHaveData('start', captionsData.start[index]);
+ return expect($(link)).toHaveText(captionsData.text[index]);
+ });
+ });
+ it('add a padding element to caption', function() {
+ expect($('.subtitles li:first')).toBe('.spacing');
+ return expect($('.subtitles li:last')).toBe('.spacing');
+ });
+ it('bind all the caption link', function() {
+ var _this = this;
+ return $('.subtitles li[data-index]').each(function(index, link) {
+ return expect($(link)).toHandleWith('click', _this.caption.seekPlayer);
+ });
+ });
+ it('set rendered to true', function() {
+ return expect(this.caption.rendered).toBeTruthy();
+ });
+ return it('set playing to true', function() {
+ return expect(this.caption.playing).toBeTruthy();
+ });
+ });
+ });
+ describe('pause', function() {
+ beforeEach(function() {
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ this.caption = this.player.caption;
+ this.caption.playing = true;
+ return this.caption.pause();
+ });
+ return it('set playing to false', function() {
+ return expect(this.caption.playing).toBeFalsy();
+ });
+ });
+ describe('updatePlayTime', function() {
+ beforeEach(function() {
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ return this.caption = this.player.caption;
+ });
+ describe('when the video speed is 1.0x', function() {
+ beforeEach(function() {
+ this.caption.currentSpeed = '1.0';
+ return this.caption.updatePlayTime(25.000);
+ });
+ return it('search the caption based on time', function() {
+ return expect(this.caption.currentIndex).toEqual(2);
+ });
+ });
+ describe('when the video speed is not 1.0x', function() {
+ beforeEach(function() {
+ this.caption.currentSpeed = '0.75';
+ return this.caption.updatePlayTime(25.000);
+ });
+ return it('search the caption based on 1.0x speed', function() {
+ return expect(this.caption.currentIndex).toEqual(1);
+ });
+ });
+ describe('when the index is not the same', function() {
+ beforeEach(function() {
+ this.caption.currentIndex = 1;
+ $('.subtitles li[data-index=1]').addClass('current');
+ return this.caption.updatePlayTime(25.000);
+ });
+ it('deactivate the previous caption', function() {
+ return expect($('.subtitles li[data-index=1]')).not.toHaveClass('current');
+ });
+ it('activate new caption', function() {
+ return expect($('.subtitles li[data-index=2]')).toHaveClass('current');
+ });
+ it('save new index', function() {
+ return expect(this.caption.currentIndex).toEqual(2);
+ });
+ return it('scroll caption to new position', function() {
+ return expect($.fn.scrollTo).toHaveBeenCalled();
+ });
+ });
+ return describe('when the index is the same', function() {
+ beforeEach(function() {
+ this.caption.currentIndex = 1;
+ $('.subtitles li[data-index=1]').addClass('current');
+ return this.caption.updatePlayTime(15.000);
+ });
+ return it('does not change current subtitle', function() {
+ return expect($('.subtitles li[data-index=1]')).toHaveClass('current');
+ });
+ });
+ });
+ describe('resize', function() {
+ beforeEach(function() {
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ this.caption = this.player.caption;
+ $('.subtitles li[data-index=1]').addClass('current');
+ return this.caption.resize();
+ });
+ it('set the height of caption container', function() {
+ return expect(parseInt($('.subtitles').css('maxHeight'))).toBeCloseTo($('.video-wrapper').height(), 2);
+ });
+ it('set the height of caption spacing', function() {
+ var firstSpacing, lastSpacing;
+ firstSpacing = Math.abs(parseInt($('.subtitles .spacing:first').css('height')));
+ lastSpacing = Math.abs(parseInt($('.subtitles .spacing:last').css('height')));
+ expect(firstSpacing - this.caption.topSpacingHeight()).toBeLessThan(1);
+ return expect(lastSpacing - this.caption.bottomSpacingHeight()).toBeLessThan(1);
+ });
+ return it('scroll caption to new position', function() {
+ return expect($.fn.scrollTo).toHaveBeenCalled();
+ });
+ });
+ describe('scrollCaption', function() {
+ beforeEach(function() {
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ return this.caption = this.player.caption;
+ });
+ describe('when frozen', function() {
+ beforeEach(function() {
+ this.caption.frozen = true;
+ $('.subtitles li[data-index=1]').addClass('current');
+ return this.caption.scrollCaption();
+ });
+ return it('does not scroll the caption', function() {
+ return expect($.fn.scrollTo).not.toHaveBeenCalled();
+ });
+ });
+ return describe('when not frozen', function() {
+ beforeEach(function() {
+ return this.caption.frozen = false;
+ });
+ describe('when there is no current caption', function() {
+ beforeEach(function() {
+ return this.caption.scrollCaption();
+ });
+ return it('does not scroll the caption', function() {
+ return expect($.fn.scrollTo).not.toHaveBeenCalled();
+ });
+ });
+ return describe('when there is a current caption', function() {
+ beforeEach(function() {
+ $('.subtitles li[data-index=1]').addClass('current');
+ return this.caption.scrollCaption();
+ });
+ return it('scroll to current caption', function() {
+ var offset;
+ offset = -0.5 * ($('.video-wrapper').height() - $('.subtitles .current:first').height());
+ return expect($.fn.scrollTo).toHaveBeenCalledWith($('.subtitles .current:first', this.caption.el), {
+ offset: offset
+ });
+ });
+ });
+ });
+ });
+ describe('seekPlayer', function() {
+ beforeEach(function() {
+ var _this = this;
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ this.caption = this.player.caption;
+ return $(this.caption).bind('seek', function(event, time) {
+ return _this.time = time;
+ });
+ });
+ describe('when the video speed is 1.0x', function() {
+ beforeEach(function() {
+ this.caption.currentSpeed = '1.0';
+ return $('.subtitles li[data-start="30000"]').trigger('click');
+ });
+ return it('trigger seek event with the correct time', function() {
+ return expect(this.player.currentTime).toEqual(30.000);
+ });
+ });
+ return describe('when the video speed is not 1.0x', function() {
+ beforeEach(function() {
+ this.caption.currentSpeed = '0.75';
+ return $('.subtitles li[data-start="30000"]').trigger('click');
+ });
+ return it('trigger seek event with the correct time', function() {
+ return expect(this.player.currentTime).toEqual(40.000);
+ });
+ });
+ });
+ return describe('toggle', function() {
+ beforeEach(function() {
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ spyOn(this.video, 'log');
+ this.caption = this.player.caption;
+ return $('.subtitles li[data-index=1]').addClass('current');
+ });
+ describe('when the caption is visible', function() {
+ beforeEach(function() {
+ this.caption.el.removeClass('closed');
+ return this.caption.toggle(jQuery.Event('click'));
+ });
+ it('log the hide_transcript event', function() {
+ return expect(this.video.log).toHaveBeenCalledWith('hide_transcript', {
+ currentTime: this.player.currentTime
+ });
+ });
+ return it('hide the caption', function() {
+ return expect(this.caption.el).toHaveClass('closed');
+ });
+ });
+ return describe('when the caption is hidden', function() {
+ beforeEach(function() {
+ this.caption.el.addClass('closed');
+ return this.caption.toggle(jQuery.Event('click'));
+ });
+ it('log the show_transcript event', function() {
+ return expect(this.video.log).toHaveBeenCalledWith('show_transcript', {
+ currentTime: this.player.currentTime
+ });
+ });
+ it('show the caption', function() {
+ return expect(this.caption.el).not.toHaveClass('closed');
+ });
+ return it('scroll the caption', function() {
+ return expect($.fn.scrollTo).toHaveBeenCalled();
+ });
+ });
+ });
+ });
+
+}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_control_spec.coffee b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_control_spec.coffee
deleted file mode 100644
index a4dc8739d8..0000000000
--- a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_control_spec.coffee
+++ /dev/null
@@ -1,103 +0,0 @@
-describe 'VideoControlAlpha', ->
- beforeEach ->
- window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn false
- loadFixtures 'videoalpha.html'
- $('.video-controls').html ''
-
- describe 'constructor', ->
-
- it 'render the video controls', ->
- @control = new window.VideoControlAlpha(el: $('.video-controls'))
- expect($('.video-controls')).toContain
- ['.slider', 'ul.vcr', 'a.play', '.vidtime', '.add-fullscreen'].join(',')
- expect($('.video-controls').find('.vidtime')).toHaveText '0:00 / 0:00'
-
- it 'bind the playback button', ->
- @control = new window.VideoControlAlpha(el: $('.video-controls'))
- expect($('.video_control')).toHandleWith 'click', @control.togglePlayback
-
- describe 'when on a touch based device', ->
- beforeEach ->
- window.onTouchBasedDevice.andReturn true
- @control = new window.VideoControlAlpha(el: $('.video-controls'))
-
- it 'does not add the play class to video control', ->
- expect($('.video_control')).not.toHaveClass 'play'
- expect($('.video_control')).not.toHaveHtml 'Play'
-
-
- describe 'when on a non-touch based device', ->
-
- beforeEach ->
- @control = new window.VideoControlAlpha(el: $('.video-controls'))
-
- it 'add the play class to video control', ->
- expect($('.video_control')).toHaveClass 'play'
- expect($('.video_control')).toHaveHtml 'Play'
-
- describe 'play', ->
-
- beforeEach ->
- @control = new window.VideoControlAlpha(el: $('.video-controls'))
- @control.play()
-
- it 'switch playback button to play state', ->
- expect($('.video_control')).not.toHaveClass 'play'
- expect($('.video_control')).toHaveClass 'pause'
- expect($('.video_control')).toHaveHtml 'Pause'
-
- describe 'pause', ->
-
- beforeEach ->
- @control = new window.VideoControlAlpha(el: $('.video-controls'))
- @control.pause()
-
- it 'switch playback button to pause state', ->
- expect($('.video_control')).not.toHaveClass 'pause'
- expect($('.video_control')).toHaveClass 'play'
- expect($('.video_control')).toHaveHtml 'Play'
-
- describe 'togglePlayback', ->
-
- beforeEach ->
- @control = new window.VideoControlAlpha(el: $('.video-controls'))
-
- describe 'when the control does not have play or pause class', ->
- beforeEach ->
- $('.video_control').removeClass('play').removeClass('pause')
-
- describe 'when the video is playing', ->
- beforeEach ->
- $('.video_control').addClass('play')
- spyOnEvent @control, 'pause'
- @control.togglePlayback jQuery.Event('click')
-
- it 'does not trigger the pause event', ->
- expect('pause').not.toHaveBeenTriggeredOn @control
-
- describe 'when the video is paused', ->
- beforeEach ->
- $('.video_control').addClass('pause')
- spyOnEvent @control, 'play'
- @control.togglePlayback jQuery.Event('click')
-
- it 'does not trigger the play event', ->
- expect('play').not.toHaveBeenTriggeredOn @control
-
- describe 'when the video is playing', ->
- beforeEach ->
- spyOnEvent @control, 'pause'
- $('.video_control').addClass 'pause'
- @control.togglePlayback jQuery.Event('click')
-
- it 'trigger the pause event', ->
- expect('pause').toHaveBeenTriggeredOn @control
-
- describe 'when the video is paused', ->
- beforeEach ->
- spyOnEvent @control, 'play'
- $('.video_control').addClass 'play'
- @control.togglePlayback jQuery.Event('click')
-
- it 'trigger the play event', ->
- expect('play').toHaveBeenTriggeredOn @control
diff --git a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_control_spec.js b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_control_spec.js
new file mode 100644
index 0000000000..9def050941
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_control_spec.js
@@ -0,0 +1,128 @@
+// Generated by CoffeeScript 1.6.3
+(function() {
+ xdescribe('VideoControlAlpha', function() {
+ beforeEach(function() {
+ window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn(false);
+ loadFixtures('videoalpha.html');
+ return $('.video-controls').html('');
+ });
+ describe('constructor', function() {
+ it('render the video controls', function() {
+ this.control = new window.VideoControlAlpha({
+ el: $('.video-controls')
+ });
+ expect($('.video-controls')).toContain;
+ ['.slider', 'ul.vcr', 'a.play', '.vidtime', '.add-fullscreen'].join(',');
+ return expect($('.video-controls').find('.vidtime')).toHaveText('0:00 / 0:00');
+ });
+ it('bind the playback button', function() {
+ this.control = new window.VideoControlAlpha({
+ el: $('.video-controls')
+ });
+ return expect($('.video_control')).toHandleWith('click', this.control.togglePlayback);
+ });
+ describe('when on a touch based device', function() {
+ beforeEach(function() {
+ window.onTouchBasedDevice.andReturn(true);
+ return this.control = new window.VideoControlAlpha({
+ el: $('.video-controls')
+ });
+ });
+ return it('does not add the play class to video control', function() {
+ expect($('.video_control')).not.toHaveClass('play');
+ return expect($('.video_control')).not.toHaveHtml('Play');
+ });
+ });
+ return describe('when on a non-touch based device', function() {
+ beforeEach(function() {
+ return this.control = new window.VideoControlAlpha({
+ el: $('.video-controls')
+ });
+ });
+ return it('add the play class to video control', function() {
+ expect($('.video_control')).toHaveClass('play');
+ return expect($('.video_control')).toHaveHtml('Play');
+ });
+ });
+ });
+ describe('play', function() {
+ beforeEach(function() {
+ this.control = new window.VideoControlAlpha({
+ el: $('.video-controls')
+ });
+ return this.control.play();
+ });
+ return it('switch playback button to play state', function() {
+ expect($('.video_control')).not.toHaveClass('play');
+ expect($('.video_control')).toHaveClass('pause');
+ return expect($('.video_control')).toHaveHtml('Pause');
+ });
+ });
+ describe('pause', function() {
+ beforeEach(function() {
+ this.control = new window.VideoControlAlpha({
+ el: $('.video-controls')
+ });
+ return this.control.pause();
+ });
+ return it('switch playback button to pause state', function() {
+ expect($('.video_control')).not.toHaveClass('pause');
+ expect($('.video_control')).toHaveClass('play');
+ return expect($('.video_control')).toHaveHtml('Play');
+ });
+ });
+ return describe('togglePlayback', function() {
+ beforeEach(function() {
+ return this.control = new window.VideoControlAlpha({
+ el: $('.video-controls')
+ });
+ });
+ return describe('when the control does not have play or pause class', function() {
+ beforeEach(function() {
+ return $('.video_control').removeClass('play').removeClass('pause');
+ });
+ describe('when the video is playing', function() {
+ beforeEach(function() {
+ $('.video_control').addClass('play');
+ spyOnEvent(this.control, 'pause');
+ return this.control.togglePlayback(jQuery.Event('click'));
+ });
+ return it('does not trigger the pause event', function() {
+ return expect('pause').not.toHaveBeenTriggeredOn(this.control);
+ });
+ });
+ describe('when the video is paused', function() {
+ beforeEach(function() {
+ $('.video_control').addClass('pause');
+ spyOnEvent(this.control, 'play');
+ return this.control.togglePlayback(jQuery.Event('click'));
+ });
+ return it('does not trigger the play event', function() {
+ return expect('play').not.toHaveBeenTriggeredOn(this.control);
+ });
+ });
+ describe('when the video is playing', function() {
+ beforeEach(function() {
+ spyOnEvent(this.control, 'pause');
+ $('.video_control').addClass('pause');
+ return this.control.togglePlayback(jQuery.Event('click'));
+ });
+ return it('trigger the pause event', function() {
+ return expect('pause').toHaveBeenTriggeredOn(this.control);
+ });
+ });
+ return describe('when the video is paused', function() {
+ beforeEach(function() {
+ spyOnEvent(this.control, 'play');
+ $('.video_control').addClass('play');
+ return this.control.togglePlayback(jQuery.Event('click'));
+ });
+ return it('trigger the play event', function() {
+ return expect('play').toHaveBeenTriggeredOn(this.control);
+ });
+ });
+ });
+ });
+ });
+
+}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_player_spec.coffee b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_player_spec.coffee
deleted file mode 100644
index e9a5ca30b4..0000000000
--- a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_player_spec.coffee
+++ /dev/null
@@ -1,561 +0,0 @@
-describe 'VideoPlayerAlpha', ->
- playerVars =
- controls: 0
- wmode: 'transparent'
- rel: 0
- showinfo: 0
- enablejsapi: 1
- modestbranding: 1
- html5: 1
-
- beforeEach ->
- window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn false
- # It tries to call methods of VideoProgressSlider on Spy
- for part in ['VideoCaptionAlpha', 'VideoSpeedControlAlpha', 'VideoVolumeControlAlpha', 'VideoProgressSliderAlpha', 'VideoControlAlpha']
- spyOn(window[part].prototype, 'initialize').andCallThrough()
-
-
- afterEach ->
- YT.Player = undefined
-
- describe 'constructor', ->
- beforeEach ->
- $.fn.qtip.andCallFake ->
- $(this).data('qtip', true)
-
- describe 'always', ->
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @, [], false
- $('.video').append $('
')
- @player = new VideoPlayerAlpha video: @video
-
- it 'instanticate current time to zero', ->
- expect(@player.currentTime).toEqual 0
-
- it 'set the element', ->
- expect(@player.el).toHaveId 'video_id'
-
- it 'create video control', ->
- expect(window.VideoControlAlpha.prototype.initialize).toHaveBeenCalled()
- expect(@player.control).toBeDefined()
- expect(@player.control.el).toBe $('.video-controls', @player.el)
-
- it 'create video caption', ->
- expect(window.VideoCaptionAlpha.prototype.initialize).toHaveBeenCalled()
- expect(@player.caption).toBeDefined()
- expect(@player.caption.el).toBe @player.el
- expect(@player.caption.youtubeId).toEqual 'normalSpeedYoutubeId'
- expect(@player.caption.currentSpeed).toEqual '1.0'
- expect(@player.caption.captionAssetPath).toEqual '/static/subs/'
-
- it 'create video speed control', ->
- expect(window.VideoSpeedControlAlpha.prototype.initialize).toHaveBeenCalled()
- expect(@player.speedControl).toBeDefined()
- expect(@player.speedControl.el).toBe $('.secondary-controls', @player.el)
- expect(@player.speedControl.speeds).toEqual ['0.75', '1.0']
- expect(@player.speedControl.currentSpeed).toEqual '1.0'
-
- it 'create video progress slider', ->
- expect(window.VideoSpeedControlAlpha.prototype.initialize).toHaveBeenCalled()
- expect(@player.progressSlider).toBeDefined()
- expect(@player.progressSlider.el).toBe $('.slider', @player.el)
-
- it 'bind to video control play event', ->
- expect($(@player.control)).toHandleWith 'play', @player.play
-
- it 'bind to video control pause event', ->
- expect($(@player.control)).toHandleWith 'pause', @player.pause
-
- it 'bind to video caption seek event', ->
- expect($(@player.caption)).toHandleWith 'caption_seek', @player.onSeek
-
- it 'bind to video speed control speedChange event', ->
- expect($(@player.speedControl)).toHandleWith 'speedChange', @player.onSpeedChange
-
- it 'bind to video progress slider seek event', ->
- expect($(@player.progressSlider)).toHandleWith 'slide_seek', @player.onSeek
-
- it 'bind to video volume control volumeChange event', ->
- expect($(@player.volumeControl)).toHandleWith 'volumeChange', @player.onVolumeChange
-
- it 'bind to key press', ->
- expect($(document.documentElement)).toHandleWith 'keyup', @player.bindExitFullScreen
-
- it 'bind to fullscreen switching button', ->
- expect($('.add-fullscreen')).toHandleWith 'click', @player.toggleFullScreen
-
- it 'create Youtube player', ->
- jasmine.stubVideoPlayerAlpha @, [], false
- $('.video').append $('
')
- spyOn YT, 'Player'
- @player = new VideoPlayerAlpha video: @video
- expect(YT.Player).toHaveBeenCalledWith('id', {
- playerVars: playerVars
- videoId: 'normalSpeedYoutubeId'
- events:
- onReady: @player.onReady
- onStateChange: @player.onStateChange
- onPlaybackQualityChange: @player.onPlaybackQualityChange
- })
-
- it 'create HTML5 player', ->
- jasmine.stubVideoPlayerAlpha @, [], false, true
- spyOn HTML5Video, 'Player'
- $('.video').append $('
')
- @player = new VideoPlayerAlpha video: @video
- expect(HTML5Video.Player).toHaveBeenCalledWith @video.el,
- playerVars: playerVars
- videoSources: @video.html5Sources
- events:
- onReady: @player.onReady
- onStateChange: @player.onStateChange
-
- describe 'when not on a touch based device', ->
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @, [], false
- $('.video').append $('
')
- $('.add-fullscreen, .hide-subtitles').removeData 'qtip'
- @player = new VideoPlayerAlpha video: @video
-
- it 'add the tooltip to fullscreen and subtitle button', ->
- expect($('.add-fullscreen')).toHaveData 'qtip'
- expect($('.hide-subtitles')).toHaveData 'qtip'
-
- it 'create video volume control', ->
- expect(window.VideoVolumeControlAlpha.prototype.initialize).toHaveBeenCalled()
- expect(@player.volumeControl).toBeDefined()
- expect(@player.volumeControl.el).toBe $('.secondary-controls', @player.el)
-
- describe 'when on a touch based device', ->
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @, [], false
- $('.video').append $('
')
- window.onTouchBasedDevice.andReturn true
- $('.add-fullscreen, .hide-subtitles').removeData 'qtip'
- @player = new VideoPlayerAlpha video: @video
-
- it 'does not add the tooltip to fullscreen and subtitle button', ->
- expect($('.add-fullscreen')).not.toHaveData 'qtip'
- expect($('.hide-subtitles')).not.toHaveData 'qtip'
-
- it 'does not create video volume control', ->
- expect(window.VideoVolumeControlAlpha.prototype.initialize).not.toHaveBeenCalled()
- expect(@player.volumeControl).not.toBeDefined()
-
- describe 'onReady', ->
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @, [], false
- spyOn @video, 'log'
- $('.video').append $('
')
- @video.embed()
- @player = @video.player
- spyOnEvent @player, 'ready'
- spyOnEvent @player, 'updatePlayTime'
- @player.onReady()
-
- it 'log the load_video event', ->
- expect(@video.log).toHaveBeenCalledWith 'load_video'
-
- describe 'when not on a touch based device', ->
- beforeEach ->
- spyOn @player, 'play'
- @player.onReady()
-
- it 'autoplay the first video', ->
- expect(@player.play).toHaveBeenCalled()
-
- describe 'when on a touch based device', ->
- beforeEach ->
- window.onTouchBasedDevice.andReturn true
- spyOn @player, 'play'
- @player.onReady()
-
- it 'does not autoplay the first video', ->
- expect(@player.play).not.toHaveBeenCalled()
-
- describe 'onStateChange', ->
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @, [], false
- $('.video').append $('
')
-
- describe 'when the video is unstarted', ->
- beforeEach ->
- @player = new VideoPlayerAlpha video: @video
- spyOn @player.control, 'pause'
- @player.caption.pause = jasmine.createSpy('VideoCaptionAlpha.pause')
- @player.onStateChange data: YT.PlayerState.UNSTARTED
-
- it 'pause the video control', ->
- expect(@player.control.pause).toHaveBeenCalled()
-
- it 'pause the video caption', ->
- expect(@player.caption.pause).toHaveBeenCalled()
-
- describe 'when the video is playing', ->
- beforeEach ->
- @anotherPlayer = jasmine.createSpyObj 'AnotherPlayer', ['onPause']
- window.OldVideoPlayerAlpha = @anotherPlayer
- @player = new VideoPlayerAlpha video: @video
- spyOn @video, 'log'
- spyOn(window, 'setInterval').andReturn 100
- spyOn @player.control, 'play'
- @player.caption.play = jasmine.createSpy('VideoCaptionAlpha.play')
- @player.progressSlider.play = jasmine.createSpy('VideoProgressSliderAlpha.play')
- @player.player.getVideoEmbedCode.andReturn 'embedCode'
- @player.onStateChange data: YT.PlayerState.PLAYING
-
- it 'log the play_video event', ->
- expect(@video.log).toHaveBeenCalledWith 'play_video', {currentTime: 0}
-
- it 'pause other video player', ->
- expect(@anotherPlayer.onPause).toHaveBeenCalled()
-
- it 'set current video player as active player', ->
- expect(window.OldVideoPlayerAlpha).toEqual @player
-
- it 'set update interval', ->
- expect(window.setInterval).toHaveBeenCalledWith @player.update, 200
- expect(@player.player.interval).toEqual 100
-
- it 'play the video control', ->
- expect(@player.control.play).toHaveBeenCalled()
-
- it 'play the video caption', ->
- expect(@player.caption.play).toHaveBeenCalled()
-
- it 'play the video progress slider', ->
- expect(@player.progressSlider.play).toHaveBeenCalled()
-
- describe 'when the video is paused', ->
- beforeEach ->
- @player = new VideoPlayerAlpha video: @video
- spyOn @video, 'log'
- spyOn window, 'clearInterval'
- spyOn @player.control, 'pause'
- @player.caption.pause = jasmine.createSpy('VideoCaptionAlpha.pause')
- @player.player.interval = 100
- @player.player.getVideoEmbedCode.andReturn 'embedCode'
- @player.onStateChange data: YT.PlayerState.PAUSED
-
- it 'log the pause_video event', ->
- expect(@video.log).toHaveBeenCalledWith 'pause_video', {currentTime: 0}
-
- it 'clear update interval', ->
- expect(window.clearInterval).toHaveBeenCalledWith 100
- expect(@player.player.interval).toBeNull()
-
- it 'pause the video control', ->
- expect(@player.control.pause).toHaveBeenCalled()
-
- it 'pause the video caption', ->
- expect(@player.caption.pause).toHaveBeenCalled()
-
- describe 'when the video is ended', ->
- beforeEach ->
- @player = new VideoPlayerAlpha video: @video
- spyOn @player.control, 'pause'
- @player.caption.pause = jasmine.createSpy('VideoCaptionAlpha.pause')
- @player.onStateChange data: YT.PlayerState.ENDED
-
- it 'pause the video control', ->
- expect(@player.control.pause).toHaveBeenCalled()
-
- it 'pause the video caption', ->
- expect(@player.caption.pause).toHaveBeenCalled()
-
- describe 'onSeek', ->
- conf = [{
- desc : 'check if seek_video is logged with slide_seek type',
- type: 'slide_seek',
- obj: 'progressSlider'
- },{
- desc : 'check if seek_video is logged with caption_seek type',
- type: 'caption_seek',
- obj: 'caption'
- }]
-
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @, [], false
- $('.video').append $('
')
- @player = new VideoPlayerAlpha video: @video
- spyOn window, 'clearInterval'
- @player.player.interval = 100
- spyOn @player, 'updatePlayTime'
- spyOn @video, 'log'
-
- $.each conf, (key, value) ->
- it value.desc, ->
- type = value.type
- old_time = 0
- new_time = 60
- $(@player[value.obj]).trigger value.type, new_time
- expect(@video.log).toHaveBeenCalledWith 'seek_video',
- old_time: old_time
- new_time: new_time
- type: value.type
-
- it 'seek the player', ->
- $(@player.progressSlider).trigger 'slide_seek', 60
- expect(@player.player.seekTo).toHaveBeenCalledWith 60, true
-
- it 'call updatePlayTime on player', ->
- $(@player.progressSlider).trigger 'slide_seek', 60
- expect(@player.updatePlayTime).toHaveBeenCalledWith 60
-
- describe 'when the player is playing', ->
- beforeEach ->
- $(@player.progressSlider).trigger 'slide_seek', 60
- @player.player.getPlayerState.andReturn YT.PlayerState.PLAYING
- @player.onSeek {}, 60
-
- it 'reset the update interval', ->
- expect(window.clearInterval).toHaveBeenCalledWith 100
-
- describe 'when the player is not playing', ->
- beforeEach ->
- $(@player.progressSlider).trigger 'slide_seek', 60
- @player.player.getPlayerState.andReturn YT.PlayerState.PAUSED
- @player.onSeek {}, 60
-
- it 'set the current time', ->
- expect(@player.currentTime).toEqual 60
-
- describe 'onSpeedChange', ->
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @, [], false
- $('.video').append $('
')
- @player = new VideoPlayerAlpha video: @video
- @player.currentTime = 60
- spyOn @player, 'updatePlayTime'
- spyOn(@video, 'setSpeed').andCallThrough()
- spyOn(@video, 'log')
-
- describe 'always', ->
- beforeEach ->
- @player.onSpeedChange {}, '0.75', false
-
- it 'check if speed_change_video is logged', ->
- expect(@video.log).toHaveBeenCalledWith 'speed_change_video',
- currentTime: @player.currentTime
- old_speed: '1.0'
- new_speed: '0.75'
-
- it 'convert the current time to the new speed', ->
- expect(@player.currentTime).toEqual '80.000'
-
- it 'set video speed to the new speed', ->
- expect(@video.setSpeed).toHaveBeenCalledWith '0.75', false
-
- it 'tell video caption that the speed has changed', ->
- expect(@player.caption.currentSpeed).toEqual '0.75'
-
- describe 'when the video is playing', ->
- beforeEach ->
- @player.player.getPlayerState.andReturn YT.PlayerState.PLAYING
- @player.onSpeedChange {}, '0.75'
-
- it 'load the video', ->
- expect(@player.player.loadVideoById).toHaveBeenCalledWith 'slowerSpeedYoutubeId', '80.000'
-
- it 'trigger updatePlayTime event', ->
- expect(@player.updatePlayTime).toHaveBeenCalledWith '80.000'
-
- describe 'when the video is not playing', ->
- beforeEach ->
- @player.player.getPlayerState.andReturn YT.PlayerState.PAUSED
- @player.onSpeedChange {}, '0.75'
-
- it 'cue the video', ->
- expect(@player.player.cueVideoById).toHaveBeenCalledWith 'slowerSpeedYoutubeId', '80.000'
-
- it 'trigger updatePlayTime event', ->
- expect(@player.updatePlayTime).toHaveBeenCalledWith '80.000'
-
- describe 'onVolumeChange', ->
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @, [], false
- $('.video').append $('
')
- @player = new VideoPlayerAlpha video: @video
- @player.onVolumeChange undefined, 60
-
- it 'set the volume on player', ->
- expect(@player.player.setVolume).toHaveBeenCalledWith 60
-
- describe 'update', ->
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @, [], false
- $('.video').append $('
')
- @player = new VideoPlayerAlpha video: @video
- spyOn @player, 'updatePlayTime'
-
- describe 'when the current time is unavailable from the player', ->
- beforeEach ->
- @player.player.getCurrentTime.andReturn undefined
- @player.update()
-
- it 'does not trigger updatePlayTime event', ->
- expect(@player.updatePlayTime).not.toHaveBeenCalled()
-
- describe 'when the current time is available from the player', ->
- beforeEach ->
- @player.player.getCurrentTime.andReturn 60
- @player.update()
-
- it 'trigger updatePlayTime event', ->
- expect(@player.updatePlayTime).toHaveBeenCalledWith(60)
-
- describe 'updatePlayTime', ->
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @, [], false
- $('.video').append $('
')
- @player = new VideoPlayerAlpha video: @video
- spyOn(@video, 'getDuration').andReturn 1800
- @player.caption.updatePlayTime = jasmine.createSpy('VideoCaptionAlpha.updatePlayTime')
- @player.progressSlider.updatePlayTime = jasmine.createSpy('VideoProgressSliderAlpha.updatePlayTime')
- @player.updatePlayTime 60
-
- it 'update the video playback time', ->
- expect($('.vidtime')).toHaveHtml '1:00 / 30:00'
-
- it 'update the playback time on caption', ->
- expect(@player.caption.updatePlayTime).toHaveBeenCalledWith 60
-
- it 'update the playback time on progress slider', ->
- expect(@player.progressSlider.updatePlayTime).toHaveBeenCalledWith 60, 1800
-
- describe 'toggleFullScreen', ->
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @, [], false
- $('.video').append $('
')
- @player = new VideoPlayerAlpha video: @video
- @player.caption.resize = jasmine.createSpy('VideoCaptionAlpha.resize')
-
- describe 'when the video player is not full screen', ->
- beforeEach ->
- spyOn @video, 'log'
- @player.el.removeClass 'fullscreen'
- @player.toggleFullScreen(jQuery.Event("click"))
-
- it 'log the fullscreen event', ->
- expect(@video.log).toHaveBeenCalledWith 'fullscreen',
- currentTime: @player.currentTime
-
- it 'replace the full screen button tooltip', ->
- expect($('.add-fullscreen')).toHaveAttr 'title', 'Exit fill browser'
-
- it 'add the fullscreen class', ->
- expect(@player.el).toHaveClass 'fullscreen'
-
- it 'tell VideoCaption to resize', ->
- expect(@player.caption.resize).toHaveBeenCalled()
-
- describe 'when the video player already full screen', ->
- beforeEach ->
- spyOn @video, 'log'
- @player.el.addClass 'fullscreen'
- @player.toggleFullScreen(jQuery.Event("click"))
-
- it 'log the not_fullscreen event', ->
- expect(@video.log).toHaveBeenCalledWith 'not_fullscreen',
- currentTime: @player.currentTime
-
- it 'replace the full screen button tooltip', ->
- expect($('.add-fullscreen')).toHaveAttr 'title', 'Fill browser'
-
- it 'remove exit full screen button', ->
- expect(@player.el).not.toContain 'a.exit'
-
- it 'remove the fullscreen class', ->
- expect(@player.el).not.toHaveClass 'fullscreen'
-
- it 'tell VideoCaption to resize', ->
- expect(@player.caption.resize).toHaveBeenCalled()
-
- describe 'play', ->
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @, [], false
- $('.video').append $('
')
- @player = new VideoPlayerAlpha video: @video
-
- describe 'when the player is not ready', ->
- beforeEach ->
- @player.player.playVideo = undefined
- @player.play()
-
- it 'does nothing', ->
- expect(@player.player.playVideo).toBeUndefined()
-
- describe 'when the player is ready', ->
- beforeEach ->
- @player.player.playVideo.andReturn true
- @player.play()
-
- it 'delegate to the Youtube player', ->
- expect(@player.player.playVideo).toHaveBeenCalled()
-
- describe 'isPlaying', ->
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @, [], false
- $('.video').append $('
')
- @player = new VideoPlayerAlpha video: @video
-
- describe 'when the video is playing', ->
- beforeEach ->
- @player.player.getPlayerState.andReturn YT.PlayerState.PLAYING
-
- it 'return true', ->
- expect(@player.isPlaying()).toBeTruthy()
-
- describe 'when the video is not playing', ->
- beforeEach ->
- @player.player.getPlayerState.andReturn YT.PlayerState.PAUSED
-
- it 'return false', ->
- expect(@player.isPlaying()).toBeFalsy()
-
- describe 'pause', ->
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @, [], false
- $('.video').append $('
')
- @player = new VideoPlayerAlpha video: @video
- @player.pause()
-
- it 'delegate to the Youtube player', ->
- expect(@player.player.pauseVideo).toHaveBeenCalled()
-
- describe 'duration', ->
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @, [], false
- $('.video').append $('
')
- @player = new VideoPlayerAlpha video: @video
- spyOn @video, 'getDuration'
- @player.duration()
-
- it 'delegate to the video', ->
- expect(@video.getDuration).toHaveBeenCalled()
-
- describe 'currentSpeed', ->
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @, [], false
- $('.video').append $('
')
- @player = new VideoPlayerAlpha video: @video
- @video.speed = '3.0'
-
- it 'delegate to the video', ->
- expect(@player.currentSpeed()).toEqual '3.0'
-
- describe 'volume', ->
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @, [], false
- $('.video').append $('
')
- @player = new VideoPlayerAlpha video: @video
- @player.player.getVolume.andReturn 42
-
- describe 'without value', ->
- it 'return current volume', ->
- expect(@player.volume()).toEqual 42
-
- describe 'with value', ->
- it 'set player volume', ->
- @player.volume(60)
- expect(@player.player.setVolume).toHaveBeenCalledWith(60)
diff --git a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_player_spec.js b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_player_spec.js
new file mode 100644
index 0000000000..d1c3938181
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_player_spec.js
@@ -0,0 +1,677 @@
+// Generated by CoffeeScript 1.6.3
+(function() {
+ xdescribe('VideoPlayerAlpha', function() {
+ var playerVars;
+ playerVars = {
+ controls: 0,
+ wmode: 'transparent',
+ rel: 0,
+ showinfo: 0,
+ enablejsapi: 1,
+ modestbranding: 1,
+ html5: 1
+ };
+ beforeEach(function() {
+ var part, _i, _len, _ref, _results;
+ window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn(false);
+ _ref = ['VideoCaptionAlpha', 'VideoSpeedControlAlpha', 'VideoVolumeControlAlpha', 'VideoProgressSliderAlpha', 'VideoControlAlpha'];
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ part = _ref[_i];
+ _results.push(spyOn(window[part].prototype, 'initialize').andCallThrough());
+ }
+ return _results;
+ });
+ afterEach(function() {
+ return YT.Player = void 0;
+ });
+ describe('constructor', function() {
+ beforeEach(function() {
+ return $.fn.qtip.andCallFake(function() {
+ return $(this).data('qtip', true);
+ });
+ });
+ describe('always', function() {
+ beforeEach(function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ $('.video').append($('
'));
+ return this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ });
+ it('instanticate current time to zero', function() {
+ return expect(this.player.currentTime).toEqual(0);
+ });
+ it('set the element', function() {
+ return expect(this.player.el).toHaveId('video_id');
+ });
+ it('create video control', function() {
+ expect(window.VideoControlAlpha.prototype.initialize).toHaveBeenCalled();
+ expect(this.player.control).toBeDefined();
+ return expect(this.player.control.el).toBe($('.video-controls', this.player.el));
+ });
+ it('create video caption', function() {
+ expect(window.VideoCaptionAlpha.prototype.initialize).toHaveBeenCalled();
+ expect(this.player.caption).toBeDefined();
+ expect(this.player.caption.el).toBe(this.player.el);
+ expect(this.player.caption.youtubeId).toEqual('normalSpeedYoutubeId');
+ expect(this.player.caption.currentSpeed).toEqual('1.0');
+ return expect(this.player.caption.captionAssetPath).toEqual('/static/subs/');
+ });
+ it('create video speed control', function() {
+ expect(window.VideoSpeedControlAlpha.prototype.initialize).toHaveBeenCalled();
+ expect(this.player.speedControl).toBeDefined();
+ expect(this.player.speedControl.el).toBe($('.secondary-controls', this.player.el));
+ expect(this.player.speedControl.speeds).toEqual(['0.75', '1.0']);
+ return expect(this.player.speedControl.currentSpeed).toEqual('1.0');
+ });
+ it('create video progress slider', function() {
+ expect(window.VideoSpeedControlAlpha.prototype.initialize).toHaveBeenCalled();
+ expect(this.player.progressSlider).toBeDefined();
+ return expect(this.player.progressSlider.el).toBe($('.slider', this.player.el));
+ });
+ it('bind to video control play event', function() {
+ return expect($(this.player.control)).toHandleWith('play', this.player.play);
+ });
+ it('bind to video control pause event', function() {
+ return expect($(this.player.control)).toHandleWith('pause', this.player.pause);
+ });
+ it('bind to video caption seek event', function() {
+ return expect($(this.player.caption)).toHandleWith('caption_seek', this.player.onSeek);
+ });
+ it('bind to video speed control speedChange event', function() {
+ return expect($(this.player.speedControl)).toHandleWith('speedChange', this.player.onSpeedChange);
+ });
+ it('bind to video progress slider seek event', function() {
+ return expect($(this.player.progressSlider)).toHandleWith('slide_seek', this.player.onSeek);
+ });
+ it('bind to video volume control volumeChange event', function() {
+ return expect($(this.player.volumeControl)).toHandleWith('volumeChange', this.player.onVolumeChange);
+ });
+ it('bind to key press', function() {
+ return expect($(document.documentElement)).toHandleWith('keyup', this.player.bindExitFullScreen);
+ });
+ return it('bind to fullscreen switching button', function() {
+ return expect($('.add-fullscreen')).toHandleWith('click', this.player.toggleFullScreen);
+ });
+ });
+ it('create Youtube player', function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ $('.video').append($('
'));
+ spyOn(YT, 'Player');
+ this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ return expect(YT.Player).toHaveBeenCalledWith('id', {
+ playerVars: playerVars,
+ videoId: 'normalSpeedYoutubeId',
+ events: {
+ onReady: this.player.onReady,
+ onStateChange: this.player.onStateChange,
+ onPlaybackQualityChange: this.player.onPlaybackQualityChange
+ }
+ });
+ });
+ it('create HTML5 player', function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false, true);
+ spyOn(HTML5Video, 'Player');
+ $('.video').append($('
'));
+ this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ return expect(HTML5Video.Player).toHaveBeenCalledWith(this.video.el, {
+ playerVars: playerVars,
+ videoSources: this.video.html5Sources,
+ events: {
+ onReady: this.player.onReady,
+ onStateChange: this.player.onStateChange
+ }
+ });
+ });
+ describe('when not on a touch based device', function() {
+ beforeEach(function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ $('.video').append($('
'));
+ $('.add-fullscreen, .hide-subtitles').removeData('qtip');
+ return this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ });
+ it('add the tooltip to fullscreen and subtitle button', function() {
+ expect($('.add-fullscreen')).toHaveData('qtip');
+ return expect($('.hide-subtitles')).toHaveData('qtip');
+ });
+ return it('create video volume control', function() {
+ expect(window.VideoVolumeControlAlpha.prototype.initialize).toHaveBeenCalled();
+ expect(this.player.volumeControl).toBeDefined();
+ return expect(this.player.volumeControl.el).toBe($('.secondary-controls', this.player.el));
+ });
+ });
+ return describe('when on a touch based device', function() {
+ beforeEach(function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ $('.video').append($('
'));
+ window.onTouchBasedDevice.andReturn(true);
+ $('.add-fullscreen, .hide-subtitles').removeData('qtip');
+ return this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ });
+ it('does not add the tooltip to fullscreen and subtitle button', function() {
+ expect($('.add-fullscreen')).not.toHaveData('qtip');
+ return expect($('.hide-subtitles')).not.toHaveData('qtip');
+ });
+ return it('does not create video volume control', function() {
+ expect(window.VideoVolumeControlAlpha.prototype.initialize).not.toHaveBeenCalled();
+ return expect(this.player.volumeControl).not.toBeDefined();
+ });
+ });
+ });
+ describe('onReady', function() {
+ beforeEach(function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ spyOn(this.video, 'log');
+ $('.video').append($('
'));
+ this.video.embed();
+ this.player = this.video.player;
+ spyOnEvent(this.player, 'ready');
+ spyOnEvent(this.player, 'updatePlayTime');
+ return this.player.onReady();
+ });
+ it('log the load_video event', function() {
+ return expect(this.video.log).toHaveBeenCalledWith('load_video');
+ });
+ describe('when not on a touch based device', function() {
+ beforeEach(function() {
+ spyOn(this.player, 'play');
+ return this.player.onReady();
+ });
+ return it('autoplay the first video', function() {
+ return expect(this.player.play).toHaveBeenCalled();
+ });
+ });
+ return describe('when on a touch based device', function() {
+ beforeEach(function() {
+ window.onTouchBasedDevice.andReturn(true);
+ spyOn(this.player, 'play');
+ return this.player.onReady();
+ });
+ return it('does not autoplay the first video', function() {
+ return expect(this.player.play).not.toHaveBeenCalled();
+ });
+ });
+ });
+ describe('onStateChange', function() {
+ beforeEach(function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ return $('.video').append($('
'));
+ });
+ describe('when the video is unstarted', function() {
+ beforeEach(function() {
+ this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ spyOn(this.player.control, 'pause');
+ this.player.caption.pause = jasmine.createSpy('VideoCaptionAlpha.pause');
+ return this.player.onStateChange({
+ data: YT.PlayerState.UNSTARTED
+ });
+ });
+ it('pause the video control', function() {
+ return expect(this.player.control.pause).toHaveBeenCalled();
+ });
+ return it('pause the video caption', function() {
+ return expect(this.player.caption.pause).toHaveBeenCalled();
+ });
+ });
+ describe('when the video is playing', function() {
+ beforeEach(function() {
+ this.anotherPlayer = jasmine.createSpyObj('AnotherPlayer', ['onPause']);
+ window.OldVideoPlayerAlpha = this.anotherPlayer;
+ this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ spyOn(this.video, 'log');
+ spyOn(window, 'setInterval').andReturn(100);
+ spyOn(this.player.control, 'play');
+ this.player.caption.play = jasmine.createSpy('VideoCaptionAlpha.play');
+ this.player.progressSlider.play = jasmine.createSpy('VideoProgressSliderAlpha.play');
+ this.player.player.getVideoEmbedCode.andReturn('embedCode');
+ return this.player.onStateChange({
+ data: YT.PlayerState.PLAYING
+ });
+ });
+ it('log the play_video event', function() {
+ return expect(this.video.log).toHaveBeenCalledWith('play_video', {
+ currentTime: 0
+ });
+ });
+ it('pause other video player', function() {
+ return expect(this.anotherPlayer.onPause).toHaveBeenCalled();
+ });
+ it('set current video player as active player', function() {
+ return expect(window.OldVideoPlayerAlpha).toEqual(this.player);
+ });
+ it('set update interval', function() {
+ expect(window.setInterval).toHaveBeenCalledWith(this.player.update, 200);
+ return expect(this.player.player.interval).toEqual(100);
+ });
+ it('play the video control', function() {
+ return expect(this.player.control.play).toHaveBeenCalled();
+ });
+ it('play the video caption', function() {
+ return expect(this.player.caption.play).toHaveBeenCalled();
+ });
+ return it('play the video progress slider', function() {
+ return expect(this.player.progressSlider.play).toHaveBeenCalled();
+ });
+ });
+ describe('when the video is paused', function() {
+ beforeEach(function() {
+ this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ spyOn(this.video, 'log');
+ spyOn(window, 'clearInterval');
+ spyOn(this.player.control, 'pause');
+ this.player.caption.pause = jasmine.createSpy('VideoCaptionAlpha.pause');
+ this.player.player.interval = 100;
+ this.player.player.getVideoEmbedCode.andReturn('embedCode');
+ return this.player.onStateChange({
+ data: YT.PlayerState.PAUSED
+ });
+ });
+ it('log the pause_video event', function() {
+ return expect(this.video.log).toHaveBeenCalledWith('pause_video', {
+ currentTime: 0
+ });
+ });
+ it('clear update interval', function() {
+ expect(window.clearInterval).toHaveBeenCalledWith(100);
+ return expect(this.player.player.interval).toBeNull();
+ });
+ it('pause the video control', function() {
+ return expect(this.player.control.pause).toHaveBeenCalled();
+ });
+ return it('pause the video caption', function() {
+ return expect(this.player.caption.pause).toHaveBeenCalled();
+ });
+ });
+ return describe('when the video is ended', function() {
+ beforeEach(function() {
+ this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ spyOn(this.player.control, 'pause');
+ this.player.caption.pause = jasmine.createSpy('VideoCaptionAlpha.pause');
+ return this.player.onStateChange({
+ data: YT.PlayerState.ENDED
+ });
+ });
+ it('pause the video control', function() {
+ return expect(this.player.control.pause).toHaveBeenCalled();
+ });
+ return it('pause the video caption', function() {
+ return expect(this.player.caption.pause).toHaveBeenCalled();
+ });
+ });
+ });
+ describe('onSeek', function() {
+ var conf;
+ conf = [
+ {
+ desc: 'check if seek_video is logged with slide_seek type',
+ type: 'slide_seek',
+ obj: 'progressSlider'
+ }, {
+ desc: 'check if seek_video is logged with caption_seek type',
+ type: 'caption_seek',
+ obj: 'caption'
+ }
+ ];
+ beforeEach(function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ $('.video').append($('
'));
+ this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ spyOn(window, 'clearInterval');
+ this.player.player.interval = 100;
+ spyOn(this.player, 'updatePlayTime');
+ return spyOn(this.video, 'log');
+ });
+ $.each(conf, function(key, value) {
+ return it(value.desc, function() {
+ var new_time, old_time, type;
+ type = value.type;
+ old_time = 0;
+ new_time = 60;
+ $(this.player[value.obj]).trigger(value.type, new_time);
+ return expect(this.video.log).toHaveBeenCalledWith('seek_video', {
+ old_time: old_time,
+ new_time: new_time,
+ type: value.type
+ });
+ });
+ });
+ it('seek the player', function() {
+ $(this.player.progressSlider).trigger('slide_seek', 60);
+ return expect(this.player.player.seekTo).toHaveBeenCalledWith(60, true);
+ });
+ it('call updatePlayTime on player', function() {
+ $(this.player.progressSlider).trigger('slide_seek', 60);
+ return expect(this.player.updatePlayTime).toHaveBeenCalledWith(60);
+ });
+ describe('when the player is playing', function() {
+ beforeEach(function() {
+ $(this.player.progressSlider).trigger('slide_seek', 60);
+ this.player.player.getPlayerState.andReturn(YT.PlayerState.PLAYING);
+ return this.player.onSeek({}, 60);
+ });
+ return it('reset the update interval', function() {
+ return expect(window.clearInterval).toHaveBeenCalledWith(100);
+ });
+ });
+ return describe('when the player is not playing', function() {
+ beforeEach(function() {
+ $(this.player.progressSlider).trigger('slide_seek', 60);
+ this.player.player.getPlayerState.andReturn(YT.PlayerState.PAUSED);
+ return this.player.onSeek({}, 60);
+ });
+ return it('set the current time', function() {
+ return expect(this.player.currentTime).toEqual(60);
+ });
+ });
+ });
+ describe('onSpeedChange', function() {
+ beforeEach(function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ $('.video').append($('
'));
+ this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ this.player.currentTime = 60;
+ spyOn(this.player, 'updatePlayTime');
+ spyOn(this.video, 'setSpeed').andCallThrough();
+ return spyOn(this.video, 'log');
+ });
+ describe('always', function() {
+ beforeEach(function() {
+ return this.player.onSpeedChange({}, '0.75', false);
+ });
+ it('check if speed_change_video is logged', function() {
+ return expect(this.video.log).toHaveBeenCalledWith('speed_change_video', {
+ currentTime: this.player.currentTime,
+ old_speed: '1.0',
+ new_speed: '0.75'
+ });
+ });
+ it('convert the current time to the new speed', function() {
+ return expect(this.player.currentTime).toEqual('80.000');
+ });
+ it('set video speed to the new speed', function() {
+ return expect(this.video.setSpeed).toHaveBeenCalledWith('0.75', false);
+ });
+ return it('tell video caption that the speed has changed', function() {
+ return expect(this.player.caption.currentSpeed).toEqual('0.75');
+ });
+ });
+ describe('when the video is playing', function() {
+ beforeEach(function() {
+ this.player.player.getPlayerState.andReturn(YT.PlayerState.PLAYING);
+ return this.player.onSpeedChange({}, '0.75');
+ });
+ it('load the video', function() {
+ return expect(this.player.player.loadVideoById).toHaveBeenCalledWith('slowerSpeedYoutubeId', '80.000');
+ });
+ return it('trigger updatePlayTime event', function() {
+ return expect(this.player.updatePlayTime).toHaveBeenCalledWith('80.000');
+ });
+ });
+ return describe('when the video is not playing', function() {
+ beforeEach(function() {
+ this.player.player.getPlayerState.andReturn(YT.PlayerState.PAUSED);
+ return this.player.onSpeedChange({}, '0.75');
+ });
+ it('cue the video', function() {
+ return expect(this.player.player.cueVideoById).toHaveBeenCalledWith('slowerSpeedYoutubeId', '80.000');
+ });
+ return it('trigger updatePlayTime event', function() {
+ return expect(this.player.updatePlayTime).toHaveBeenCalledWith('80.000');
+ });
+ });
+ });
+ describe('onVolumeChange', function() {
+ beforeEach(function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ $('.video').append($('
'));
+ this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ return this.player.onVolumeChange(void 0, 60);
+ });
+ return it('set the volume on player', function() {
+ return expect(this.player.player.setVolume).toHaveBeenCalledWith(60);
+ });
+ });
+ describe('update', function() {
+ beforeEach(function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ $('.video').append($('
'));
+ this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ return spyOn(this.player, 'updatePlayTime');
+ });
+ describe('when the current time is unavailable from the player', function() {
+ beforeEach(function() {
+ this.player.player.getCurrentTime.andReturn(void 0);
+ return this.player.update();
+ });
+ return it('does not trigger updatePlayTime event', function() {
+ return expect(this.player.updatePlayTime).not.toHaveBeenCalled();
+ });
+ });
+ return describe('when the current time is available from the player', function() {
+ beforeEach(function() {
+ this.player.player.getCurrentTime.andReturn(60);
+ return this.player.update();
+ });
+ return it('trigger updatePlayTime event', function() {
+ return expect(this.player.updatePlayTime).toHaveBeenCalledWith(60);
+ });
+ });
+ });
+ describe('updatePlayTime', function() {
+ beforeEach(function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ $('.video').append($('
'));
+ this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ spyOn(this.video, 'getDuration').andReturn(1800);
+ this.player.caption.updatePlayTime = jasmine.createSpy('VideoCaptionAlpha.updatePlayTime');
+ this.player.progressSlider.updatePlayTime = jasmine.createSpy('VideoProgressSliderAlpha.updatePlayTime');
+ return this.player.updatePlayTime(60);
+ });
+ it('update the video playback time', function() {
+ return expect($('.vidtime')).toHaveHtml('1:00 / 30:00');
+ });
+ it('update the playback time on caption', function() {
+ return expect(this.player.caption.updatePlayTime).toHaveBeenCalledWith(60);
+ });
+ return it('update the playback time on progress slider', function() {
+ return expect(this.player.progressSlider.updatePlayTime).toHaveBeenCalledWith(60, 1800);
+ });
+ });
+ describe('toggleFullScreen', function() {
+ beforeEach(function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ $('.video').append($('
'));
+ this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ return this.player.caption.resize = jasmine.createSpy('VideoCaptionAlpha.resize');
+ });
+ describe('when the video player is not full screen', function() {
+ beforeEach(function() {
+ spyOn(this.video, 'log');
+ this.player.el.removeClass('fullscreen');
+ return this.player.toggleFullScreen(jQuery.Event("click"));
+ });
+ it('log the fullscreen event', function() {
+ return expect(this.video.log).toHaveBeenCalledWith('fullscreen', {
+ currentTime: this.player.currentTime
+ });
+ });
+ it('replace the full screen button tooltip', function() {
+ return expect($('.add-fullscreen')).toHaveAttr('title', 'Exit fill browser');
+ });
+ it('add the fullscreen class', function() {
+ return expect(this.player.el).toHaveClass('fullscreen');
+ });
+ return it('tell VideoCaption to resize', function() {
+ return expect(this.player.caption.resize).toHaveBeenCalled();
+ });
+ });
+ return describe('when the video player already full screen', function() {
+ beforeEach(function() {
+ spyOn(this.video, 'log');
+ this.player.el.addClass('fullscreen');
+ return this.player.toggleFullScreen(jQuery.Event("click"));
+ });
+ it('log the not_fullscreen event', function() {
+ return expect(this.video.log).toHaveBeenCalledWith('not_fullscreen', {
+ currentTime: this.player.currentTime
+ });
+ });
+ it('replace the full screen button tooltip', function() {
+ return expect($('.add-fullscreen')).toHaveAttr('title', 'Fill browser');
+ });
+ it('remove exit full screen button', function() {
+ return expect(this.player.el).not.toContain('a.exit');
+ });
+ it('remove the fullscreen class', function() {
+ return expect(this.player.el).not.toHaveClass('fullscreen');
+ });
+ return it('tell VideoCaption to resize', function() {
+ return expect(this.player.caption.resize).toHaveBeenCalled();
+ });
+ });
+ });
+ describe('play', function() {
+ beforeEach(function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ $('.video').append($('
'));
+ return this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ });
+ describe('when the player is not ready', function() {
+ beforeEach(function() {
+ this.player.player.playVideo = void 0;
+ return this.player.play();
+ });
+ return it('does nothing', function() {
+ return expect(this.player.player.playVideo).toBeUndefined();
+ });
+ });
+ return describe('when the player is ready', function() {
+ beforeEach(function() {
+ this.player.player.playVideo.andReturn(true);
+ return this.player.play();
+ });
+ return it('delegate to the Youtube player', function() {
+ return expect(this.player.player.playVideo).toHaveBeenCalled();
+ });
+ });
+ });
+ describe('isPlaying', function() {
+ beforeEach(function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ $('.video').append($('
'));
+ return this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ });
+ describe('when the video is playing', function() {
+ beforeEach(function() {
+ return this.player.player.getPlayerState.andReturn(YT.PlayerState.PLAYING);
+ });
+ return it('return true', function() {
+ return expect(this.player.isPlaying()).toBeTruthy();
+ });
+ });
+ return describe('when the video is not playing', function() {
+ beforeEach(function() {
+ return this.player.player.getPlayerState.andReturn(YT.PlayerState.PAUSED);
+ });
+ return it('return false', function() {
+ return expect(this.player.isPlaying()).toBeFalsy();
+ });
+ });
+ });
+ describe('pause', function() {
+ beforeEach(function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ $('.video').append($('
'));
+ this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ return this.player.pause();
+ });
+ return it('delegate to the Youtube player', function() {
+ return expect(this.player.player.pauseVideo).toHaveBeenCalled();
+ });
+ });
+ describe('duration', function() {
+ beforeEach(function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ $('.video').append($('
'));
+ this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ spyOn(this.video, 'getDuration');
+ return this.player.duration();
+ });
+ return it('delegate to the video', function() {
+ return expect(this.video.getDuration).toHaveBeenCalled();
+ });
+ });
+ describe('currentSpeed', function() {
+ beforeEach(function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ $('.video').append($('
'));
+ this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ return this.video.speed = '3.0';
+ });
+ return it('delegate to the video', function() {
+ return expect(this.player.currentSpeed()).toEqual('3.0');
+ });
+ });
+ return describe('volume', function() {
+ beforeEach(function() {
+ jasmine.stubVideoPlayerAlpha(this, [], false);
+ $('.video').append($('
'));
+ this.player = new VideoPlayerAlpha({
+ video: this.video
+ });
+ return this.player.player.getVolume.andReturn(42);
+ });
+ describe('without value', function() {
+ return it('return current volume', function() {
+ return expect(this.player.volume()).toEqual(42);
+ });
+ });
+ return describe('with value', function() {
+ return it('set player volume', function() {
+ this.player.volume(60);
+ return expect(this.player.player.setVolume).toHaveBeenCalledWith(60);
+ });
+ });
+ });
+ });
+
+}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_progress_slider_spec.coffee b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_progress_slider_spec.coffee
deleted file mode 100644
index dd787aefbb..0000000000
--- a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_progress_slider_spec.coffee
+++ /dev/null
@@ -1,165 +0,0 @@
-describe 'VideoProgressSliderAlpha', ->
- beforeEach ->
- window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn false
-
- describe 'constructor', ->
- describe 'on a non-touch based device', ->
- beforeEach ->
- spyOn($.fn, 'slider').andCallThrough()
- @player = jasmine.stubVideoPlayerAlpha @
- @progressSlider = @player.progressSlider
-
- it 'build the slider', ->
- expect(@progressSlider.slider).toBe '.slider'
- expect($.fn.slider).toHaveBeenCalledWith
- range: 'min'
- change: @progressSlider.onChange
- slide: @progressSlider.onSlide
- stop: @progressSlider.onStop
-
- it 'build the seek handle', ->
- expect(@progressSlider.handle).toBe '.slider .ui-slider-handle'
- expect($.fn.qtip).toHaveBeenCalledWith
- content: "0:00"
- position:
- my: 'bottom center'
- at: 'top center'
- container: @progressSlider.handle
- hide:
- delay: 700
- style:
- classes: 'ui-tooltip-slider'
- widget: true
-
- describe 'on a touch-based device', ->
- beforeEach ->
- window.onTouchBasedDevice.andReturn true
- spyOn($.fn, 'slider').andCallThrough()
- @player = jasmine.stubVideoPlayerAlpha @
- @progressSlider = @player.progressSlider
-
- it 'does not build the slider', ->
- expect(@progressSlider.slider).toBeUndefined
- expect($.fn.slider).not.toHaveBeenCalled()
-
- describe 'play', ->
- beforeEach ->
- spyOn(VideoProgressSliderAlpha.prototype, 'buildSlider').andCallThrough()
- @player = jasmine.stubVideoPlayerAlpha @
- @progressSlider = @player.progressSlider
-
- describe 'when the slider was already built', ->
-
- beforeEach ->
- @progressSlider.play()
-
- it 'does not build the slider', ->
- expect(@progressSlider.buildSlider.calls.length).toEqual 1
-
- describe 'when the slider was not already built', ->
- beforeEach ->
- spyOn($.fn, 'slider').andCallThrough()
- @progressSlider.slider = null
- @progressSlider.play()
-
- it 'build the slider', ->
- expect(@progressSlider.slider).toBe '.slider'
- expect($.fn.slider).toHaveBeenCalledWith
- range: 'min'
- change: @progressSlider.onChange
- slide: @progressSlider.onSlide
- stop: @progressSlider.onStop
-
- it 'build the seek handle', ->
- expect(@progressSlider.handle).toBe '.ui-slider-handle'
- expect($.fn.qtip).toHaveBeenCalledWith
- content: "0:00"
- position:
- my: 'bottom center'
- at: 'top center'
- container: @progressSlider.handle
- hide:
- delay: 700
- style:
- classes: 'ui-tooltip-slider'
- widget: true
-
- describe 'updatePlayTime', ->
- beforeEach ->
- @player = jasmine.stubVideoPlayerAlpha @
- @progressSlider = @player.progressSlider
-
- describe 'when frozen', ->
- beforeEach ->
- spyOn($.fn, 'slider').andCallThrough()
- @progressSlider.frozen = true
- @progressSlider.updatePlayTime 20, 120
-
- it 'does not update the slider', ->
- expect($.fn.slider).not.toHaveBeenCalled()
-
- describe 'when not frozen', ->
- beforeEach ->
- spyOn($.fn, 'slider').andCallThrough()
- @progressSlider.frozen = false
- @progressSlider.updatePlayTime 20, 120
-
- it 'update the max value of the slider', ->
- expect($.fn.slider).toHaveBeenCalledWith 'option', 'max', 120
-
- it 'update current value of the slider', ->
- expect($.fn.slider).toHaveBeenCalledWith 'value', 20
-
- describe 'onSlide', ->
- beforeEach ->
- @player = jasmine.stubVideoPlayerAlpha @
- @progressSlider = @player.progressSlider
- spyOnEvent @progressSlider, 'slide_seek'
- @progressSlider.onSlide {}, value: 20
-
- it 'freeze the slider', ->
- expect(@progressSlider.frozen).toBeTruthy()
-
- it 'update the tooltip', ->
- expect($.fn.qtip).toHaveBeenCalled()
-
- it 'trigger seek event', ->
- expect('slide_seek').toHaveBeenTriggeredOn @progressSlider
- expect(@player.currentTime).toEqual 20
-
- describe 'onChange', ->
- beforeEach ->
- @player = jasmine.stubVideoPlayerAlpha @
- @progressSlider = @player.progressSlider
- @progressSlider.onChange {}, value: 20
-
- it 'update the tooltip', ->
- expect($.fn.qtip).toHaveBeenCalled()
-
- describe 'onStop', ->
- beforeEach ->
- @player = jasmine.stubVideoPlayerAlpha @
- @progressSlider = @player.progressSlider
- spyOnEvent @progressSlider, 'slide_seek'
- @progressSlider.onStop {}, value: 20
-
- it 'freeze the slider', ->
- expect(@progressSlider.frozen).toBeTruthy()
-
- it 'trigger seek event', ->
- expect('slide_seek').toHaveBeenTriggeredOn @progressSlider
- expect(@player.currentTime).toEqual 20
-
- it 'set timeout to unfreeze the slider', ->
- expect(window.setTimeout).toHaveBeenCalledWith jasmine.any(Function), 200
- window.setTimeout.mostRecentCall.args[0]()
- expect(@progressSlider.frozen).toBeFalsy()
-
- describe 'updateTooltip', ->
- beforeEach ->
- @player = jasmine.stubVideoPlayerAlpha @
- @progressSlider = @player.progressSlider
- @progressSlider.updateTooltip 90
-
- it 'set the tooltip value', ->
- expect($.fn.qtip).toHaveBeenCalledWith 'option', 'content.text', '1:30'
diff --git a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_progress_slider_spec.js b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_progress_slider_spec.js
new file mode 100644
index 0000000000..6b5b36635e
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_progress_slider_spec.js
@@ -0,0 +1,199 @@
+// Generated by CoffeeScript 1.6.3
+(function() {
+ xdescribe('VideoProgressSliderAlpha', function() {
+ beforeEach(function() {
+ return window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn(false);
+ });
+ describe('constructor', function() {
+ describe('on a non-touch based device', function() {
+ beforeEach(function() {
+ spyOn($.fn, 'slider').andCallThrough();
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ return this.progressSlider = this.player.progressSlider;
+ });
+ it('build the slider', function() {
+ expect(this.progressSlider.slider).toBe('.slider');
+ return expect($.fn.slider).toHaveBeenCalledWith({
+ range: 'min',
+ change: this.progressSlider.onChange,
+ slide: this.progressSlider.onSlide,
+ stop: this.progressSlider.onStop
+ });
+ });
+ return it('build the seek handle', function() {
+ expect(this.progressSlider.handle).toBe('.slider .ui-slider-handle');
+ return expect($.fn.qtip).toHaveBeenCalledWith({
+ content: "0:00",
+ position: {
+ my: 'bottom center',
+ at: 'top center',
+ container: this.progressSlider.handle
+ },
+ hide: {
+ delay: 700
+ },
+ style: {
+ classes: 'ui-tooltip-slider',
+ widget: true
+ }
+ });
+ });
+ });
+ return describe('on a touch-based device', function() {
+ beforeEach(function() {
+ window.onTouchBasedDevice.andReturn(true);
+ spyOn($.fn, 'slider').andCallThrough();
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ return this.progressSlider = this.player.progressSlider;
+ });
+ return it('does not build the slider', function() {
+ expect(this.progressSlider.slider).toBeUndefined;
+ return expect($.fn.slider).not.toHaveBeenCalled();
+ });
+ });
+ });
+ describe('play', function() {
+ beforeEach(function() {
+ spyOn(VideoProgressSliderAlpha.prototype, 'buildSlider').andCallThrough();
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ return this.progressSlider = this.player.progressSlider;
+ });
+ describe('when the slider was already built', function() {
+ beforeEach(function() {
+ return this.progressSlider.play();
+ });
+ return it('does not build the slider', function() {
+ return expect(this.progressSlider.buildSlider.calls.length).toEqual(1);
+ });
+ });
+ return describe('when the slider was not already built', function() {
+ beforeEach(function() {
+ spyOn($.fn, 'slider').andCallThrough();
+ this.progressSlider.slider = null;
+ return this.progressSlider.play();
+ });
+ it('build the slider', function() {
+ expect(this.progressSlider.slider).toBe('.slider');
+ return expect($.fn.slider).toHaveBeenCalledWith({
+ range: 'min',
+ change: this.progressSlider.onChange,
+ slide: this.progressSlider.onSlide,
+ stop: this.progressSlider.onStop
+ });
+ });
+ return it('build the seek handle', function() {
+ expect(this.progressSlider.handle).toBe('.ui-slider-handle');
+ return expect($.fn.qtip).toHaveBeenCalledWith({
+ content: "0:00",
+ position: {
+ my: 'bottom center',
+ at: 'top center',
+ container: this.progressSlider.handle
+ },
+ hide: {
+ delay: 700
+ },
+ style: {
+ classes: 'ui-tooltip-slider',
+ widget: true
+ }
+ });
+ });
+ });
+ });
+ describe('updatePlayTime', function() {
+ beforeEach(function() {
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ return this.progressSlider = this.player.progressSlider;
+ });
+ describe('when frozen', function() {
+ beforeEach(function() {
+ spyOn($.fn, 'slider').andCallThrough();
+ this.progressSlider.frozen = true;
+ return this.progressSlider.updatePlayTime(20, 120);
+ });
+ return it('does not update the slider', function() {
+ return expect($.fn.slider).not.toHaveBeenCalled();
+ });
+ });
+ return describe('when not frozen', function() {
+ beforeEach(function() {
+ spyOn($.fn, 'slider').andCallThrough();
+ this.progressSlider.frozen = false;
+ return this.progressSlider.updatePlayTime(20, 120);
+ });
+ it('update the max value of the slider', function() {
+ return expect($.fn.slider).toHaveBeenCalledWith('option', 'max', 120);
+ });
+ return it('update current value of the slider', function() {
+ return expect($.fn.slider).toHaveBeenCalledWith('value', 20);
+ });
+ });
+ });
+ describe('onSlide', function() {
+ beforeEach(function() {
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ this.progressSlider = this.player.progressSlider;
+ spyOnEvent(this.progressSlider, 'slide_seek');
+ return this.progressSlider.onSlide({}, {
+ value: 20
+ });
+ });
+ it('freeze the slider', function() {
+ return expect(this.progressSlider.frozen).toBeTruthy();
+ });
+ it('update the tooltip', function() {
+ return expect($.fn.qtip).toHaveBeenCalled();
+ });
+ return it('trigger seek event', function() {
+ expect('slide_seek').toHaveBeenTriggeredOn(this.progressSlider);
+ return expect(this.player.currentTime).toEqual(20);
+ });
+ });
+ describe('onChange', function() {
+ beforeEach(function() {
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ this.progressSlider = this.player.progressSlider;
+ return this.progressSlider.onChange({}, {
+ value: 20
+ });
+ });
+ return it('update the tooltip', function() {
+ return expect($.fn.qtip).toHaveBeenCalled();
+ });
+ });
+ describe('onStop', function() {
+ beforeEach(function() {
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ this.progressSlider = this.player.progressSlider;
+ spyOnEvent(this.progressSlider, 'slide_seek');
+ return this.progressSlider.onStop({}, {
+ value: 20
+ });
+ });
+ it('freeze the slider', function() {
+ return expect(this.progressSlider.frozen).toBeTruthy();
+ });
+ it('trigger seek event', function() {
+ expect('slide_seek').toHaveBeenTriggeredOn(this.progressSlider);
+ return expect(this.player.currentTime).toEqual(20);
+ });
+ return it('set timeout to unfreeze the slider', function() {
+ expect(window.setTimeout).toHaveBeenCalledWith(jasmine.any(Function), 200);
+ window.setTimeout.mostRecentCall.args[0]();
+ return expect(this.progressSlider.frozen).toBeFalsy();
+ });
+ });
+ return describe('updateTooltip', function() {
+ beforeEach(function() {
+ this.player = jasmine.stubVideoPlayerAlpha(this);
+ this.progressSlider = this.player.progressSlider;
+ return this.progressSlider.updateTooltip(90);
+ });
+ return it('set the tooltip value', function() {
+ return expect($.fn.qtip).toHaveBeenCalledWith('option', 'content.text', '1:30');
+ });
+ });
+ });
+
+}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_speed_control_spec.coffee b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_speed_control_spec.coffee
deleted file mode 100644
index ca4bfe815a..0000000000
--- a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_speed_control_spec.coffee
+++ /dev/null
@@ -1,91 +0,0 @@
-describe 'VideoSpeedControlAlpha', ->
- beforeEach ->
- window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn false
- jasmine.stubVideoPlayerAlpha @
- $('.speeds').remove()
-
- describe 'constructor', ->
- describe 'always', ->
- beforeEach ->
- @speedControl = new VideoSpeedControlAlpha el: $('.secondary-controls'), speeds: @video.speeds, currentSpeed: '1.0'
-
- it 'add the video speed control to player', ->
- 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', @speedControl.currentSpeed
- expect(li.length).toBe @speedControl.speeds.length
- $.each li.toArray().reverse(), (index, link) =>
- expect($(link)).toHaveData 'speed', @speedControl.speeds[index]
- expect($(link).find('a').text()).toBe @speedControl.speeds[index] + 'x'
-
- it 'bind to change video speed link', ->
- expect($('.video_speeds a')).toHandleWith 'click', @speedControl.changeVideoSpeed
-
- describe 'when running on touch based device', ->
- beforeEach ->
- window.onTouchBasedDevice.andReturn true
- $('.speeds').removeClass 'open'
- @speedControl = new VideoSpeedControlAlpha el: $('.secondary-controls'), speeds: @video.speeds, currentSpeed: '1.0'
-
- it 'open the speed toggle on click', ->
- $('.speeds').click()
- expect($('.speeds')).toHaveClass 'open'
- $('.speeds').click()
- expect($('.speeds')).not.toHaveClass 'open'
-
- describe 'when running on non-touch based device', ->
- beforeEach ->
- $('.speeds').removeClass 'open'
- @speedControl = new VideoSpeedControlAlpha el: $('.secondary-controls'), speeds: @video.speeds, currentSpeed: '1.0'
-
- it 'open the speed toggle on hover', ->
- $('.speeds').mouseenter()
- expect($('.speeds')).toHaveClass 'open'
- $('.speeds').mouseleave()
- expect($('.speeds')).not.toHaveClass 'open'
-
- it 'close the speed toggle on mouse out', ->
- $('.speeds').mouseenter().mouseleave()
- expect($('.speeds')).not.toHaveClass 'open'
-
- it 'close the speed toggle on click', ->
- $('.speeds').mouseenter().click()
- expect($('.speeds')).not.toHaveClass 'open'
-
- describe 'changeVideoSpeed', ->
- beforeEach ->
- @speedControl = new VideoSpeedControlAlpha el: $('.secondary-controls'), speeds: @video.speeds, currentSpeed: '1.0'
- @video.setSpeed '1.0'
-
- describe 'when new speed is the same', ->
- beforeEach ->
- spyOnEvent @speedControl, 'speedChange'
- $('li[data-speed="1.0"] a').click()
-
- it 'does not trigger speedChange event', ->
- expect('speedChange').not.toHaveBeenTriggeredOn @speedControl
-
- describe 'when new speed is not the same', ->
- beforeEach ->
- @newSpeed = null
- $(@speedControl).bind 'speedChange', (event, newSpeed) => @newSpeed = newSpeed
- spyOnEvent @speedControl, 'speedChange'
- $('li[data-speed="0.75"] a').click()
-
- it 'trigger speedChange event', ->
- expect('speedChange').toHaveBeenTriggeredOn @speedControl
- expect(@newSpeed).toEqual 0.75
-
- describe 'onSpeedChange', ->
- beforeEach ->
- @speedControl = new VideoSpeedControlAlpha el: $('.secondary-controls'), speeds: @video.speeds, currentSpeed: '1.0'
- $('li[data-speed="1.0"] a').addClass 'active'
- @speedControl.setSpeed '0.75'
-
- it 'set the new speed as active', ->
- 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'
diff --git a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_speed_control_spec.js b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_speed_control_spec.js
new file mode 100644
index 0000000000..e31c31fcc8
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_speed_control_spec.js
@@ -0,0 +1,131 @@
+// Generated by CoffeeScript 1.6.3
+(function() {
+ xdescribe('VideoSpeedControlAlpha', function() {
+ beforeEach(function() {
+ window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn(false);
+ jasmine.stubVideoPlayerAlpha(this);
+ return $('.speeds').remove();
+ });
+ describe('constructor', function() {
+ describe('always', function() {
+ beforeEach(function() {
+ return this.speedControl = new VideoSpeedControlAlpha({
+ el: $('.secondary-controls'),
+ speeds: this.video.speeds,
+ currentSpeed: '1.0'
+ });
+ });
+ it('add the video speed control to player', function() {
+ var li, secondaryControls,
+ _this = this;
+ 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', this.speedControl.currentSpeed);
+ expect(li.length).toBe(this.speedControl.speeds.length);
+ return $.each(li.toArray().reverse(), function(index, link) {
+ expect($(link)).toHaveData('speed', _this.speedControl.speeds[index]);
+ return expect($(link).find('a').text()).toBe(_this.speedControl.speeds[index] + 'x');
+ });
+ });
+ return it('bind to change video speed link', function() {
+ return expect($('.video_speeds a')).toHandleWith('click', this.speedControl.changeVideoSpeed);
+ });
+ });
+ describe('when running on touch based device', function() {
+ beforeEach(function() {
+ window.onTouchBasedDevice.andReturn(true);
+ $('.speeds').removeClass('open');
+ return this.speedControl = new VideoSpeedControlAlpha({
+ el: $('.secondary-controls'),
+ speeds: this.video.speeds,
+ currentSpeed: '1.0'
+ });
+ });
+ return it('open the speed toggle on click', function() {
+ $('.speeds').click();
+ expect($('.speeds')).toHaveClass('open');
+ $('.speeds').click();
+ return expect($('.speeds')).not.toHaveClass('open');
+ });
+ });
+ return describe('when running on non-touch based device', function() {
+ beforeEach(function() {
+ $('.speeds').removeClass('open');
+ return this.speedControl = new VideoSpeedControlAlpha({
+ el: $('.secondary-controls'),
+ speeds: this.video.speeds,
+ currentSpeed: '1.0'
+ });
+ });
+ it('open the speed toggle on hover', function() {
+ $('.speeds').mouseenter();
+ expect($('.speeds')).toHaveClass('open');
+ $('.speeds').mouseleave();
+ return expect($('.speeds')).not.toHaveClass('open');
+ });
+ it('close the speed toggle on mouse out', function() {
+ $('.speeds').mouseenter().mouseleave();
+ return expect($('.speeds')).not.toHaveClass('open');
+ });
+ return it('close the speed toggle on click', function() {
+ $('.speeds').mouseenter().click();
+ return expect($('.speeds')).not.toHaveClass('open');
+ });
+ });
+ });
+ describe('changeVideoSpeed', function() {
+ beforeEach(function() {
+ this.speedControl = new VideoSpeedControlAlpha({
+ el: $('.secondary-controls'),
+ speeds: this.video.speeds,
+ currentSpeed: '1.0'
+ });
+ return this.video.setSpeed('1.0');
+ });
+ describe('when new speed is the same', function() {
+ beforeEach(function() {
+ spyOnEvent(this.speedControl, 'speedChange');
+ return $('li[data-speed="1.0"] a').click();
+ });
+ return it('does not trigger speedChange event', function() {
+ return expect('speedChange').not.toHaveBeenTriggeredOn(this.speedControl);
+ });
+ });
+ return describe('when new speed is not the same', function() {
+ beforeEach(function() {
+ var _this = this;
+ this.newSpeed = null;
+ $(this.speedControl).bind('speedChange', function(event, newSpeed) {
+ return _this.newSpeed = newSpeed;
+ });
+ spyOnEvent(this.speedControl, 'speedChange');
+ return $('li[data-speed="0.75"] a').click();
+ });
+ return it('trigger speedChange event', function() {
+ expect('speedChange').toHaveBeenTriggeredOn(this.speedControl);
+ return expect(this.newSpeed).toEqual(0.75);
+ });
+ });
+ });
+ return describe('onSpeedChange', function() {
+ beforeEach(function() {
+ this.speedControl = new VideoSpeedControlAlpha({
+ el: $('.secondary-controls'),
+ speeds: this.video.speeds,
+ currentSpeed: '1.0'
+ });
+ $('li[data-speed="1.0"] a').addClass('active');
+ return this.speedControl.setSpeed('0.75');
+ });
+ return 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');
+ return expect($('.speeds p.active')).toHaveHtml('0.75x');
+ });
+ });
+ });
+
+}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_volume_control_spec.coffee b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_volume_control_spec.coffee
deleted file mode 100644
index 4bb9f1cbf8..0000000000
--- a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_volume_control_spec.coffee
+++ /dev/null
@@ -1,94 +0,0 @@
-describe 'VideoVolumeControlAlpha', ->
- beforeEach ->
- jasmine.stubVideoPlayerAlpha @
- $('.volume').remove()
-
- describe 'constructor', ->
- beforeEach ->
- spyOn($.fn, 'slider')
- @volumeControl = new VideoVolumeControlAlpha el: $('.secondary-controls')
-
- it 'initialize currentVolume to 100', ->
- expect(@volumeControl.currentVolume).toEqual 100
-
- it 'render the volume control', ->
- expect($('.secondary-controls').html()).toContain """
-
- """
-
- it 'create the slider', ->
- expect($.fn.slider).toHaveBeenCalledWith
- orientation: "vertical"
- range: "min"
- min: 0
- max: 100
- value: 100
- change: @volumeControl.onChange
- slide: @volumeControl.onChange
-
- it 'bind the volume control', ->
- expect($('.volume>a')).toHandleWith 'click', @volumeControl.toggleMute
-
- expect($('.volume')).not.toHaveClass 'open'
- $('.volume').mouseenter()
- expect($('.volume')).toHaveClass 'open'
- $('.volume').mouseleave()
- expect($('.volume')).not.toHaveClass 'open'
-
- describe 'onChange', ->
- beforeEach ->
- spyOnEvent @volumeControl, 'volumeChange'
- @newVolume = undefined
- @volumeControl = new VideoVolumeControlAlpha el: $('.secondary-controls')
- $(@volumeControl).bind 'volumeChange', (event, volume) => @newVolume = volume
-
- describe 'when the new volume is more than 0', ->
- beforeEach ->
- @volumeControl.onChange undefined, value: 60
-
- it 'set the player volume', ->
- expect(@newVolume).toEqual 60
-
- it 'remote muted class', ->
- expect($('.volume')).not.toHaveClass 'muted'
-
- describe 'when the new volume is 0', ->
- beforeEach ->
- @volumeControl.onChange undefined, value: 0
-
- it 'set the player volume', ->
- expect(@newVolume).toEqual 0
-
- it 'add muted class', ->
- expect($('.volume')).toHaveClass 'muted'
-
- describe 'toggleMute', ->
- beforeEach ->
- @newVolume = undefined
- @volumeControl = new VideoVolumeControlAlpha el: $('.secondary-controls')
- $(@volumeControl).bind 'volumeChange', (event, volume) => @newVolume = volume
-
- describe 'when the current volume is more than 0', ->
- beforeEach ->
- @volumeControl.currentVolume = 60
- @volumeControl.toggleMute()
-
- it 'save the previous volume', ->
- expect(@volumeControl.previousVolume).toEqual 60
-
- it 'set the player volume', ->
- expect(@newVolume).toEqual 0
-
- describe 'when the current volume is 0', ->
- beforeEach ->
- @volumeControl.currentVolume = 0
- @volumeControl.previousVolume = 60
- @volumeControl.toggleMute()
-
- it 'set the player volume to previous volume', ->
- expect(@newVolume).toEqual 60
diff --git a/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_volume_control_spec.js b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_volume_control_spec.js
new file mode 100644
index 0000000000..dcc4485f2a
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_volume_control_spec.js
@@ -0,0 +1,113 @@
+(function() {
+ describe('VideoVolumeControlAlpha', function() {
+ var state, player;
+
+ describe('constructor', function() {
+ beforeEach(function() {
+ // spyOn($.fn, 'slider');
+ state = jasmine.stubVideoPlayerAlpha(this);
+ player = state.videoPlayer.player;
+ });
+
+ it('initialize currentVolume to 100', function() {
+ return expect(state.videoVolumeControl.currentVolume).toEqual(1);
+ });
+ it('render the volume control', function() {
+ return expect($('.secondary-controls').html()).toContain("
");
+ });
+ it('create the slider', function() {
+ return expect($.fn.slider).toHaveBeenCalledWith({
+ orientation: "vertical",
+ range: "min",
+ min: 0,
+ max: 100,
+ value: 100,
+ change: this.volumeControl.onChange,
+ slide: this.volumeControl.onChange
+ });
+ });
+ return it('bind the volume control', function() {
+ expect($('.volume>a')).toHandleWith('click', this.volumeControl.toggleMute);
+ expect($('.volume')).not.toHaveClass('open');
+ $('.volume').mouseenter();
+ expect($('.volume')).toHaveClass('open');
+ $('.volume').mouseleave();
+ return expect($('.volume')).not.toHaveClass('open');
+ });
+ });
+ describe('onChange', function() {
+ beforeEach(function() {
+ var _this = this;
+ spyOnEvent(this.volumeControl, 'volumeChange');
+ this.newVolume = void 0;
+ this.volumeControl = new VideoVolumeControlAlpha({
+ el: $('.secondary-controls')
+ });
+ return $(this.volumeControl).bind('volumeChange', function(event, volume) {
+ return _this.newVolume = volume;
+ });
+ });
+ describe('when the new volume is more than 0', function() {
+ beforeEach(function() {
+ return this.volumeControl.onChange(void 0, {
+ value: 60
+ });
+ });
+ it('set the player volume', function() {
+ return expect(this.newVolume).toEqual(60);
+ });
+ return it('remote muted class', function() {
+ return expect($('.volume')).not.toHaveClass('muted');
+ });
+ });
+ return describe('when the new volume is 0', function() {
+ beforeEach(function() {
+ return this.volumeControl.onChange(void 0, {
+ value: 0
+ });
+ });
+ it('set the player volume', function() {
+ return expect(this.newVolume).toEqual(0);
+ });
+ return it('add muted class', function() {
+ return expect($('.volume')).toHaveClass('muted');
+ });
+ });
+ });
+ return describe('toggleMute', function() {
+ beforeEach(function() {
+ var _this = this;
+ this.newVolume = void 0;
+ this.volumeControl = new VideoVolumeControlAlpha({
+ el: $('.secondary-controls')
+ });
+ return $(this.volumeControl).bind('volumeChange', function(event, volume) {
+ return _this.newVolume = volume;
+ });
+ });
+ describe('when the current volume is more than 0', function() {
+ beforeEach(function() {
+ this.volumeControl.currentVolume = 60;
+ return this.volumeControl.toggleMute();
+ });
+ it('save the previous volume', function() {
+ return expect(this.volumeControl.previousVolume).toEqual(60);
+ });
+ return it('set the player volume', function() {
+ return expect(this.newVolume).toEqual(0);
+ });
+ });
+ return describe('when the current volume is 0', function() {
+ beforeEach(function() {
+ this.volumeControl.currentVolume = 0;
+ this.volumeControl.previousVolume = 60;
+ return this.volumeControl.toggleMute();
+ });
+ return it('set the player volume to previous volume', function() {
+ return expect(this.newVolume).toEqual(60);
+ });
+ });
+ });
+ });
+
+}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/videoalpha/display_spec.coffee b/common/lib/xmodule/xmodule/js/spec/videoalpha/display_spec.coffee
deleted file mode 100644
index 3715c3d813..0000000000
--- a/common/lib/xmodule/xmodule/js/spec/videoalpha/display_spec.coffee
+++ /dev/null
@@ -1,286 +0,0 @@
-describe 'VideoAlpha', ->
- metadata =
- slowerSpeedYoutubeId:
- id: @slowerSpeedYoutubeId
- duration: 300
- normalSpeedYoutubeId:
- id: @normalSpeedYoutubeId
- duration: 200
-
- beforeEach ->
- jasmine.stubRequests()
- window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn false
- @videosDefinition = '0.75:slowerSpeedYoutubeId,1.0:normalSpeedYoutubeId'
- @slowerSpeedYoutubeId = 'slowerSpeedYoutubeId'
- @normalSpeedYoutubeId = 'normalSpeedYoutubeId'
-
- afterEach ->
- window.OldVideoPlayerAlpha = undefined
- window.onYouTubePlayerAPIReady = undefined
- window.onHTML5PlayerAPIReady = undefined
-
- describe 'constructor', ->
- describe 'YT', ->
- beforeEach ->
- loadFixtures 'videoalpha.html'
- @stubVideoPlayerAlpha = jasmine.createSpy('VideoPlayerAlpha')
- $.cookie.andReturn '0.75'
-
- describe 'by default', ->
- beforeEach ->
- spyOn(window.VideoAlpha.prototype, 'fetchMetadata').andCallFake ->
- @metadata = metadata
- @video = new VideoAlpha '#example', @videosDefinition
-
- it 'check videoType', ->
- expect(@video.videoType).toEqual('youtube')
-
- it 'reset the current video player', ->
- expect(window.OldVideoPlayerAlpha).toBeUndefined()
-
- it 'set the elements', ->
- expect(@video.el).toBe '#video_id'
-
- it 'parse the videos', ->
- expect(@video.videos).toEqual
- '0.75': @slowerSpeedYoutubeId
- '1.0': @normalSpeedYoutubeId
-
- it 'fetch the video metadata', ->
- expect(@video.fetchMetadata).toHaveBeenCalled
- expect(@video.metadata).toEqual metadata
-
- it 'parse available video speeds', ->
- expect(@video.speeds).toEqual ['0.75', '1.0']
-
- it 'set current video speed via cookie', ->
- expect(@video.speed).toEqual '0.75'
-
- it 'store a reference for this video player in the element', ->
- expect($('.video').data('video')).toEqual @video
-
- describe 'when the Youtube API is already available', ->
- beforeEach ->
- @originalYT = window.YT
- window.YT = { Player: true }
- spyOn(window, 'VideoPlayerAlpha').andReturn(@stubVideoPlayerAlpha)
- @video = new VideoAlpha '#example', @videosDefinition
-
- afterEach ->
- window.YT = @originalYT
-
- it 'create the Video Player', ->
- expect(window.VideoPlayerAlpha).toHaveBeenCalledWith(video: @video)
- expect(@video.player).toEqual @stubVideoPlayerAlpha
-
- describe 'when the Youtube API is not ready', ->
- beforeEach ->
- @originalYT = window.YT
- window.YT = {}
- @video = new VideoAlpha '#example', @videosDefinition
-
- afterEach ->
- window.YT = @originalYT
-
- it 'set the callback on the window object', ->
- expect(window.onYouTubePlayerAPIReady).toEqual jasmine.any(Function)
-
- describe 'when the Youtube API becoming ready', ->
- beforeEach ->
- @originalYT = window.YT
- window.YT = {}
- spyOn(window, 'VideoPlayerAlpha').andReturn(@stubVideoPlayerAlpha)
- @video = new VideoAlpha '#example', @videosDefinition
- window.onYouTubePlayerAPIReady()
-
- afterEach ->
- window.YT = @originalYT
-
- it 'create the Video Player for all video elements', ->
- expect(window.VideoPlayerAlpha).toHaveBeenCalledWith(video: @video)
- expect(@video.player).toEqual @stubVideoPlayerAlpha
-
- describe 'HTML5', ->
- beforeEach ->
- loadFixtures 'videoalpha_html5.html'
- @stubVideoPlayerAlpha = jasmine.createSpy('VideoPlayerAlpha')
- $.cookie.andReturn '0.75'
-
- describe 'by default', ->
- beforeEach ->
- @originalHTML5 = window.HTML5Video.Player
- window.HTML5Video.Player = undefined
- @video = new VideoAlpha '#example', @videosDefinition
-
- afterEach ->
- window.HTML5Video.Player = @originalHTML5
-
- it 'check videoType', ->
- expect(@video.videoType).toEqual('html5')
-
- it 'reset the current video player', ->
- expect(window.OldVideoPlayerAlpha).toBeUndefined()
-
- it 'set the elements', ->
- expect(@video.el).toBe '#video_id'
-
- it 'parse the videos if subtitles exist', ->
- sub = 'test_name_of_the_subtitles'
- expect(@video.videos).toEqual
- '0.75': sub
- '1.0': sub
- '1.25': sub
- '1.5': sub
-
- it 'parse the videos if subtitles doesn\'t exist', ->
- $('#example').find('.video').data('sub', '')
- @video = new VideoAlpha '#example', @videosDefinition
- sub = ''
- expect(@video.videos).toEqual
- '0.75': sub
- '1.0': sub
- '1.25': sub
- '1.5': sub
-
- it 'parse Html5 sources', ->
- html5Sources =
- mp4: 'test.mp4'
- webm: 'test.webm'
- ogg: 'test.ogv'
- expect(@video.html5Sources).toEqual html5Sources
-
- it 'parse available video speeds', ->
- speeds = jasmine.stubbedHtml5Speeds
- expect(@video.speeds).toEqual speeds
-
- it 'set current video speed via cookie', ->
- expect(@video.speed).toEqual '0.75'
-
- it 'store a reference for this video player in the element', ->
- expect($('.video').data('video')).toEqual @video
-
- describe 'when the HTML5 API is already available', ->
- beforeEach ->
- @originalHTML5Video = window.HTML5Video
- window.HTML5Video = { Player: true }
- spyOn(window, 'VideoPlayerAlpha').andReturn(@stubVideoPlayerAlpha)
- @video = new VideoAlpha '#example', @videosDefinition
-
- afterEach ->
- window.HTML5Video = @originalHTML5Video
-
- it 'create the Video Player', ->
- expect(window.VideoPlayerAlpha).toHaveBeenCalledWith(video: @video)
- expect(@video.player).toEqual @stubVideoPlayerAlpha
-
- describe 'when the HTML5 API is not ready', ->
- beforeEach ->
- @originalHTML5Video = window.HTML5Video
- window.HTML5Video = {}
- @video = new VideoAlpha '#example', @videosDefinition
-
- afterEach ->
- window.HTML5Video = @originalHTML5Video
-
- it 'set the callback on the window object', ->
- expect(window.onHTML5PlayerAPIReady).toEqual jasmine.any(Function)
-
- describe 'when the HTML5 API becoming ready', ->
- beforeEach ->
- @originalHTML5Video = window.HTML5Video
- window.HTML5Video = {}
- spyOn(window, 'VideoPlayerAlpha').andReturn(@stubVideoPlayerAlpha)
- @video = new VideoAlpha '#example', @videosDefinition
- window.onHTML5PlayerAPIReady()
-
- afterEach ->
- window.HTML5Video = @originalHTML5Video
-
- it 'create the Video Player for all video elements', ->
- expect(window.VideoPlayerAlpha).toHaveBeenCalledWith(video: @video)
- expect(@video.player).toEqual @stubVideoPlayerAlpha
-
- describe 'youtubeId', ->
- beforeEach ->
- loadFixtures 'videoalpha.html'
- $.cookie.andReturn '1.0'
- @video = new VideoAlpha '#example', @videosDefinition
-
- describe 'with speed', ->
- it 'return the video id for given speed', ->
- expect(@video.youtubeId('0.75')).toEqual @slowerSpeedYoutubeId
- expect(@video.youtubeId('1.0')).toEqual @normalSpeedYoutubeId
-
- describe 'without speed', ->
- it 'return the video id for current speed', ->
- expect(@video.youtubeId()).toEqual @normalSpeedYoutubeId
-
- describe 'setSpeed', ->
- describe 'YT', ->
- beforeEach ->
- loadFixtures 'videoalpha.html'
- @video = new VideoAlpha '#example', @videosDefinition
-
- describe 'when new speed is available', ->
- beforeEach ->
- @video.setSpeed '0.75'
-
- it 'set new speed', ->
- expect(@video.speed).toEqual '0.75'
-
- it 'save setting for new speed', ->
- expect($.cookie).toHaveBeenCalledWith 'video_speed', '0.75', expires: 3650, path: '/'
-
- describe 'when new speed is not available', ->
- beforeEach ->
- @video.setSpeed '1.75'
-
- it 'set speed to 1.0x', ->
- expect(@video.speed).toEqual '1.0'
-
- describe 'HTML5', ->
- beforeEach ->
- loadFixtures 'videoalpha_html5.html'
- @video = new VideoAlpha '#example', @videosDefinition
-
- describe 'when new speed is available', ->
- beforeEach ->
- @video.setSpeed '0.75'
-
- it 'set new speed', ->
- expect(@video.speed).toEqual '0.75'
-
- it 'save setting for new speed', ->
- expect($.cookie).toHaveBeenCalledWith 'video_speed', '0.75', expires: 3650, path: '/'
-
- describe 'when new speed is not available', ->
- beforeEach ->
- @video.setSpeed '1.75'
-
- it 'set speed to 1.0x', ->
- expect(@video.speed).toEqual '1.0'
-
- describe 'getDuration', ->
- beforeEach ->
- loadFixtures 'videoalpha.html'
- @video = new VideoAlpha '#example', @videosDefinition
-
- it 'return duration for current video', ->
- expect(@video.getDuration()).toEqual 200
-
- describe 'log', ->
- beforeEach ->
- loadFixtures 'videoalpha.html'
- @video = new VideoAlpha '#example', @videosDefinition
- spyOn Logger, 'log'
- @video.log 'someEvent', {
- currentTime: 25,
- speed: '1.0'
- }
-
- it 'call the logger with valid extra parameters', ->
- expect(Logger.log).toHaveBeenCalledWith 'someEvent',
- id: 'id'
- code: @normalSpeedYoutubeId
- currentTime: 25
- speed: '1.0'
diff --git a/common/lib/xmodule/xmodule/js/spec/videoalpha/display_spec.js b/common/lib/xmodule/xmodule/js/spec/videoalpha/display_spec.js
new file mode 100644
index 0000000000..e0a8854f04
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/videoalpha/display_spec.js
@@ -0,0 +1,392 @@
+(function () {
+ describe('VideoAlpha', function () {
+ var metadata;
+ metadata = {
+ slowerSpeedYoutubeId: {
+ id: this.slowerSpeedYoutubeId,
+ duration: 300
+ },
+ normalSpeedYoutubeId: {
+ id: this.normalSpeedYoutubeId,
+ duration: 200
+ }
+ };
+
+ beforeEach(function () {
+ jasmine.stubRequests();
+ window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn(false);
+ this.videosDefinition = '0.75:slowerSpeedYoutubeId,1.0:normalSpeedYoutubeId';
+ this.slowerSpeedYoutubeId = 'slowerSpeedYoutubeId';
+ this.normalSpeedYoutubeId = 'normalSpeedYoutubeId';
+ });
+
+ afterEach(function () {
+ window.OldVideoPlayerAlpha = void 0;
+ window.onYouTubePlayerAPIReady = void 0;
+ window.onHTML5PlayerAPIReady = void 0;
+ });
+
+ describe('constructor', function () {
+ describe('YT', function () {
+ beforeEach(function () {
+ loadFixtures('videoalpha.html');
+ // TO DO??? this.stubbedVideoPlayer = jasmine.createSpy('StubbedVideoPlayer');
+ $.cookie.andReturn('0.75');
+ });
+
+ describe('by default', function () {
+ beforeEach(function () {
+ this.state = new window.VideoAlpha('#example');
+ });
+
+ it('check videoType', function () {
+ expect(this.state.videoType).toEqual('youtube');
+ });
+
+ it('reset the current video player', function () {
+ expect(window.OldVideoPlayerAlpha).toBeUndefined();
+ });
+
+ it('set the elements', function () {
+ console.log('We are in this function.');
+
+ expect(this.state.el).toBe('#video_id');
+ });
+
+ it('parse the videos', function () {
+ expect(this.state.videos).toEqual({
+ '0.75': this.slowerSpeedYoutubeId,
+ '1.0': this.normalSpeedYoutubeId
+ });
+ });
+
+ it('parse available video speeds', function () {
+ expect(this.state.speeds).toEqual(['0.75', '1.0']);
+ });
+
+ it('set current video speed via cookie', function () {
+ expect(this.state.speed).toEqual('0.75');
+ });
+
+ //it('store a reference for this video player in the element', function () {
+ //expect($('.video').data('video')).toEqual(this.state);
+ //});
+ });
+
+ /*describe('when the Youtube API is already available', function () {
+ beforeEach(function () {
+ this.originalYT = window.YT;
+ window.YT = {
+ Player: true
+ };
+ this.state = new window.VideoAlpha('#example');
+ });
+
+ afterEach(function () {
+ window.YT = this.originalYT;
+ });
+
+ it('create the Video Player', function () {
+ expect(window.VideoPlayerAlpha).toHaveBeenCalledWith({
+ video: this.video
+ });
+ expect(this.video.player).toEqual(this.stubbedVideoPlayer);
+ });
+ });
+
+ describe('when the Youtube API is not ready', function () {
+ beforeEach(function () {
+ this.originalYT = window.YT;
+ window.YT = {};
+ this.video = new VideoAlpha('#example');
+ });
+
+ afterEach(function () {
+ window.YT = this.originalYT;
+ });
+
+ it('set the callback on the window object', function () {
+ expect(window.onYouTubePlayerAPIReady).toEqual(jasmine.any(Function));
+ });
+ });
+
+ describe('when the Youtube API becoming ready', function () {
+ beforeEach(function () {
+ this.originalYT = window.YT;
+ window.YT = {};
+ spyOn(window, 'VideoPlayerAlpha').andReturn(this.stubVideoPlayerAlpha);
+ this.video = new VideoAlpha('#example');
+ window.onYouTubePlayerAPIReady();
+ });
+
+ afterEach(function () {
+ window.YT = this.originalYT;
+ });
+
+ it('create the Video Player for all video elements', function () {
+ expect(window.VideoPlayerAlpha).toHaveBeenCalledWith({
+ video: this.video
+ });
+ expect(this.video.player).toEqual(this.stubVideoPlayerAlpha);
+ });
+ });*/
+ });
+
+ describe('HTML5', function () {
+ var state;
+
+ beforeEach(function () {
+ loadFixtures('videoalpha_html5.html');
+ this.stubVideoPlayerAlpha = jasmine.createSpy('VideoPlayerAlpha');
+ $.cookie.andReturn('0.75');
+ });
+
+ describe('by default', function () {
+ beforeEach(function () {
+ state = new window.VideoAlpha('#example');
+ });
+
+ afterEach(function () {
+ state = void 0;
+ });
+
+ it('check videoType', function () {
+ expect(state.videoType).toEqual('html5');
+ });
+
+ it('reset the current video player', function () {
+ expect(window.OldVideoPlayerAlpha).toBeUndefined();
+ });
+
+ it('set the elements', function () {
+ expect(state.el).toBe('#video_id');
+ });
+
+ it('parse the videos if subtitles exist', function () {
+ var sub;
+ sub = 'test_name_of_the_subtitles';
+ expect(state.videos).toEqual({
+ '0.75': sub,
+ '1.0': sub,
+ '1.25': sub,
+ '1.5': sub
+ });
+ });
+
+ it('parse the videos if subtitles do not exist', function () {
+ var sub;
+ $('#example').find('.videoalpha').data('sub', '');
+ state = new window.VideoAlpha('#example');
+ sub = '';
+ expect(state.videos).toEqual({
+ '0.75': sub,
+ '1.0': sub,
+ '1.25': sub,
+ '1.5': sub
+ });
+ });
+
+ it('parse Html5 sources', function () {
+ var html5Sources;
+ html5Sources = {
+ mp4: 'test.mp4',
+ webm: 'test.webm',
+ ogg: 'test.ogv'
+ };
+ expect(state.html5Sources).toEqual(html5Sources);
+ });
+
+ it('parse available video speeds', function () {
+ var speeds;
+ speeds = jasmine.stubbedHtml5Speeds;
+ expect(state.speeds).toEqual(speeds);
+ });
+
+ it('set current video speed via cookie', function () {
+ expect(state.speed).toEqual('0.75');
+ });
+ });
+
+ // Note that the loading of stand alone HTML5 player API is handled by
+ // Require JS. When state.videoPlayer is created, the stand alone HTML5
+ // player object is already loaded, so no further testing in that case
+ // is required.
+ describe('HTML5 API is available', function () {
+ beforeEach(function () {
+ //TO DO??? spyOn(window, 'VideoAlpha').andReturn(jasmine.stubbedState);
+ state = new VideoAlpha('#example');
+ });
+
+ afterEach(function () {
+ state = null;
+ });
+
+ it('create the Video Player', function () {
+ expect(state.videoPlayer.player).not.toBeUndefined();
+ });
+ });
+
+ /* NOT NECESSARY??? describe('when the HTML5 API is not ready', function () {
+ beforeEach(function () {
+ this.originalHTML5Video = window.HTML5Video;
+ window.HTML5Video = {};
+ state = new VideoAlpha('#example');
+ });
+
+ afterEach(function () {
+ window.HTML5Video = this.originalHTML5Video;
+ });
+
+ it('set the callback on the window object', function () {
+ expect(window.onHTML5PlayerAPIReady).toEqual(jasmine.any(Function));
+ });
+ });
+
+ describe('when the HTML5 API becoming ready', function () {
+ beforeEach(function () {
+ this.originalHTML5Video = window.HTML5Video;
+ window.HTML5Video = {};
+ spyOn(window, 'VideoPlayerAlpha').andReturn(this.stubVideoPlayerAlpha);
+ state = new VideoAlpha('#example');
+ window.onHTML5PlayerAPIReady();
+ });
+
+ afterEach(function () {
+ window.HTML5Video = this.originalHTML5Video;
+ });
+
+ it('create the Video Player for all video elements', function () {
+ expect(window.VideoPlayerAlpha).toHaveBeenCalledWith({
+ video: this.video
+ });
+ expect(this.video.player).toEqual(this.stubVideoPlayerAlpha);
+ });
+ });*/
+ });
+ });
+
+ describe('youtubeId', function () {
+ beforeEach(function () {
+ loadFixtures('videoalpha.html');
+ $.cookie.andReturn('1.0');
+ state = new VideoAlpha('#example');
+ });
+
+ describe('with speed', function () {
+ it('return the video id for given speed', function () {
+ expect(state.youtubeId('0.75')).toEqual(this.slowerSpeedYoutubeId);
+ expect(state.youtubeId('1.0')).toEqual(this.normalSpeedYoutubeId);
+ });
+ });
+
+ describe('without speed', function () {
+ it('return the video id for current speed', function () {
+ expect(state.youtubeId()).toEqual(this.normalSpeedYoutubeId);
+ });
+ });
+ });
+
+ describe('setSpeed', function () {
+ describe('YT', function () {
+ beforeEach(function () {
+ loadFixtures('videoalpha.html');
+ state = new VideoAlpha('#example');
+ });
+
+ describe('when new speed is available', function () {
+ beforeEach(function () {
+ state.setSpeed('0.75');
+ });
+
+ it('set new speed', function () {
+ expect(state.speed).toEqual('0.75');
+ });
+
+ it('save setting for new speed', function () {
+ expect($.cookie).toHaveBeenCalledWith('video_speed', '0.75', {
+ expires: 3650,
+ path: '/'
+ });
+ });
+ });
+
+ describe('when new speed is not available', function () {
+ beforeEach(function () {
+ state.setSpeed('1.75');
+ });
+
+ it('set speed to 1.0x', function () {
+ expect(state.speed).toEqual('1.0');
+ });
+ });
+ });
+
+ describe('HTML5', function () {
+ beforeEach(function () {
+ loadFixtures('videoalpha_html5.html');
+ state = new VideoAlpha('#example');
+ });
+
+ describe('when new speed is available', function () {
+ beforeEach(function () {
+ state.setSpeed('0.75');
+ });
+
+ it('set new speed', function () {
+ expect(state.speed).toEqual('0.75');
+ });
+
+ it('save setting for new speed', function () {
+ expect($.cookie).toHaveBeenCalledWith('video_speed', '0.75', {
+ expires: 3650,
+ path: '/'
+ });
+ });
+ });
+
+ describe('when new speed is not available', function () {
+ beforeEach(function () {
+ state.setSpeed('1.75');
+ });
+
+ it('set speed to 1.0x', function () {
+ expect(state.speed).toEqual('1.0');
+ });
+ });
+ });
+ });
+
+ describe('getDuration', function () {
+ beforeEach(function () {
+ loadFixtures('videoalpha.html');
+ state = new VideoAlpha('#example');
+ });
+
+ it('return duration for current video', function () {
+ expect(state.getDuration()).toEqual(200);
+ });
+ });
+
+ describe('log', function () {
+ beforeEach(function () {
+ //TO DO??? loadFixtures('videoalpha.html');
+ loadFixtures('videoalpha_html5.html');
+ state = new VideoAlpha('#example');
+ spyOn(Logger, 'log');
+ state.videoPlayer.log('someEvent', {
+ currentTime: 25,
+ speed: '1.0'
+ });
+ });
+
+ it('call the logger with valid extra parameters', function () {
+ expect(Logger.log).toHaveBeenCalledWith('someEvent', {
+ id: 'id',
+ code: 'html5',
+ currentTime: 25,
+ speed: '1.0'
+ });
+ });
+ });
+ });
+}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/src/videoalpha/display/.gitignore b/common/lib/xmodule/xmodule/js/src/videoalpha/display/.gitignore
deleted file mode 100644
index c7a88ce092..0000000000
--- a/common/lib/xmodule/xmodule/js/src/videoalpha/display/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-!html5_video.js
diff --git a/common/lib/xmodule/xmodule/js/src/videoalpha/helper_utils.js b/common/lib/xmodule/xmodule/js/src/videoalpha/helper_utils.js
new file mode 100644
index 0000000000..451fc5e45d
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/src/videoalpha/helper_utils.js
@@ -0,0 +1,74 @@
+// IE browser supports Function.bind() only starting with version 9.
+//
+// The bind function is a recent addition to ECMA-262, 5th edition; as such it may not be present in all
+// browsers. You can partially work around this by inserting the following code at the beginning of your
+// scripts, allowing use of much of the functionality of bind() in implementations that do not natively support
+// it.
+//
+// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind
+if (!Function.prototype.bind) {
+ Function.prototype.bind = function (oThis) {
+ var aArgs, fToBind, fNOP, fBound;
+
+ if (typeof this !== 'function') {
+ // closest thing possible to the ECMAScript 5 internal IsCallable function
+ throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
+ }
+
+ aArgs = Array.prototype.slice.call(arguments, 1);
+ fToBind = this;
+ fNOP = function () {};
+ fBound = function () {
+ return fToBind.apply(
+ this instanceof fNOP && oThis ? this : oThis,
+ aArgs.concat(Array.prototype.slice.call(arguments))
+ );
+ };
+
+ fNOP.prototype = this.prototype;
+ fBound.prototype = new fNOP();
+
+ return fBound;
+ };
+}
+
+// IE browser supports Array.indexOf() only starting with version 9.
+//
+// indexOf is a recent addition to the ECMA-262 standard; as such it may not be present in all browsers. You can work
+// around this by utilizing the following code at the beginning of your scripts. This will allow you to use indexOf
+// when there is still no native support. This algorithm matches the one specified in ECMA-262, 5th edition, assuming
+// Object, TypeError, Number, Math.floor, Math.abs, and Math.max have their original values.
+//
+// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf
+if (!Array.prototype.indexOf) {
+ Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
+ 'use strict';
+ if (this == null) {
+ throw new TypeError();
+ }
+ var t = Object(this);
+ var len = t.length >>> 0;
+ if (len === 0) {
+ return -1;
+ }
+ var n = 0;
+ if (arguments.length > 1) {
+ n = Number(arguments[1]);
+ if (n != n) { // shortcut for verifying if it's NaN
+ n = 0;
+ } else if (n != 0 && n != Infinity && n != -Infinity) {
+ n = (n > 0 || -1) * Math.floor(Math.abs(n));
+ }
+ }
+ if (n >= len) {
+ return -1;
+ }
+ var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
+ for (; k < len; k++) {
+ if (k in t && t[k] === searchElement) {
+ return k;
+ }
+ }
+ return -1;
+ }
+}
diff --git a/common/lib/xmodule/xmodule/js/src/videoalpha/display/html5_video.js b/common/lib/xmodule/xmodule/js/src/videoalpha/html5_video.js
similarity index 86%
rename from common/lib/xmodule/xmodule/js/src/videoalpha/display/html5_video.js
rename to common/lib/xmodule/xmodule/js/src/videoalpha/html5_video.js
index 5372108aba..bf10f6586a 100644
--- a/common/lib/xmodule/xmodule/js/src/videoalpha/display/html5_video.js
+++ b/common/lib/xmodule/xmodule/js/src/videoalpha/html5_video.js
@@ -23,7 +23,7 @@ function () {
Player.prototype.callStateChangeCallback = function () {
if ($.isFunction(this.config.events.onStateChange)) {
this.config.events.onStateChange({
- 'data': this.playerState
+ data: this.playerState
});
}
};
@@ -96,61 +96,47 @@ function () {
*
* config = {
*
- * 'videoSources': {}, // An object with properties being video sources. The property name is the
+ * videoSources: {}, // An object with properties being video sources. The property name is the
* // video format of the source. Supported video formats are: 'mp4', 'webm', and
* // 'ogg'.
*
- * 'playerVars': { // Object's properties identify player parameters.
- * 'start': 0, // Possible values: positive integer. Position from which to start playing the
+ * playerVars: { // Object's properties identify player parameters.
+ * start: 0, // Possible values: positive integer. Position from which to start playing the
* // video. Measured in seconds. If value is non-numeric, or 'start' property is
* // not specified, the video will start playing from the beginning.
*
- * 'end': null // Possible values: positive integer. Position when to stop playing the
+ * end: null // Possible values: positive integer. Position when to stop playing the
* // video. Measured in seconds. If value is null, or 'end' property is not
* // specified, the video will end playing at the end.
*
* },
*
- * 'events': { // Object's properties identify the events that the API fires, and the
+ * events: { // Object's properties identify the events that the API fires, and the
* // functions (event listeners) that the API will call when those events occur.
* // If value is null, or property is not specified, then no callback will be
* // called for that event.
*
- * 'onReady': null,
- * 'onStateChange': null
+ * onReady: null,
+ * onStateChange: null
* }
* }
*/
function Player(el, config) {
var sourceStr, _this;
- // If el is string, we assume it is an ID of a DOM element. Get the element, and check that the ID
- // really belongs to an element. If we didn't get a DOM element, return. At this stage, nothing will
- // break because other parts of the video player are waiting for 'onReady' callback to be called.
-
- // REFACTOR: Use .length === 0
-
- this.el = $(el);
- // REFACTOR: Simplify chck.
+ // Initially we assume that el is a DOM element. If jQuery selector fails to select something, we
+ // assume that el is an ID of a DOM element. We try to select by ID. If jQuery fails this time,
+ // we return. Nothing breaks because the player 'onReady' event will never be fired.
+
+ this.el = $(el);
+ if (this.el.length === 0) {
+ this.el = $('#' + el);
+
if (this.el.length === 0) {
return;
}
-
-
-
-
- if (typeof el === 'string') {
- this.el = $(el);
- // REFACTOR: Simplify chck.
- if (this.el.length === 0) {
- return;
- }
- } else if (el instanceof jQuery) {
- this.el = el;
- } else {
- return;
}
-
+
// A simple test to see that the 'config' is a normal object.
if ($.isPlainObject(config)) {
this.config = config;
@@ -165,9 +151,9 @@ function () {
// From the start, all sources are empty. We will populate this object below.
sourceStr = {
- 'mp4': ' ',
- 'webm': ' ',
- 'ogg': ' '
+ mp4: ' ',
+ webm: ' ',
+ ogg: ' '
};
// Will be used in inner functions to point to the current object.
@@ -304,14 +290,16 @@ function () {
}
}());
- // REFACTOR: Doc.
+ // The YouTube API presents several constants which describe the player's state at a given moment.
+ // HTML5Video API will copy these constats so that code which uses both the YouTube API and this API
+ // doesn't have to change.
HTML5Video.PlayerState = {
- 'UNSTARTED': -1,
- 'ENDED': 0,
- 'PLAYING': 1,
- 'PAUSED': 2,
- 'BUFFERING': 3,
- 'CUED': 5
+ UNSTARTED: -1,
+ ENDED: 0,
+ PLAYING: 1,
+ PAUSED: 2,
+ BUFFERING: 3,
+ CUED: 5
};
// HTML5Video object - what this module exports.
diff --git a/common/lib/xmodule/xmodule/js/src/videoalpha/display/initialize.js b/common/lib/xmodule/xmodule/js/src/videoalpha/initialize.js
similarity index 85%
rename from common/lib/xmodule/xmodule/js/src/videoalpha/display/initialize.js
rename to common/lib/xmodule/xmodule/js/src/videoalpha/initialize.js
index 049746b47e..e681db0696 100644
--- a/common/lib/xmodule/xmodule/js/src/videoalpha/display/initialize.js
+++ b/common/lib/xmodule/xmodule/js/src/videoalpha/initialize.js
@@ -25,7 +25,6 @@ function (VideoPlayer) {
* @param {DOM element} element Container of the entire Video Alpha DOM element.
*/
return function (state, element) {
- checkForNativeFunctions();
makeFunctionsPublic(state);
renderElements(state, element);
};
@@ -57,6 +56,9 @@ function (VideoPlayer) {
function renderElements(state, element) {
var onPlayerReadyFunc;
+ // This is used in places where we instead would have to check if an element has a CSS class 'fullscreen'.
+ state.isFullScreen = false;
+
// The parent element of the video, and the ID.
state.el = $(element).find('.videoalpha');
state.id = state.el.attr('id').replace(/video_/, '');
@@ -77,7 +79,18 @@ function (VideoPlayer) {
sub: state.el.data('sub'),
mp4Source: state.el.data('mp4-source'),
webmSource: state.el.data('webm-source'),
- oggSource: state.el.data('ogg-source')
+ oggSource: state.el.data('ogg-source'),
+
+ fadeOutTimeout: 1400,
+
+ availableQualities: ['hd720', 'hd1080', 'highres'],
+
+ qTipConfig: {
+ position: {
+ my: 'top right',
+ at: 'top center'
+ }
+ }
};
// Try to parse YouTube stream ID's. If
@@ -95,9 +108,9 @@ function (VideoPlayer) {
parseVideoSources(
state,
{
- 'mp4': state.config.mp4Source,
- 'webm': state.config.webmSource,
- 'ogg': state.config.oggSource
+ mp4: state.config.mp4Source,
+ webm: state.config.webmSource,
+ ogg: state.config.oggSource
}
);
@@ -136,8 +149,8 @@ function (VideoPlayer) {
state.hide_captions = true;
$.cookie('hide_captions', state.hide_captions, {
- 'expires': 3650,
- 'path': '/'
+ expires: 3650,
+ path: '/'
});
state.el.addClass('closed');
@@ -153,8 +166,8 @@ function (VideoPlayer) {
state.currentPlayerMode = currentPlayerMode;
} else {
$.cookie('current_player_mode', 'html5', {
- 'expires': 3650,
- 'path': '/'
+ expires: 3650,
+ path: '/'
});
state.currentPlayerMode = 'html5';
}
@@ -196,7 +209,7 @@ function (VideoPlayer) {
function parseYoutubeStreams(state, youtubeStreams) {
- if (!youtubeStreams.length) {
+ if (typeof youtubeStreams === 'undefined' || youtubeStreams.length === 0) {
return false;
}
@@ -219,7 +232,11 @@ function (VideoPlayer) {
// Take the HTML5 sources (URLs of videos), and make them available explictly for each type
// of video format (mp4, webm, ogg).
function parseVideoSources(state, sources) {
- state.html5Sources = { 'mp4': null, 'webm': null, 'ogg': null };
+ state.html5Sources = {
+ mp4: null,
+ webm: null,
+ ogg: null
+ };
$.each(sources, function (name, source) {
if (source && source.length) {
@@ -254,48 +271,6 @@ function (VideoPlayer) {
state.setSpeed($.cookie('video_speed'));
}
- function checkForNativeFunctions() {
- // REFACTOR:
- // 1.) IE8 doc.
- // 2.) Move to separate file.
- // 3.) Write about a generic soluction system wide.
-
- //
- // IE browser supports Function.bind() only starting with version 8.
- //
- // The bind function is a recent addition to ECMA-262, 5th edition; as such it may not be present in all
- // browsers. You can partially work around this by inserting the following code at the beginning of your
- // scripts, allowing use of much of the functionality of bind() in implementations that do not natively support
- // it.
- //
- // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind
- if (!Function.prototype.bind) {
- Function.prototype.bind = function (oThis) {
- var aArgs, fToBind, fNOP, fBound;
-
- if (typeof this !== 'function') {
- // closest thing possible to the ECMAScript 5 internal IsCallable function
- throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
- }
-
- aArgs = Array.prototype.slice.call(arguments, 1);
- fToBind = this;
- fNOP = function () {};
- fBound = function () {
- return fToBind.apply(
- this instanceof fNOP && oThis ? this : oThis,
- aArgs.concat(Array.prototype.slice.call(arguments))
- );
- };
-
- fNOP.prototype = this.prototype;
- fBound.prototype = new fNOP();
-
- return fBound;
- };
- }
- }
-
// ***************************************************************
// Public functions start here.
// These are available via the 'state' object. Their context ('this' keyword) is the 'state' object.
@@ -311,8 +286,8 @@ function (VideoPlayer) {
if (updateCookie) {
$.cookie('video_speed', this.speed, {
- 'expires': 3650,
- 'path': '/'
+ expires: 3650,
+ path: '/'
});
}
}
diff --git a/common/lib/xmodule/xmodule/js/src/videoalpha/main.js b/common/lib/xmodule/xmodule/js/src/videoalpha/main.js
index ac29e5f16d..513e977ead 100644
--- a/common/lib/xmodule/xmodule/js/src/videoalpha/main.js
+++ b/common/lib/xmodule/xmodule/js/src/videoalpha/main.js
@@ -1,7 +1,5 @@
(function (requirejs, require, define) {
-// REFACTOR. Build JS doc. Add docs on how to build docs.
-
// Main module.
require(
[
@@ -38,7 +36,7 @@ function (
// Check for existance of previous state, uninitialize it if necessary, and create a new state.
// Store new state for future invocation of this module consturctor function.
- if (previousState !== null) {
+ if (previousState !== null && typeof previousState.videoPlayer !== 'undefined') {
previousState.videoPlayer.onPause();
}
state = {};
@@ -55,6 +53,11 @@ function (
if (state.config.show_captions) {
VideoCaption(state);
}
+
+ // Because the 'state' object is only available inside this closure, we will also make
+ // it available to the caller by returning it. This is necessary so that we can test
+ // VideoAlpha with Jasmine.
+ return state;
};
});
diff --git a/common/lib/xmodule/xmodule/js/src/videoalpha/display/video_caption.js b/common/lib/xmodule/xmodule/js/src/videoalpha/video_caption.js
similarity index 78%
rename from common/lib/xmodule/xmodule/js/src/videoalpha/display/video_caption.js
rename to common/lib/xmodule/xmodule/js/src/videoalpha/video_caption.js
index 095dc784a0..9a1d33df22 100644
--- a/common/lib/xmodule/xmodule/js/src/videoalpha/display/video_caption.js
+++ b/common/lib/xmodule/xmodule/js/src/videoalpha/video_caption.js
@@ -60,14 +60,13 @@ function () {
state.el.find('.video-controls .secondary-controls').append(state.videoCaption.hideSubtitlesEl);
state.el.find('.subtitles').css({
- 'maxHeight': state.el.find('.video-wrapper').height() - 5
+ maxHeight: state.el.find('.video-wrapper').height() - 5
});
fetchCaption(state);
- // REFACTOR. Const.
if (state.videoType === 'html5') {
- state.videoCaption.fadeOutTimeout = 1400;
+ state.videoCaption.fadeOutTimeout = state.config.fadeOutTimeout;
state.videoCaption.subtitlesEl.addClass('html5');
state.captionHideTimeout = setTimeout(state.videoCaption.autoHideCaptions, state.videoCaption.fadeOutTimeout);
@@ -80,21 +79,24 @@ function () {
function bindHandlers(state) {
$(window).bind('resize', state.videoCaption.resize);
state.videoCaption.hideSubtitlesEl.click(state.videoCaption.toggle);
-
- // REFACTOR: Use .on()
- state.videoCaption.subtitlesEl.mouseenter(
- state.videoCaption.onMouseEnter
- ).mouseleave(
- state.videoCaption.onMouseLeave
- ).mousemove(
- state.videoCaption.onMovement
- ).bind(
- 'mousewheel',
- state.videoCaption.onMovement
- ).bind(
- 'DOMMouseScroll',
- state.videoCaption.onMovement
- );
+
+ state.videoCaption.subtitlesEl
+ .on(
+ 'mouseenter',
+ state.videoCaption.onMouseEnter
+ ).on(
+ 'mouseleave',
+ state.videoCaption.onMouseLeave
+ ).on(
+ 'mousemove',
+ state.videoCaption.onMovement
+ ).on(
+ 'mousewheel',
+ state.videoCaption.onMovement
+ ).on(
+ 'DOMMouseScroll',
+ state.videoCaption.onMovement
+ );
if (state.videoType === 'html5') {
state.el.on('mousemove', state.videoCaption.autoShowCaptions)
@@ -137,23 +139,18 @@ function () {
this.captionsShowLock = true;
- // REFACTOR.
-
if (this.captionState === 'invisible') {
this.videoCaption.subtitlesEl.show();
this.captionState = 'visible';
- this.captionHideTimeout = setTimeout(this.videoCaption.autoHideCaptions, this.videoCaption.fadeOutTimeout);
} else if (this.captionState === 'hiding') {
- this.videoCaption.subtitlesEl.stop(true, false);
- this.videoCaption.subtitlesEl.css('opacity', 1);
- this.videoCaption.subtitlesEl.show();
+ this.videoCaption.subtitlesEl.stop(true, false).css('opacity', 1).show();
this.captionState = 'visible';
- this.captionHideTimeout = setTimeout(this.videoCaption.autoHideCaptions, this.videoCaption.fadeOutTimeout);
} else if (this.captionState === 'visible') {
clearTimeout(this.captionHideTimeout);
- this.captionHideTimeout = setTimeout(this.videoCaption.autoHideCaptions, this.videoCaption.fadeOutTimeout);
}
+ this.captionHideTimeout = setTimeout(this.videoCaption.autoHideCaptions, this.videoCaption.fadeOutTimeout);
+
this.captionsShowLock = false;
}
}
@@ -171,20 +168,19 @@ function () {
_this = this;
- // REFACTOR.
- this.videoCaption.subtitlesEl.fadeOut(1000, function () {
+ this.videoCaption.subtitlesEl.fadeOut(this.videoCaption.fadeOutTimeout, function () {
_this.captionState = 'invisible';
});
}
function resize() {
this.videoCaption.subtitlesEl.css({
- 'maxHeight': this.videoCaption.captionHeight()
+ maxHeight: this.videoCaption.captionHeight()
});
- // REFACTOR: Chain.
- this.videoCaption.subtitlesEl.find('.spacing:first').height(this.videoCaption.topSpacingHeight());
- this.videoCaption.subtitlesEl.find('.spacing:last').height(this.videoCaption.bottomSpacingHeight());
+ this.videoCaption.subtitlesEl
+ .find('.spacing:first').height(this.videoCaption.topSpacingHeight())
+ .find('.spacing:last').height(this.videoCaption.bottomSpacingHeight());
this.videoCaption.scrollCaption();
}
@@ -194,7 +190,6 @@ function () {
clearTimeout(this.videoCaption.frozen);
}
- // REFACTOR.
this.videoCaption.frozen = setTimeout(this.videoCaption.onMouseLeave, 10000);
}
@@ -219,37 +214,34 @@ function () {
container = $('
');
$.each(this.videoCaption.captions, function(index, text) {
- // REFACTOR: Use .data()
- container.append($('- ').html(text).attr({
- 'data-index': index,
- 'data-start': _this.videoCaption.start[index]
- }));
+ container.append(
+ $('
- ').html(text)
+ .data('index', index)
+ .data('start', _this.videoCaption.start[index])
+ );
});
- // REFACTOR: Chain.
- this.videoCaption.subtitlesEl.html(container.html());
- this.videoCaption.subtitlesEl.find('li[data-index]').on('click', this.videoCaption.seekPlayer);
this.videoCaption.subtitlesEl
- .prepend(
- $('
- ').height(this.videoCaption.topSpacingHeight())
- )
- .append(
- $('
- ').height(this.videoCaption.bottomSpacingHeight())
- );
+ .html(container.html())
+ .find('li[data-index]').on('click', this.videoCaption.seekPlayer)
+ .prepend(
+ $('
- ').height(this.videoCaption.topSpacingHeight())
+ )
+ .append(
+ $('
- ').height(this.videoCaption.bottomSpacingHeight())
+ );
this.videoCaption.rendered = true;
}
function scrollCaption() {
- // REFACTOR: Cache current:first
- if (
- !this.videoCaption.frozen &&
- this.videoCaption.subtitlesEl.find('.current:first').length
- ) {
+ var el = this.videoCaption.subtitlesEl.find('.current:first');
+
+ if (!this.videoCaption.frozen && el.length) {
this.videoCaption.subtitlesEl.scrollTo(
- this.videoCaption.subtitlesEl.find('.current:first'),
+ el,
{
- 'offset': -this.videoCaption.calculateOffset(this.videoCaption.subtitlesEl.find('.current:first'))
+ offset: -this.videoCaption.calculateOffset(el)
}
);
}
@@ -323,7 +315,7 @@ function () {
event.preventDefault();
time = Math.round(Time.convert($(event.target).data('start'), '1.0', this.speed) / 1000);
- this.trigger(['videoPlayer', 'onSeek'], time);
+ this.trigger(['videoPlayer', 'onCaptionSeek'], time);
}
function calculateOffset(element) {
@@ -361,14 +353,13 @@ function () {
}
$.cookie('hide_captions', hide_captions, {
- 'expires': 3650,
- 'path': '/'
+ expires: 3650,
+ path: '/'
});
}
function captionHeight() {
- // REFACTOR: Use property instead of class.
- if (this.el.hasClass('fullscreen')) {
+ if (this.isFullScreen) {
return $(window).height() - this.el.find('.video-controls').height();
} else {
return this.el.find('.video-wrapper').height();
diff --git a/common/lib/xmodule/xmodule/js/src/videoalpha/display/video_control.js b/common/lib/xmodule/xmodule/js/src/videoalpha/video_control.js
similarity index 78%
rename from common/lib/xmodule/xmodule/js/src/videoalpha/display/video_control.js
rename to common/lib/xmodule/xmodule/js/src/videoalpha/video_control.js
index cc2a85a803..008f7f8e70 100644
--- a/common/lib/xmodule/xmodule/js/src/videoalpha/display/video_control.js
+++ b/common/lib/xmodule/xmodule/js/src/videoalpha/video_control.js
@@ -40,8 +40,6 @@ function () {
// make the created DOM elements available via the 'state' object. Much easier to work this
// way - you don't have to do repeated jQuery element selects.
function renderElements(state) {
- var qTipConfig;
-
state.videoControl.el = state.el.find('.video-controls');
// state.videoControl.el.append(el);
@@ -56,23 +54,14 @@ function () {
if (!onTouchBasedDevice()) {
state.videoControl.pause();
- qTipConfig = {
- 'position': {
- 'my': 'top right',
- 'at': 'top center'
- }
- };
-
- state.videoControl.playPauseEl.qtip(qTipConfig);
- state.videoControl.fullScreenEl.qtip(qTipConfig);
+ state.videoControl.playPauseEl.qtip(state.config.qTipConfig);
+ state.videoControl.fullScreenEl.qtip(state.config.qTipConfig);
} else {
state.videoControl.play();
}
if (state.videoType === 'html5') {
- // REFACTOR: constants move to initialize()
-
- state.videoControl.fadeOutTimeout = 1400;
+ state.videoControl.fadeOutTimeout = state.config.fadeOutTimeout;
state.videoControl.el.addClass('html5');
state.controlHideTimeout = setTimeout(state.videoControl.hideControls, state.videoControl.fadeOutTimeout);
@@ -97,7 +86,6 @@ function () {
// These are available via the 'state' object. Their context ('this' keyword) is the 'state' object.
// The magic private function that makes them available and sets up their context is makeFunctionsPublic().
// ***************************************************************
- // REFACTOR document
function showControls(event) {
if (!this.controlShowLock) {
if (!this.captionsHidden) {
@@ -106,25 +94,18 @@ function () {
this.controlShowLock = true;
- // Refactor: separate UI state in object. No code duplication.
- // REFACTOR:
- // 1.) Chain jQuery calls.
- // 2.) Drop out common code.
if (this.controlState === 'invisible') {
this.videoControl.el.show();
this.controlState = 'visible';
- this.controlHideTimeout = setTimeout(this.videoControl.hideControls, this.videoControl.fadeOutTimeout);
} else if (this.controlState === 'hiding') {
- this.videoControl.el.stop(true, false);
- this.videoControl.el.css('opacity', 1);
- this.videoControl.el.show();
+ this.videoControl.el.stop(true, false).css('opacity', 1).show();
this.controlState = 'visible';
- this.controlHideTimeout = setTimeout(this.videoControl.hideControls, this.videoControl.fadeOutTimeout);
} else if (this.controlState === 'visible') {
clearTimeout(this.controlHideTimeout);
- this.controlHideTimeout = setTimeout(this.videoControl.hideControls, this.videoControl.fadeOutTimeout);
}
+ this.controlHideTimeout = setTimeout(this.videoControl.hideControls, this.videoControl.fadeOutTimeout);
+
this.controlShowLock = false;
}
}
@@ -142,28 +123,27 @@ function () {
_this = this;
- this.videoControl.el.fadeOut(1000, function () {
+ this.videoControl.el.fadeOut(this.videoControl.fadeOutTimeout, function () {
_this.controlState = 'invisible';
});
}
function play() {
- // REFACTOR: this.videoControl.playPauseState should be bool.
this.videoControl.playPauseEl.removeClass('play').addClass('pause').attr('title', 'Pause');
- this.videoControl.playPauseState = 'playing';
+ this.videoControl.isPlaying = true;
}
function pause() {
this.videoControl.playPauseEl.removeClass('pause').addClass('play').attr('title', 'Play');
- this.videoControl.playPauseState = 'paused';
+ this.videoControl.isPlaying = false;
}
function togglePlayback(event) {
event.preventDefault();
- if (this.videoControl.playPauseState === 'playing') {
+ if (this.videoControl.isPlaying) {
this.trigger(['videoPlayer', 'pause'], null);
- } else { // if (this.videoControl.playPauseState === 'paused') {
+ } else {
this.trigger(['videoPlayer', 'play'], null);
}
}
@@ -174,10 +154,12 @@ function () {
if (this.videoControl.fullScreenState) {
this.videoControl.fullScreenState = false;
this.el.removeClass('fullscreen');
+ this.isFullScreen = false;
this.videoControl.fullScreenEl.attr('title', 'Fullscreen');
} else {
this.videoControl.fullScreenState = true;
this.el.addClass('fullscreen');
+ this.isFullScreen = true;
this.videoControl.fullScreenEl.attr('title', 'Exit fullscreen');
}
@@ -185,8 +167,7 @@ function () {
}
function exitFullScreen(event) {
- // REFACTOR: Add variable instead of class.
- if ((this.el.hasClass('fullscreen')) && (event.keyCode === 27)) {
+ if ((this.isFullScreen) && (event.keyCode === 27)) {
this.videoControl.toggleFullScreen(event);
}
}
diff --git a/common/lib/xmodule/xmodule/js/src/videoalpha/display/video_player.js b/common/lib/xmodule/xmodule/js/src/videoalpha/video_player.js
similarity index 77%
rename from common/lib/xmodule/xmodule/js/src/videoalpha/display/video_player.js
rename to common/lib/xmodule/xmodule/js/src/videoalpha/video_player.js
index f14769e0b4..43ba8de5c5 100644
--- a/common/lib/xmodule/xmodule/js/src/videoalpha/display/video_player.js
+++ b/common/lib/xmodule/xmodule/js/src/videoalpha/video_player.js
@@ -28,7 +28,8 @@ function (HTML5Video) {
state.videoPlayer.play = play.bind(state);
state.videoPlayer.update = update.bind(state);
state.videoPlayer.onSpeedChange = onSpeedChange.bind(state);
- state.videoPlayer.onSeek = onSeek.bind(state);
+ state.videoPlayer.onCaptionSeek = onSeek.bind(state);
+ state.videoPlayer.onSlideSeek = onSeek.bind(state);
state.videoPlayer.onEnded = onEnded.bind(state);
state.videoPlayer.onPause = onPause.bind(state);
state.videoPlayer.onPlay = onPlay.bind(state);
@@ -62,12 +63,12 @@ function (HTML5Video) {
state.videoPlayer.currentTime = 0;
state.videoPlayer.playerVars = {
- 'controls': 0,
- 'wmode': 'transparent',
- 'rel': 0,
- 'showinfo': 0,
- 'enablejsapi': 1,
- 'modestbranding': 1
+ controls: 0,
+ wmode: 'transparent',
+ rel: 0,
+ showinfo: 0,
+ enablejsapi: 1,
+ modestbranding: 1
};
if (state.currentPlayerMode !== 'flash') {
@@ -82,13 +83,26 @@ function (HTML5Video) {
state.videoPlayer.playerVars.end = state.config.end;
}
+ // There is a bug which prevents YouTube API to correctly set the speed
+ // to 1.0 from another speed in Firefox when in HTML5 mode. There is a
+ // fix which basically reloads the video at speed 1.0 when this change
+ // is requested (instead of simply requesting a speed change to 1.0).
+ // This has to be done only when the video is being watched in Firefox.
+ // We need to figure out what browser is currently executing this code.
+ //
+ // TODO: Check the status of
+ // http://code.google.com/p/gdata-issues/issues/detail?id=4654
+ // When the YouTube team fixes the API bug, we can remove this temporary
+ // bug fix.
+ state.browserIsFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
+
if (state.videoType === 'html5') {
state.videoPlayer.player = new HTML5Video.Player(state.el, {
- 'playerVars': state.videoPlayer.playerVars,
- 'videoSources': state.html5Sources,
- 'events': {
- 'onReady': state.videoPlayer.onReady,
- 'onStateChange': state.videoPlayer.onStateChange
+ playerVars: state.videoPlayer.playerVars,
+ videoSources: state.html5Sources,
+ events: {
+ onReady: state.videoPlayer.onReady,
+ onStateChange: state.videoPlayer.onStateChange
}
});
} else { // if (state.videoType === 'youtube') {
@@ -98,12 +112,12 @@ function (HTML5Video) {
youTubeId = state.youtubeId('1.0');
}
state.videoPlayer.player = new YT.Player(state.id, {
- 'playerVars': state.videoPlayer.playerVars,
- 'videoId': youTubeId,
- 'events': {
- 'onReady': state.videoPlayer.onReady,
- 'onStateChange': state.videoPlayer.onStateChange,
- 'onPlaybackQualityChange': state.videoPlayer.onPlaybackQualityChange
+ playerVars: state.videoPlayer.playerVars,
+ videoId: youTubeId,
+ events: {
+ onReady: state.videoPlayer.onReady,
+ onStateChange: state.videoPlayer.onStateChange,
+ onPlaybackQualityChange: state.videoPlayer.onPlaybackQualityChange
}
});
}
@@ -173,6 +187,11 @@ function (HTML5Video) {
}
}
+ // We request the reloading of the video in the case when YouTube is in
+ // Flash player mode, or when we are in Firefox, and the new speed is 1.0.
+ // The second case is necessary to avoid the bug where in Firefox speed
+ // switching to 1.0 in HTML5 player mode is handled incorrectly by YouTube
+ // API.
function onSpeedChange(newSpeed, updateCookie) {
if (this.currentPlayerMode === 'flash') {
this.videoPlayer.currentTime = Time.convert(
@@ -182,9 +201,19 @@ function (HTML5Video) {
);
}
newSpeed = parseFloat(newSpeed).toFixed(2).replace(/\.00$/, '.0');
+
+ this.videoPlayer.log(
+ 'speed_change_video',
+ {
+ current_time: this.videoPlayer.currentTime,
+ old_speed: this.speed,
+ new_speed: newSpeed
+ }
+ );
+
this.setSpeed(newSpeed, updateCookie);
- if (this.currentPlayerMode === 'html5') {
+ if (this.currentPlayerMode === 'html5' && !(state.browserIsFirefox && newSpeed === '1.0')) {
this.videoPlayer.player.setPlaybackRate(newSpeed);
} else { // if (this.currentPlayerMode === 'flash') {
if (this.videoPlayer.isPlaying()) {
@@ -197,7 +226,16 @@ function (HTML5Video) {
}
}
- function onSeek(time) {
+ function onSeek(event, time) {
+ this.videoPlayer.log(
+ 'seek_video',
+ {
+ old_time: this.videoPlayer.currentTime,
+ new_time: time,
+ type: event.type
+ }
+ );
+
this.videoPlayer.player.seekTo(time, true);
if (this.videoPlayer.isPlaying()) {
@@ -215,7 +253,12 @@ function (HTML5Video) {
}
function onPause() {
- this.videoPlayer.log('pause_video');
+ this.videoPlayer.log(
+ 'pause_video',
+ {
+ 'currentTime': this.videoPlayer.currentTime
+ }
+ );
clearInterval(this.videoPlayer.updateInterval);
delete this.videoPlayer.updateInterval;
@@ -224,7 +267,12 @@ function (HTML5Video) {
}
function onPlay() {
- this.videoPlayer.log('play_video');
+ this.videoPlayer.log(
+ 'play_video',
+ {
+ 'currentTime': this.videoPlayer.currentTime
+ }
+ );
if (!this.videoPlayer.updateInterval) {
this.videoPlayer.updateInterval = setInterval(this.videoPlayer.update, 200);
@@ -249,8 +297,8 @@ function (HTML5Video) {
function onReady() {
var availablePlaybackRates, baseSpeedSubs, _this;
-
- // REFACTOR: Check if logic.
+
+ this.videoPlayer.log('load_video');
availablePlaybackRates = this.videoPlayer.player.getAvailablePlaybackRates();
if ((this.currentPlayerMode === 'html5') && (this.videoType === 'youtube')) {
@@ -283,7 +331,7 @@ function (HTML5Video) {
this.videoPlayer.player.setPlaybackRate(this.speed);
}
- if (!onTouchBasedDevice()) {
+ if (!onTouchBasedDevice() && $('.video:first').data('autoplay') === 'True') {
this.videoPlayer.play();
}
}
@@ -330,16 +378,22 @@ function (HTML5Video) {
return duration;
}
- function log(eventName) {
+ function log(eventName, data) {
var logInfo;
+ // Default parameters that always get logged.
logInfo = {
- 'id': this.id,
- 'code': this.youtubeId(),
- 'currentTime': this.videoPlayer.currentTime,
- 'speed': this.speed
+ 'id': this.id,
+ 'code': this.youtubeId()
};
+ // If extra parameters were passed to the log.
+ if (data) {
+ $.each(data, function(paramName, value) {
+ logInfo[paramName] = value;
+ });
+ }
+
if (this.videoType === 'youtube') {
logInfo.code = this.youtubeId();
} else {
diff --git a/common/lib/xmodule/xmodule/js/src/videoalpha/display/video_progress_slider.js b/common/lib/xmodule/xmodule/js/src/videoalpha/video_progress_slider.js
similarity index 80%
rename from common/lib/xmodule/xmodule/js/src/videoalpha/display/video_progress_slider.js
rename to common/lib/xmodule/xmodule/js/src/videoalpha/video_progress_slider.js
index 612beb9abf..d0cd2a8470 100644
--- a/common/lib/xmodule/xmodule/js/src/videoalpha/display/video_progress_slider.js
+++ b/common/lib/xmodule/xmodule/js/src/videoalpha/video_progress_slider.js
@@ -61,10 +61,10 @@ function () {
function buildSlider(state) {
state.videoProgressSlider.slider = state.videoProgressSlider.el.slider({
- 'range': 'min',
- 'change': state.videoProgressSlider.onChange,
- 'slide': state.videoProgressSlider.onSlide,
- 'stop': state.videoProgressSlider.onStop
+ range: 'min',
+ change: state.videoProgressSlider.onChange,
+ slide: state.videoProgressSlider.onSlide,
+ stop: state.videoProgressSlider.onStop
});
}
@@ -72,18 +72,18 @@ function () {
state.videoProgressSlider.handle = state.videoProgressSlider.el.find('.ui-slider-handle');
state.videoProgressSlider.handle.qtip({
- 'content': '' + Time.format(state.videoProgressSlider.slider.slider('value')),
- 'position': {
- 'my': 'bottom center',
- 'at': 'top center',
- 'container': state.videoProgressSlider.handle
+ content: '' + Time.format(state.videoProgressSlider.slider.slider('value')),
+ position: {
+ my: 'bottom center',
+ at: 'top center',
+ container: state.videoProgressSlider.handle
},
- 'hide': {
- 'delay': 700
+ hide: {
+ delay: 700
},
- 'style': {
- 'classes': 'ui-tooltip-slider',
- 'widget': true
+ style: {
+ classes: 'ui-tooltip-slider',
+ widget: true
}
});
}
@@ -97,7 +97,7 @@ function () {
function onSlide(event, ui) {
this.videoProgressSlider.frozen = true;
this.videoProgressSlider.updateTooltip(ui.value);
- this.trigger(['videoPlayer', 'onSeek'], ui.value);
+ this.trigger(['videoPlayer', 'onSlideSeek'], ui.value);
}
function onChange(event, ui) {
@@ -109,7 +109,7 @@ function () {
this.videoProgressSlider.frozen = true;
- this.trigger(['videoPlayer', 'onSeek'], ui.value);
+ this.trigger(['videoPlayer', 'onSlideSeek'], ui.value);
setTimeout(function() {
_this.videoProgressSlider.frozen = false;
@@ -122,9 +122,9 @@ function () {
function updatePlayTime(params) {
if ((this.videoProgressSlider.slider) && (!this.videoProgressSlider.frozen)) {
- // REFACTOR: Check if you can chain.
- this.videoProgressSlider.slider.slider('option', 'max', params.duration);
- this.videoProgressSlider.slider.slider('value', params.time);
+ this.videoProgressSlider.slider
+ .slider('option', 'max', params.duration)
+ .slider('value', params.time);
}
}
diff --git a/common/lib/xmodule/xmodule/js/src/videoalpha/display/video_quality_control.js b/common/lib/xmodule/xmodule/js/src/videoalpha/video_quality_control.js
similarity index 82%
rename from common/lib/xmodule/xmodule/js/src/videoalpha/display/video_quality_control.js
rename to common/lib/xmodule/xmodule/js/src/videoalpha/video_quality_control.js
index 1d559e45cd..67840e4902 100644
--- a/common/lib/xmodule/xmodule/js/src/videoalpha/display/video_quality_control.js
+++ b/common/lib/xmodule/xmodule/js/src/videoalpha/video_quality_control.js
@@ -44,13 +44,7 @@ function () {
state.videoQualityControl.quality = null;
if (!onTouchBasedDevice()) {
- // REFACTOR: Move qtip config to state.config
- state.videoQualityControl.el.qtip({
- 'position': {
- 'my': 'top right',
- 'at': 'top center'
- }
- });
+ state.videoQualityControl.el.qtip(state.config.qTipConfig);
}
}
@@ -70,8 +64,7 @@ function () {
function onQualityChange(value) {
this.videoQualityControl.quality = value;
- // refactor: Move constants to state.config.
- if ((value === 'hd720') || (value === 'hd1080') || (value === 'highres')) {
+ if (this.config.availableQualities.indexOf(value) !== -1) {
this.videoQualityControl.el.addClass('active');
} else {
this.videoQualityControl.el.removeClass('active');
@@ -79,14 +72,12 @@ function () {
}
function toggleQuality(event) {
- var newQuality, _ref;
+ var newQuality,
+ value = this.videoQualityControl.quality;
event.preventDefault();
- _ref = this.videoQualityControl.quality;
-
- // refactor: Move constants to state.config.
- if ((_ref === 'hd720') || (_ref === 'hd1080') || (_ref === 'highres')) {
+ if (this.config.availableQualities.indexOf(value) !== -1) {
newQuality = 'large';
} else {
newQuality = 'hd720';
diff --git a/common/lib/xmodule/xmodule/js/src/videoalpha/display/video_speed_control.js b/common/lib/xmodule/xmodule/js/src/videoalpha/video_speed_control.js
similarity index 77%
rename from common/lib/xmodule/xmodule/js/src/videoalpha/display/video_speed_control.js
rename to common/lib/xmodule/xmodule/js/src/videoalpha/video_speed_control.js
index 2c8d8ca9b7..f78badf4ec 100644
--- a/common/lib/xmodule/xmodule/js/src/videoalpha/display/video_speed_control.js
+++ b/common/lib/xmodule/xmodule/js/src/videoalpha/video_speed_control.js
@@ -44,13 +44,9 @@ function () {
state.videoControl.secondaryControlsEl.prepend(state.videoSpeedControl.el);
$.each(state.videoSpeedControl.speeds, function(index, speed) {
- // REFACTOR: Move all HTML into one function call.
- var link = $('').attr({
- 'href': '#'
- }).html('' + speed + 'x');
+ var link = $('' + speed + 'x');
- // REFACTOR: Use jQuery .data()
- state.videoSpeedControl.videoSpeedsEl.prepend($('
- ').attr('data-speed', speed).html(link));
+ state.videoSpeedControl.videoSpeedsEl.prepend($('
- ' + link + '
'));
});
state.videoSpeedControl.setSpeed(state.speed);
@@ -68,19 +64,17 @@ function () {
$(this).toggleClass('open');
});
} else {
- // REFACTOR: Chain.
- state.videoSpeedControl.el.on('mouseenter', function() {
- $(this).addClass('open');
- });
-
- state.videoSpeedControl.el.on('mouseleave', function() {
- $(this).removeClass('open');
- });
-
- state.videoSpeedControl.el.on('click', function(event) {
- event.preventDefault();
- $(this).removeClass('open');
- });
+ state.videoSpeedControl.el
+ .on('mouseenter', function () {
+ $(this).addClass('open');
+ })
+ .on('mouseleave', function () {
+ $(this).removeClass('open');
+ })
+ .on('click', function (event) {
+ event.preventDefault();
+ $(this).removeClass('open');
+ });
}
}
@@ -91,18 +85,18 @@ function () {
// ***************************************************************
function setSpeed(speed) {
- // REFACTOR: Use chaining.
this.videoSpeedControl.videoSpeedsEl.find('li').removeClass('active');
this.videoSpeedControl.videoSpeedsEl.find("li[data-speed='" + speed + "']").addClass('active');
this.videoSpeedControl.el.find('p.active').html('' + speed + 'x');
}
function changeVideoSpeed(event) {
+ var parentEl = $(event.target).parent();
+
event.preventDefault();
- // REFACTOR: Cache parent el.
- if (!$(event.target).parent().hasClass('active')) {
- this.videoSpeedControl.currentSpeed = $(event.target).parent().data('speed');
+ if (!parentEl.hasClass('active')) {
+ this.videoSpeedControl.currentSpeed = parentEl.data('speed');
this.videoSpeedControl.setSpeed(
// To meet the API expected format.
@@ -113,23 +107,19 @@ function () {
}
}
- // REFACTOR.
- function reRender(params /*newSpeeds, currentSpeed*/) {
- var _this;
+ function reRender(params) {
+ var _this = this;
this.videoSpeedControl.videoSpeedsEl.empty();
this.videoSpeedControl.videoSpeedsEl.find('li').removeClass('active');
this.videoSpeedControl.speeds = params.newSpeeds;
- _this = this;
$.each(this.videoSpeedControl.speeds, function(index, speed) {
var link, listItem;
- link = $('').attr({
- 'href': '#'
- }).html('' + speed + 'x');
+ link = $('' + speed + 'x');
- listItem = $('- ').attr('data-speed', speed).html(link);
+ listItem = $('
- ' + link + '
');
if (speed === params.currentSpeed) {
listItem.addClass('active');
diff --git a/common/lib/xmodule/xmodule/js/src/videoalpha/display/video_volume_control.js b/common/lib/xmodule/xmodule/js/src/videoalpha/video_volume_control.js
similarity index 82%
rename from common/lib/xmodule/xmodule/js/src/videoalpha/display/video_volume_control.js
rename to common/lib/xmodule/xmodule/js/src/videoalpha/video_volume_control.js
index 3d979a0d1a..c3892cbaae 100644
--- a/common/lib/xmodule/xmodule/js/src/videoalpha/display/video_volume_control.js
+++ b/common/lib/xmodule/xmodule/js/src/videoalpha/video_volume_control.js
@@ -41,28 +41,24 @@ function () {
state.videoControl.secondaryControlsEl.prepend(state.videoVolumeControl.el);
- // Figure out what the current volume is. Set it up so that muting/unmuting works correctly.
- // If no information about volume level could be retrieved, then we will use the default
- // 100 level (full volume).
- // REFACTOR: Remove unnecessary checks.
+ // Figure out what the current volume is. If no information about volume level could be retrieved,
+ // then we will use the default 100 level (full volume).
state.videoVolumeControl.currentVolume = parseInt($.cookie('video_player_volume_level'), 10);
- state.videoVolumeControl.previousVolume = 100;
- if (
- (!isFinite(state.videoVolumeControl.currentVolume)) ||
- (state.videoVolumeControl.currentVolume < 0) ||
- (state.videoVolumeControl.currentVolume > 100)
- ) {
+ if (!isFinite(state.videoVolumeControl.currentVolume)) {
state.videoVolumeControl.currentVolume = 100;
}
+ // Set it up so that muting/unmuting works correctly.
+ state.videoVolumeControl.previousVolume = 100;
+
state.videoVolumeControl.slider = state.videoVolumeControl.volumeSliderEl.slider({
- 'orientation': 'vertical',
- 'range': 'min',
- 'min': 0,
- 'max': 100,
- 'value': state.videoVolumeControl.currentVolume,
- 'change': state.videoVolumeControl.onChange,
- 'slide': state.videoVolumeControl.onChange
+ orientation: 'vertical',
+ range: 'min',
+ min: 0,
+ max: 100,
+ value: state.videoVolumeControl.currentVolume,
+ change: state.videoVolumeControl.onChange,
+ slide: state.videoVolumeControl.onChange
});
state.videoVolumeControl.el.toggleClass('muted', state.videoVolumeControl.currentVolume === 0);
@@ -94,8 +90,8 @@ function () {
this.videoVolumeControl.el.toggleClass('muted', this.videoVolumeControl.currentVolume === 0);
$.cookie('video_player_volume_level', ui.value, {
- 'expires': 3650,
- 'path': '/'
+ expires: 3650,
+ path: '/'
});
this.trigger(['videoPlayer', 'onVolumeChange'], ui.value);
diff --git a/common/lib/xmodule/xmodule/tests/__init__.py b/common/lib/xmodule/xmodule/tests/__init__.py
index d310110844..6565e49fbb 100644
--- a/common/lib/xmodule/xmodule/tests/__init__.py
+++ b/common/lib/xmodule/xmodule/tests/__init__.py
@@ -14,11 +14,16 @@ import fs.osfs
import numpy
+import json
+from lxml import etree
+
import calc
import xmodule
from xmodule.x_module import ModuleSystem
from mock import Mock
+
+
open_ended_grading_interface = {
'url': 'blah/',
'username': 'incorrect_user',
@@ -111,3 +116,33 @@ class ModelsTest(unittest.TestCase):
except:
exception_happened = True
self.assertTrue(exception_happened)
+
+
+class PostData(object):
+ """Class which emulate postdata."""
+ def __init__(self, dict_data):
+ self.dict_data = dict_data
+
+ def getlist(self, key):
+ return self.dict_data.get(key)
+
+
+class LogicTest(unittest.TestCase):
+ """Base class for testing xmodule logic."""
+ descriptor_class = None
+ raw_model_data = {}
+
+ def setUp(self):
+ class EmptyClass:
+ pass
+
+ self.system = None
+ self.location = None
+ self.descriptor = EmptyClass()
+
+ self.xmodule_class = self.descriptor_class.module_class
+ self.xmodule = self.xmodule_class(
+ self.system, self.descriptor, self.raw_model_data)
+
+ def ajax_request(self, dispatch, get):
+ return json.loads(self.xmodule.handle_ajax(dispatch, get))
diff --git a/common/lib/xmodule/xmodule/tests/test_conditional_logic.py b/common/lib/xmodule/xmodule/tests/test_conditional_logic.py
new file mode 100644
index 0000000000..85428fc125
--- /dev/null
+++ b/common/lib/xmodule/xmodule/tests/test_conditional_logic.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+"""Test for Conditional Xmodule functional logic."""
+
+from xmodule.conditional_module import ConditionalDescriptor
+from . import PostData, LogicTest
+
+
+class ConditionalModuleTest(LogicTest):
+ descriptor_class = ConditionalDescriptor
+
+ def test_ajax_request(self):
+ "Make shure that ajax request works correctly"
+ # Mock is_condition_satisfied
+ self.xmodule.is_condition_satisfied = lambda: True
+ setattr(self.xmodule.descriptor, 'get_children', lambda: [])
+
+ response = self.ajax_request('No', {})
+ html = response['html']
+
+ self.assertEqual(html, [])
diff --git a/common/lib/xmodule/xmodule/tests/test_logic.py b/common/lib/xmodule/xmodule/tests/test_logic.py
deleted file mode 100644
index 5fe7aa2832..0000000000
--- a/common/lib/xmodule/xmodule/tests/test_logic.py
+++ /dev/null
@@ -1,127 +0,0 @@
-# -*- coding: utf-8 -*-
-# pylint: disable=W0232
-"""Test for Xmodule functional logic."""
-
-import json
-import unittest
-
-from xmodule.poll_module import PollDescriptor
-from xmodule.conditional_module import ConditionalDescriptor
-from xmodule.word_cloud_module import WordCloudDescriptor
-from xmodule.tests import get_test_system
-
-class PostData:
- """Class which emulate postdata."""
- def __init__(self, dict_data):
- self.dict_data = dict_data
-
- def getlist(self, key):
- """Get data by key from `self.dict_data`."""
- return self.dict_data.get(key)
-
-
-class LogicTest(unittest.TestCase):
- """Base class for testing xmodule logic."""
- descriptor_class = None
- raw_model_data = {}
-
- def setUp(self):
- class EmptyClass:
- """Empty object."""
- url_name = ''
- category = 'test'
-
- self.system = get_test_system()
- self.descriptor = EmptyClass()
-
- self.xmodule_class = self.descriptor_class.module_class
- self.xmodule = self.xmodule_class(
- self.system,
- self.descriptor,
- self.raw_model_data
- )
-
- def ajax_request(self, dispatch, data):
- """Call Xmodule.handle_ajax."""
- return json.loads(self.xmodule.handle_ajax(dispatch, data))
-
-
-class PollModuleTest(LogicTest):
- """Logic tests for Poll Xmodule."""
- descriptor_class = PollDescriptor
- raw_model_data = {
- 'poll_answers': {'Yes': 1, 'Dont_know': 0, 'No': 0},
- 'voted': False,
- 'poll_answer': ''
- }
-
- def test_bad_ajax_request(self):
- response = self.ajax_request('bad_answer', {})
- self.assertDictEqual(response, {'error': 'Unknown Command!'})
-
- def test_good_ajax_request(self):
- response = self.ajax_request('No', {})
-
- poll_answers = response['poll_answers']
- total = response['total']
- callback = response['callback']
-
- self.assertDictEqual(poll_answers, {'Yes': 1, 'Dont_know': 0, 'No': 1})
- self.assertEqual(total, 2)
- self.assertDictEqual(callback, {'objectName': 'Conditional'})
- self.assertEqual(self.xmodule.poll_answer, 'No')
-
-
-class ConditionalModuleTest(LogicTest):
- """Logic tests for Conditional Xmodule."""
- descriptor_class = ConditionalDescriptor
-
- def test_ajax_request(self):
- # Mock is_condition_satisfied
- self.xmodule.is_condition_satisfied = lambda: True
- setattr(self.xmodule.descriptor, 'get_children', lambda: [])
-
- response = self.ajax_request('No', {})
- html = response['html']
-
- self.assertEqual(html, [])
-
-
-class WordCloudModuleTest(LogicTest):
- """Logic tests for Word Cloud Xmodule."""
- descriptor_class = WordCloudDescriptor
- raw_model_data = {
- 'all_words': {'cat': 10, 'dog': 5, 'mom': 1, 'dad': 2},
- 'top_words': {'cat': 10, 'dog': 5, 'dad': 2},
- 'submitted': False
- }
-
- def test_bad_ajax_request(self):
- response = self.ajax_request('bad_dispatch', {})
- self.assertDictEqual(response, {
- 'status': 'fail',
- 'error': 'Unknown Command!'
- })
-
- def test_good_ajax_request(self):
- post_data = PostData({'student_words[]': ['cat', 'cat', 'dog', 'sun']})
- response = self.ajax_request('submit', post_data)
- self.assertEqual(response['status'], 'success')
- self.assertEqual(response['submitted'], True)
- self.assertEqual(response['total_count'], 22)
- self.assertDictEqual(
- response['student_words'],
- {'sun': 1, 'dog': 6, 'cat': 12}
- )
- self.assertListEqual(
- response['top_words'],
- [{'text': 'dad', 'size': 2, 'percent': 9.0},
- {'text': 'sun', 'size': 1, 'percent': 5.0},
- {'text': 'dog', 'size': 6, 'percent': 27.0},
- {'text': 'mom', 'size': 1, 'percent': 5.0},
- {'text': 'cat', 'size': 12, 'percent': 54.0}]
- )
-
- self.assertEqual(
- 100.0,
- sum(i['percent'] for i in response['top_words']))
diff --git a/common/lib/xmodule/xmodule/tests/test_poll.py b/common/lib/xmodule/xmodule/tests/test_poll.py
new file mode 100644
index 0000000000..6e421c7cb7
--- /dev/null
+++ b/common/lib/xmodule/xmodule/tests/test_poll.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+"""Test for Poll Xmodule functional logic."""
+from xmodule.poll_module import PollDescriptor
+from . import PostData, LogicTest
+
+
+class PollModuleTest(LogicTest):
+ descriptor_class = PollDescriptor
+ raw_model_data = {
+ 'poll_answers': {'Yes': 1, 'Dont_know': 0, 'No': 0},
+ 'voted': False,
+ 'poll_answer': ''
+ }
+
+ def test_bad_ajax_request(self):
+ "Make sure that answer for incorrect request is error json"
+ response = self.ajax_request('bad_answer', {})
+ self.assertDictEqual(response, {'error': 'Unknown Command!'})
+
+ def test_good_ajax_request(self):
+ "Make shure that ajax request works correctly"
+ response = self.ajax_request('No', {})
+
+ poll_answers = response['poll_answers']
+ total = response['total']
+ callback = response['callback']
+
+ self.assertDictEqual(poll_answers, {'Yes': 1, 'Dont_know': 0, 'No': 1})
+ self.assertEqual(total, 2)
+ self.assertDictEqual(callback, {'objectName': 'Conditional'})
+ self.assertEqual(self.xmodule.poll_answer, 'No')
diff --git a/common/lib/xmodule/xmodule/tests/test_video_xml.py b/common/lib/xmodule/xmodule/tests/test_video_xml.py
index 081870792c..1ccc633ee2 100644
--- a/common/lib/xmodule/xmodule/tests/test_video_xml.py
+++ b/common/lib/xmodule/xmodule/tests/test_video_xml.py
@@ -13,15 +13,12 @@ common/lib/xmodule/xmodule/modulestore/tests/factories.py to create the
course, section, subsection, unit, etc.
"""
-import json
-import unittest
from mock import Mock
-from lxml import etree
from xmodule.video_module import VideoDescriptor, VideoModule, _parse_time, _parse_youtube
from xmodule.modulestore import Location
from xmodule.tests import get_test_system
-from xmodule.tests.test_logic import LogicTest
+from xmodule.tests import LogicTest
class VideoFactory(object):
diff --git a/common/lib/xmodule/xmodule/tests/test_videoalpha.py b/common/lib/xmodule/xmodule/tests/test_videoalpha.py
new file mode 100644
index 0000000000..909b273104
--- /dev/null
+++ b/common/lib/xmodule/xmodule/tests/test_videoalpha.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+"""Test for Video Alpha Xmodule functional logic.
+These tests data readed from xml, not from mongo.
+
+we have a ModuleStoreTestCase class defined in
+common/lib/xmodule/xmodule/modulestore/tests/django_utils.py. You can
+search for usages of this in the cms and lms tests for examples. You use
+this so that it will do things like point the modulestore setting to mongo,
+flush the contentstore before and after, load the templates, etc.
+You can then use the CourseFactory and XModuleItemFactory as defined
+in common/lib/xmodule/xmodule/modulestore/tests/factories.py to create
+the course, section, subsection, unit, etc.
+"""
+
+from xmodule.videoalpha_module import VideoAlphaDescriptor
+from . import LogicTest, etree
+
+
+class VideoAlphaModuleTest(LogicTest):
+ """Logic tests for VideoAlpha Xmodule."""
+ descriptor_class = VideoAlphaDescriptor
+
+ raw_model_data = {
+ 'data': ''
+ }
+
+ def test_get_timeframe_no_parameters(self):
+ "Make sure that timeframe() works correctly w/o parameters"
+ xmltree = etree.fromstring('test')
+ output = self.xmodule.get_timeframe(xmltree)
+ self.assertEqual(output, ('', ''))
+
+ def test_get_timeframe_with_one_parameter(self):
+ "Make sure that timeframe() works correctly with one parameter"
+ xmltree = etree.fromstring(
+ 'test'
+ )
+ output = self.xmodule.get_timeframe(xmltree)
+ self.assertEqual(output, (247, ''))
+
+ def test_get_timeframe_with_two_parameters(self):
+ "Make sure that timeframe() works correctly with two parameters"
+ xmltree = etree.fromstring(
+ '''test'''
+ )
+ output = self.xmodule.get_timeframe(xmltree)
+ self.assertEqual(output, (247, 47079))
diff --git a/common/lib/xmodule/xmodule/tests/test_word_cloud.py b/common/lib/xmodule/xmodule/tests/test_word_cloud.py
new file mode 100644
index 0000000000..87992e4d5f
--- /dev/null
+++ b/common/lib/xmodule/xmodule/tests/test_word_cloud.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+"""Test for Word cloud Xmodule functional logic."""
+
+from xmodule.word_cloud_module import WordCloudDescriptor
+from . import PostData, LogicTest
+
+
+class WordCloudModuleTest(LogicTest):
+ descriptor_class = WordCloudDescriptor
+ raw_model_data = {
+ 'all_words': {'cat': 10, 'dog': 5, 'mom': 1, 'dad': 2},
+ 'top_words': {'cat': 10, 'dog': 5, 'dad': 2},
+ 'submitted': False
+ }
+
+ def test_bad_ajax_request(self):
+ "Make sure that answer for incorrect request is error json"
+ # TODO: move top global test. Formalize all our Xmodule errors.
+ response = self.ajax_request('bad_dispatch', {})
+ self.assertDictEqual(response, {
+ 'status': 'fail',
+ 'error': 'Unknown Command!'
+ })
+
+ def test_good_ajax_request(self):
+ "Make shure that ajax request works correctly"
+ post_data = PostData({'student_words[]': ['cat', 'cat', 'dog', 'sun']})
+ response = self.ajax_request('submit', post_data)
+ self.assertEqual(response['status'], 'success')
+ self.assertEqual(response['submitted'], True)
+ self.assertEqual(response['total_count'], 22)
+ self.assertDictEqual(
+ response['student_words'],
+ {'sun': 1, 'dog': 6, 'cat': 12}
+ )
+ self.assertListEqual(
+ response['top_words'],
+ [{'text': 'dad', 'size': 2, 'percent': 9.0},
+ {'text': 'sun', 'size': 1, 'percent': 5.0},
+ {'text': 'dog', 'size': 6, 'percent': 27.0},
+ {'text': 'mom', 'size': 1, 'percent': 5.0},
+ {'text': 'cat', 'size': 12, 'percent': 54.0}]
+ )
+
+ self.assertEqual(100.0, sum(i['percent'] for i in response['top_words']) )
+
diff --git a/common/lib/xmodule/xmodule/videoalpha_module.py b/common/lib/xmodule/xmodule/videoalpha_module.py
index a0a8f395ee..fe7ef5ba43 100644
--- a/common/lib/xmodule/xmodule/videoalpha_module.py
+++ b/common/lib/xmodule/xmodule/videoalpha_module.py
@@ -69,15 +69,16 @@ class VideoAlphaModule(VideoAlphaFields, XModule):
js = {
'js': [
- resource_string(__name__, 'js/src/videoalpha/display/initialize.js'),
- resource_string(__name__, 'js/src/videoalpha/display/html5_video.js'),
- resource_string(__name__, 'js/src/videoalpha/display/video_player.js'),
- resource_string(__name__, 'js/src/videoalpha/display/video_control.js'),
- resource_string(__name__, 'js/src/videoalpha/display/video_quality_control.js'),
- resource_string(__name__, 'js/src/videoalpha/display/video_progress_slider.js'),
- resource_string(__name__, 'js/src/videoalpha/display/video_volume_control.js'),
- resource_string(__name__, 'js/src/videoalpha/display/video_speed_control.js'),
- resource_string(__name__, 'js/src/videoalpha/display/video_caption.js'),
+ resource_string(__name__, 'js/src/videoalpha/helper_utils.js'),
+ resource_string(__name__, 'js/src/videoalpha/initialize.js'),
+ resource_string(__name__, 'js/src/videoalpha/html5_video.js'),
+ resource_string(__name__, 'js/src/videoalpha/video_player.js'),
+ resource_string(__name__, 'js/src/videoalpha/video_control.js'),
+ resource_string(__name__, 'js/src/videoalpha/video_quality_control.js'),
+ resource_string(__name__, 'js/src/videoalpha/video_progress_slider.js'),
+ resource_string(__name__, 'js/src/videoalpha/video_volume_control.js'),
+ resource_string(__name__, 'js/src/videoalpha/video_speed_control.js'),
+ resource_string(__name__, 'js/src/videoalpha/video_caption.js'),
resource_string(__name__, 'js/src/videoalpha/main.js')
]
}
@@ -141,11 +142,11 @@ class VideoAlphaModule(VideoAlphaFields, XModule):
if str_time is None:
return ''
else:
- obj_time = time.strptime(str_time, '%H:%M:%S')
+ x = time.strptime(str_time, '%H:%M:%S')
return datetime.timedelta(
- hours=obj_time.tm_hour,
- minutes=obj_time.tm_min,
- seconds=obj_time.tm_sec
+ hours=x.tm_hour,
+ minutes=x.tm_min,
+ seconds=x.tm_sec
).total_seconds()
return parse_time(xmltree.get('start_time')), parse_time(xmltree.get('end_time'))
diff --git a/lms/djangoapps/courseware/features/video.feature b/lms/djangoapps/courseware/features/video.feature
index 2b8d0f013a..7ba60c4f92 100644
--- a/lms/djangoapps/courseware/features/video.feature
+++ b/lms/djangoapps/courseware/features/video.feature
@@ -7,4 +7,4 @@ Feature: Video component
Scenario: Autoplay is enabled in the LMS for a VideoAlpha component
Given the course has a VideoAlpha component
- Then when I view the video it has autoplay enabled
+ Then when I view the videoalpha it has autoplay enabled
diff --git a/lms/djangoapps/courseware/features/video.py b/lms/djangoapps/courseware/features/video.py
index f95ffd9917..2e6665f6e8 100644
--- a/lms/djangoapps/courseware/features/video.py
+++ b/lms/djangoapps/courseware/features/video.py
@@ -8,10 +8,15 @@ from common import i_am_registered_for_the_course, section_location
@step('when I view the video it has autoplay enabled')
-def does_autoplay(_step):
+def does_autoplay_video(_step):
assert(world.css_find('.video')[0]['data-autoplay'] == 'True')
+@step('when I view the videoalpha it has autoplay enabled')
+def does_autoplay_videoalpha(_step):
+ assert(world.css_find('.videoalpha')[0]['data-autoplay'] == 'True')
+
+
@step('the course has a Video component')
def view_video(_step):
coursenum = 'test_course'
diff --git a/lms/djangoapps/courseware/tests/test_videoalpha_xml.py b/lms/djangoapps/courseware/tests/test_videoalpha_xml.py
index 6df957a0e5..eaa5d1f502 100644
--- a/lms/djangoapps/courseware/tests/test_videoalpha_xml.py
+++ b/lms/djangoapps/courseware/tests/test_videoalpha_xml.py
@@ -23,7 +23,7 @@ from django.conf import settings
from xmodule.videoalpha_module import VideoAlphaDescriptor, VideoAlphaModule
from xmodule.modulestore import Location
from xmodule.tests import get_test_system
-from xmodule.tests.test_logic import LogicTest
+from xmodule.tests import LogicTest
SOURCE_XML = """
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 4f796d8534..6c64cf1d90 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -35,7 +35,7 @@ from .discussionsettings import *
PLATFORM_NAME = "edX"
COURSEWARE_ENABLED = True
-ENABLE_JASMINE = True
+ENABLE_JASMINE = False
PERFSTATS = False