Commit 6ba5d472 by Anton Stupak Committed by Alexander Kryklia

Fix with sequence switching and more.

Studio: adds in new Sass file for Studio-specific
xmodule presentation and removes full screen control
from video alpha display UI.
Adds jasmine test for subtitles scroll.
Adds tests for caption change height functionality.
Uses gettext wrapper.
Fixes caption change height functionality.
Removes IS_CMS, isCms flags.
parent 65aa7d4f
...@@ -31,8 +31,6 @@ from path import path ...@@ -31,8 +31,6 @@ from path import path
############################ FEATURE CONFIGURATION ############################# ############################ FEATURE CONFIGURATION #############################
MITX_FEATURES = { MITX_FEATURES = {
'IS_CMS': True,
'USE_DJANGO_PIPELINE': True, 'USE_DJANGO_PIPELINE': True,
'GITHUB_PUSH': False, 'GITHUB_PUSH': False,
......
...@@ -66,6 +66,7 @@ ...@@ -66,6 +66,7 @@
// xmodule // xmodule
@import 'xmodule/modules/css/module-styles.scss'; @import 'xmodule/modules/css/module-styles.scss';
@import 'xmodule/descriptors/css/module-styles.scss'; @import 'xmodule/descriptors/css/module-styles.scss';
@import 'elements/xmodules'; // styling for Studio-specific contexts
@import 'shame'; // shame file - used for any bad-form/orphaned scss that knowingly violate edX FED architecture/standards (see - http://csswizardry.com/2013/04/shame-css/) @import 'shame'; // shame file - used for any bad-form/orphaned scss that knowingly violate edX FED architecture/standards (see - http://csswizardry.com/2013/04/shame-css/)
// studio - elements - xmodules
// ====================
// Video Alpha
.xmodule_VideoAlphaModule {
// display mode
&.xmodule_display {
// full screen
.video-controls .add-fullscreen {
display: none !important; // nasty, but needed to override the bad specificity of the xmodule css selectors
}
}
}
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
videoPlayer = state.videoPlayer; videoPlayer = state.videoPlayer;
videoCaption = state.videoCaption; videoCaption = state.videoCaption;
videoSpeedControl = state.videoSpeedControl; videoSpeedControl = state.videoSpeedControl;
videoControl = state.videoControl;
} }
beforeEach(function() { beforeEach(function() {
...@@ -72,6 +73,11 @@ ...@@ -72,6 +73,11 @@
expect($('.subtitles')).toHandleWith('mousewheel', videoCaption.onMovement); expect($('.subtitles')).toHandleWith('mousewheel', videoCaption.onMovement);
expect($('.subtitles')).toHandleWith('DOMMouseScroll', videoCaption.onMovement); expect($('.subtitles')).toHandleWith('DOMMouseScroll', videoCaption.onMovement);
}); });
it('bind the scroll', function() {
expect($('.subtitles')).toHandleWith('scroll', videoCaption.autoShowCaptions);
expect($('.subtitles')).toHandleWith('scroll', videoControl.showControls);
});
}); });
describe('when on a non touch-based device', function() { describe('when on a non touch-based device', function() {
...@@ -352,8 +358,28 @@ ...@@ -352,8 +358,28 @@
videoCaption.resize(); videoCaption.resize();
}); });
it('set the height of caption container', function() { describe('set the height of caption container', function(){
expect(parseInt($('.subtitles').css('maxHeight'), 10)).toBeCloseTo($('.video-wrapper').height(), 2); it('when CC button is enabled', function() {
var realHeight = parseInt($('.subtitles').css('maxHeight'), 10),
shouldBeHeight = $('.video-wrapper').height();
// Because of some problems with rounding on different enviroments:
// Linux * Mac * FF * Chrome
expect(realHeight).toBeCloseTo(shouldBeHeight, 2);
});
it('when CC button is disabled ', function() {
var realHeight = parseInt($('.subtitles').css('maxHeight'), 10),
videoWrapperHeight = $('.video-wrapper').height(),
controlsHeight = videoControl.el.height(),
progressSliderHeight = videoControl.sliderEl.height(),
shouldBeHeight = videoWrapperHeight - controlsHeight \
- 0.5 * controlsHeight;
state.captionsHidden = true;
videoCaption.setSubtitlesHeight();
expect(realHeight).toBeCloseTo($('.video-wrapper').height(shouldBeHeight, 2));
});
}); });
it('set the height of caption spacing', function() { it('set the height of caption spacing', function() {
......
...@@ -87,6 +87,9 @@ class @Sequence ...@@ -87,6 +87,9 @@ class @Sequence
modx_full_url = @modx_url + '/' + @id + '/goto_position' modx_full_url = @modx_url + '/' + @id + '/goto_position'
$.postWithPrefix modx_full_url, position: new_position $.postWithPrefix modx_full_url, position: new_position
# On Sequence change, fire custom event "sequence:change" on element.
# Added for aborting video bufferization, see ../videoalpha/10_main.js
@el.trigger "sequence:change"
@mark_active new_position @mark_active new_position
@$('#seq_content').html @contents.eq(new_position - 1).text() @$('#seq_content').html @contents.eq(new_position - 1).text()
XModule.loadModules(@$('#seq_content')) XModule.loadModules(@$('#seq_content'))
...@@ -140,7 +143,7 @@ class @Sequence ...@@ -140,7 +143,7 @@ class @Sequence
analytics.track "Accessed Next Sequential", analytics.track "Accessed Next Sequential",
sequence_id: @id sequence_id: @id
current_sequential: @position current_sequential: @position
target_sequential: new_position target_sequential: new_position
@render new_position @render new_position
......
...@@ -16,6 +16,10 @@ define( ...@@ -16,6 +16,10 @@ define(
['videoalpha/03_video_player.js'], ['videoalpha/03_video_player.js'],
function (VideoPlayer) { function (VideoPlayer) {
if (typeof(window.gettext) == "undefined") {
window.gettext = function(s){return s;};
}
/** /**
* @function * @function
* *
...@@ -47,6 +51,7 @@ function (VideoPlayer) { ...@@ -47,6 +51,7 @@ function (VideoPlayer) {
state.youtubeId = _.bind(youtubeId, state); state.youtubeId = _.bind(youtubeId, state);
state.getDuration = _.bind(getDuration, state); state.getDuration = _.bind(getDuration, state);
state.trigger = _.bind(trigger, state); state.trigger = _.bind(trigger, state);
state.stopBuffering = _.bind(stopBuffering, state);
// Old private functions. Now also public so that can be // Old private functions. Now also public so that can be
// tested by Jasmine. // tested by Jasmine.
...@@ -95,9 +100,7 @@ function (VideoPlayer) { ...@@ -95,9 +100,7 @@ function (VideoPlayer) {
my: 'top right', my: 'top right',
at: 'top center' at: 'top center'
} }
}, }
inCms: state.el.data('in-studio')
}; };
if (!(_parseYouTubeIDs(state))) { if (!(_parseYouTubeIDs(state))) {
...@@ -333,6 +336,18 @@ function (VideoPlayer) { ...@@ -333,6 +336,18 @@ function (VideoPlayer) {
} }
} }
function stopBuffering() {
var video;
if (this.videoType === 'html5'){
// HTML5 player haven't default way to abort bufferization.
// In this case we simply resetting source and call load().
video = this.videoPlayer.player.video;
video.src = '';
video.load();
}
}
function youtubeId(speed) { function youtubeId(speed) {
return this.videos[speed || this.speed]; return this.videos[speed || this.speed];
} }
......
...@@ -51,10 +51,6 @@ function () { ...@@ -51,10 +51,6 @@ function () {
state.videoControl.fullScreenState = false; state.videoControl.fullScreenState = false;
if (state.config.inCms === 'True') {
state.videoControl.fullScreenEl.hide();
}
if (!onTouchBasedDevice()) { if (!onTouchBasedDevice()) {
state.videoControl.pause(); state.videoControl.pause();
...@@ -133,12 +129,12 @@ function () { ...@@ -133,12 +129,12 @@ function () {
} }
function play() { function play() {
this.videoControl.playPauseEl.removeClass('play').addClass('pause').attr('title', 'Pause'); this.videoControl.playPauseEl.removeClass('play').addClass('pause').attr('title', gettext('Pause'));
this.videoControl.isPlaying = true; this.videoControl.isPlaying = true;
} }
function pause() { function pause() {
this.videoControl.playPauseEl.removeClass('pause').addClass('play').attr('title', 'Play'); this.videoControl.playPauseEl.removeClass('pause').addClass('play').attr('title', gettext('Play'));
this.videoControl.isPlaying = false; this.videoControl.isPlaying = false;
} }
...@@ -160,12 +156,12 @@ function () { ...@@ -160,12 +156,12 @@ function () {
this.videoControl.fullScreenState = false; this.videoControl.fullScreenState = false;
fullScreenClassNameEl.removeClass('video-fullscreen'); fullScreenClassNameEl.removeClass('video-fullscreen');
this.isFullScreen = false; this.isFullScreen = false;
this.videoControl.fullScreenEl.attr('title', 'Fullscreen'); this.videoControl.fullScreenEl.attr('title', gettext('Fullscreen'));
} else { } else {
this.videoControl.fullScreenState = true; this.videoControl.fullScreenState = true;
fullScreenClassNameEl.addClass('video-fullscreen'); fullScreenClassNameEl.addClass('video-fullscreen');
this.isFullScreen = true; this.isFullScreen = true;
this.videoControl.fullScreenEl.attr('title', 'Exit fullscreen'); this.videoControl.fullScreenEl.attr('title', gettext('Exit fullscreen'));
} }
this.trigger('videoCaption.resize', null); this.trigger('videoCaption.resize', null);
......
...@@ -43,7 +43,7 @@ function () { ...@@ -43,7 +43,7 @@ function () {
state.videoCaption.hideCaptions = _.bind(hideCaptions, state); state.videoCaption.hideCaptions = _.bind(hideCaptions, state);
state.videoCaption.calculateOffset = _.bind(calculateOffset, state); state.videoCaption.calculateOffset = _.bind(calculateOffset, state);
state.videoCaption.updatePlayTime = _.bind(updatePlayTime, state); state.videoCaption.updatePlayTime = _.bind(updatePlayTime, state);
state.videoCaption.setSubtitlesHeight = _.bind(setSubtitlesHeight, state); state.videoCaption.setSubtitlesHeight = _.bind(setSubtitlesHeight, state);
state.videoCaption.renderElements = _.bind(renderElements, state); state.videoCaption.renderElements = _.bind(renderElements, state);
state.videoCaption.bindHandlers = _.bind(bindHandlers, state); state.videoCaption.bindHandlers = _.bind(bindHandlers, state);
...@@ -71,9 +71,8 @@ function () { ...@@ -71,9 +71,8 @@ function () {
this.el.find('.video-wrapper').after(this.videoCaption.subtitlesEl); this.el.find('.video-wrapper').after(this.videoCaption.subtitlesEl);
this.el.find('.video-controls .secondary-controls').append(this.videoCaption.hideSubtitlesEl); this.el.find('.video-controls .secondary-controls').append(this.videoCaption.hideSubtitlesEl);
this.videoCaption.setSubtitlesHeight();
this.videoCaption.fetchCaption(); this.videoCaption.fetchCaption();
this.videoCaption.setSubtitlesHeight();
if (this.videoType === 'html5') { if (this.videoType === 'html5') {
this.videoCaption.fadeOutTimeout = this.config.fadeOutTimeout; this.videoCaption.fadeOutTimeout = this.config.fadeOutTimeout;
...@@ -133,7 +132,7 @@ function () { ...@@ -133,7 +132,7 @@ function () {
if (onTouchBasedDevice()) { if (onTouchBasedDevice()) {
_this.videoCaption.subtitlesEl.find('li').html( _this.videoCaption.subtitlesEl.find('li').html(
'Caption will be displayed when you start playing the video.' gettext('Caption will be displayed when you start playing the video.')
); );
} else { } else {
_this.videoCaption.renderCaption(); _this.videoCaption.renderCaption();
...@@ -189,14 +188,13 @@ function () { ...@@ -189,14 +188,13 @@ function () {
} }
function resize() { function resize() {
this.videoCaption.setSubtitlesHeight();
this.videoCaption.subtitlesEl this.videoCaption.subtitlesEl
.find('.spacing:first').height(this.videoCaption.topSpacingHeight()) .find('.spacing:first').height(this.videoCaption.topSpacingHeight())
.find('.spacing:last').height(this.videoCaption.bottomSpacingHeight()); .find('.spacing:last').height(this.videoCaption.bottomSpacingHeight());
this.videoCaption.scrollCaption(); this.videoCaption.scrollCaption();
this.videoCaption.setSubtitlesHeight();
} }
function onMouseEnter() { function onMouseEnter() {
...@@ -363,12 +361,12 @@ function () { ...@@ -363,12 +361,12 @@ function () {
if (hide_captions) { if (hide_captions) {
type = 'hide_transcript'; type = 'hide_transcript';
this.captionsHidden = true; this.captionsHidden = true;
this.videoCaption.hideSubtitlesEl.attr('title', 'Turn on captions'); this.videoCaption.hideSubtitlesEl.attr('title', gettext('Turn on captions'));
this.el.addClass('closed'); this.el.addClass('closed');
} else { } else {
type = 'show_transcript'; type = 'show_transcript';
this.captionsHidden = false; this.captionsHidden = false;
this.videoCaption.hideSubtitlesEl.attr('title', 'Turn off captions'); this.videoCaption.hideSubtitlesEl.attr('title', gettext('Turn off captions'));
this.el.removeClass('closed'); this.el.removeClass('closed');
this.videoCaption.scrollCaption(); this.videoCaption.scrollCaption();
} }
...@@ -379,6 +377,8 @@ function () { ...@@ -379,6 +377,8 @@ function () {
}); });
} }
this.videoCaption.setSubtitlesHeight();
$.cookie('hide_captions', hide_captions, { $.cookie('hide_captions', hide_captions, {
expires: 3650, expires: 3650,
path: '/' path: '/'
...@@ -387,7 +387,9 @@ function () { ...@@ -387,7 +387,9 @@ function () {
function captionHeight() { function captionHeight() {
if (this.isFullScreen) { if (this.isFullScreen) {
return $(window).height() - this.el.find('.video-controls').height(); return $(window).height() - this.el.find('.video-controls').height() -
0.5 * this.videoControl.sliderEl.height() -
2 * parseInt(this.videoCaption.subtitlesEl.css('padding-top'), 10);
} else { } else {
return this.el.find('.video-wrapper').height(); return this.el.find('.video-wrapper').height();
} }
...@@ -395,15 +397,18 @@ function () { ...@@ -395,15 +397,18 @@ function () {
function setSubtitlesHeight() { function setSubtitlesHeight() {
var height = 0; var height = 0;
if (this.videoType === 'html5') if (this.videoType === 'html5'){
if ( (this.captionsHidden === undefined && this.hide_captions === true ) || // on page load captionHidden = undefined
(this.captionsHidden === true) ) { if (
// In case of html5 autoshowing subtitles, (this.captionsHidden === undefined && this.hide_captions === true ) ||
// we ajdust height of subs, by height of scrollbar (this.captionsHidden === true) ) {
height = this.videoControl.el.height() + this.videoControl.sliderEl.height() / 2; // In case of html5 autoshowing subtitles,
// height of videoControl does not contain height of slider. // we ajdust height of subs, by height of scrollbar
// (css is set to absolute, to avoid yanking when slider autochanges its height) height = this.videoControl.el.height() + 0.5 * this.videoControl.sliderEl.height();
} // height of videoControl does not contain height of slider.
// (css is set to absolute, to avoid yanking when slider autochanges its height)
}
}
this.videoCaption.subtitlesEl.css({ this.videoCaption.subtitlesEl.css({
maxHeight: this.videoCaption.captionHeight() - height maxHeight: this.videoCaption.captionHeight() - height
}); });
......
...@@ -34,6 +34,17 @@ function ( ...@@ -34,6 +34,17 @@ function (
window.VideoAlpha = function (element) { window.VideoAlpha = function (element) {
var state; var state;
// Stop bufferization of previous video on sequence change.
// Problem: multiple video tags with the same src cannot
// play together. The second tag waiting when first video will be fully loaded.
// That's why we abort bufferization forcibly.
$(element).closest('.sequence').bind('sequence:change', function(e){
if (previousState !== null && typeof previousState.videoPlayer !== 'undefined') {
previousState.stopBuffering();
$(e.currentTarget).unbind('sequence:change');
}
});
// Check for existance of previous state, uninitialize it if necessary, and create a new state. // 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. // Store new state for future invocation of this module consturctor function.
if (previousState !== null && typeof previousState.videoPlayer !== 'undefined') { if (previousState !== null && typeof previousState.videoPlayer !== 'undefined') {
......
...@@ -14,12 +14,12 @@ class PollModuleTest(LogicTest): ...@@ -14,12 +14,12 @@ class PollModuleTest(LogicTest):
} }
def test_bad_ajax_request(self): def test_bad_ajax_request(self):
"Make sure that answer for incorrect request is error json" # Make sure that answer for incorrect request is error json.
response = self.ajax_request('bad_answer', {}) response = self.ajax_request('bad_answer', {})
self.assertDictEqual(response, {'error': 'Unknown Command!'}) self.assertDictEqual(response, {'error': 'Unknown Command!'})
def test_good_ajax_request(self): def test_good_ajax_request(self):
"Make shure that ajax request works correctly" # Make sure that ajax request works correctly.
response = self.ajax_request('No', {}) response = self.ajax_request('No', {})
poll_answers = response['poll_answers'] poll_answers = response['poll_answers']
......
...@@ -141,11 +141,11 @@ class VideoAlphaModule(VideoAlphaFields, XModule): ...@@ -141,11 +141,11 @@ class VideoAlphaModule(VideoAlphaFields, XModule):
if str_time is None: if str_time is None:
return '' return ''
else: else:
x = time.strptime(str_time, '%H:%M:%S') obj_time = time.strptime(str_time, '%H:%M:%S')
return datetime.timedelta( return datetime.timedelta(
hours=x.tm_hour, hours=obj_time.tm_hour,
minutes=x.tm_min, minutes=obj_time.tm_min,
seconds=x.tm_sec seconds=obj_time.tm_sec
).total_seconds() ).total_seconds()
return parse_time(xmltree.get('start_time')), parse_time(xmltree.get('end_time')) return parse_time(xmltree.get('start_time')), parse_time(xmltree.get('end_time'))
......
...@@ -46,7 +46,6 @@ DISCUSSION_SETTINGS = { ...@@ -46,7 +46,6 @@ DISCUSSION_SETTINGS = {
# Features # Features
MITX_FEATURES = { MITX_FEATURES = {
'IS_CMS': False,
'SAMPLE': False, 'SAMPLE': False,
'USE_DJANGO_PIPELINE': True, 'USE_DJANGO_PIPELINE': True,
'DISPLAY_HISTOGRAMS_TO_STAFF': True, 'DISPLAY_HISTOGRAMS_TO_STAFF': True,
......
...@@ -12,8 +12,6 @@ ...@@ -12,8 +12,6 @@
data-streams="${youtube_streams}" data-streams="${youtube_streams}"
% endif % endif
data-in-studio="${settings.MITX_FEATURES['IS_CMS']}"
${'data-sub="{}"'.format(sub) if sub else ''} ${'data-sub="{}"'.format(sub) if sub else ''}
${'data-autoplay="{}"'.format(autoplay) if autoplay else ''} ${'data-autoplay="{}"'.format(autoplay) if autoplay else ''}
% if not settings.MITX_FEATURES['STUB_VIDEO_FOR_TESTING']: % if not settings.MITX_FEATURES['STUB_VIDEO_FOR_TESTING']:
...@@ -40,13 +38,13 @@ ...@@ -40,13 +38,13 @@
<div class="slider"></div> <div class="slider"></div>
<div> <div>
<ul class="vcr"> <ul class="vcr">
<li><a class="video_control" href="#" title="Play"></a></li> <li><a class="video_control" href="#" title="${_('Play')}"></a></li>
<li><div class="vidtime">0:00 / 0:00</div></li> <li><div class="vidtime">0:00 / 0:00</div></li>
</ul> </ul>
<div class="secondary-controls"> <div class="secondary-controls">
<div class="speeds"> <div class="speeds">
<a href="#"> <a href="#">
<h3>Speed</h3> <h3>${_('Speed')}</h3>
<p class="active"></p> <p class="active"></p>
</a> </a>
<ol class="video_speeds"></ol> <ol class="video_speeds"></ol>
...@@ -57,11 +55,11 @@ ...@@ -57,11 +55,11 @@
<div class="volume-slider"></div> <div class="volume-slider"></div>
</div> </div>
</div> </div>
<a href="#" class="add-fullscreen" title="Fill browser">Fill Browser</a> <a href="#" class="add-fullscreen" title="${_('Fill browser')}">${_('Fill browser')}</a>
<a href="#" class="quality_control" title="HD">HD</a> <a href="#" class="quality_control" title="${_('HD')}">${_('HD')}</a>
% if show_captions == 'true': % if show_captions == 'true':
<a href="#" class="hide-subtitles" title="Turn off captions">Captions</a> <a href="#" class="hide-subtitles" title="${_('Turn off captions')}">${_('Captions')}</a>
% endif % endif
</div> </div>
</div> </div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment