diff --git a/common/lib/xmodule/xmodule/css/video/display.scss b/common/lib/xmodule/xmodule/css/video/display.scss index ed0131c54c..989ea33a26 100644 --- a/common/lib/xmodule/xmodule/css/video/display.scss +++ b/common/lib/xmodule/xmodule/css/video/display.scss @@ -74,10 +74,8 @@ div.video { } section.video-player { - height: 0; overflow: hidden; - padding-bottom: 56.25%; - position: relative; + min-height: 300px; div { &.hidden { @@ -85,12 +83,9 @@ div.video { } } - object, iframe { + object, iframe, video { border: none; height: 100%; - left: 0; - position: absolute; - top: 0; width: 100%; } @@ -701,15 +696,12 @@ div.video { display: table-cell; vertical-align: middle; float: none; - } - object, iframe { - bottom: 0; - height: 100%; - left: 0; - overflow: hidden; - position: fixed; - top: 0; + object, iframe, video{ + position: absolute; + width: auto; + height: auto; + } } section.video-controls { diff --git a/common/lib/xmodule/xmodule/js/spec/video/resizer_spec.js b/common/lib/xmodule/xmodule/js/spec/video/resizer_spec.js new file mode 100644 index 0000000000..a05e9e8bb8 --- /dev/null +++ b/common/lib/xmodule/xmodule/js/spec/video/resizer_spec.js @@ -0,0 +1,93 @@ +(function (requirejs, require, define) { + +require( +['video/00_resizer.js'], +function (Resizer) { + + describe('Resizer', function () { + var html = [ + '
', + '
', + 'Content', + '
', + '
' + ].join(''), + config, container, element; + + beforeEach(function () { + setFixtures(html); + + container = $('.rszr-wrapper'); + element = $('.rszr-el'); + config = { + container: container, + element: element + }; + }); + + it('When Initialize without required parameters, log message is shown', + function () { + spyOn(console, 'log'); + + new Resizer({ }); + expect(console.log).toHaveBeenCalled(); + } + ); + + it('`alignByWidthOnly` works correctly', function () { + var resizer = new Resizer(config).alignByWidthOnly(), + expectedWidth = container.width(), + realWidth = element.width(); + + expect(realWidth).toBe(expectedWidth); + }); + + it('`alignByHeightOnly` works correctly', function () { + var resizer = new Resizer(config).alignByHeightOnly(), + expectedHeight = container.height(), + realHeight = element.height(), + realWidth; + + expect(realHeight).toBe(expectedHeight); + }); + + it('`align` works correctly', function () { + var resizer = new Resizer(config).align(), + expectedHeight = container.height(), + realHeight = element.height(), + expectedWidth = 50; + + // containerRatio >= elementRatio + expect(realHeight).toBe(expectedHeight); + + // containerRatio < elementRatio + container.width(expectedWidth); + resizer.align(); + realWidth = element.width(); + + expect(realWidth).toBe(expectedWidth); + + }); + + it('`setMode` works correctly', function () { + var resizer = new Resizer(config).setMode('height'), + expectedHeight = container.height(), + realHeight = element.height(), + expectedWidth = 50; + + // containerRatio >= elementRatio + expect(realHeight).toBe(expectedHeight); + + // containerRatio < elementRatio + container.width(expectedWidth); + resizer.setMode('width'); + realWidth = element.width(); + + expect(realWidth).toBe(expectedWidth); + }); + + }); +}); + + +}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js index 00f51a4c80..3f5e1831ac 100644 --- a/common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js +++ b/common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js @@ -10,6 +10,8 @@ } state = new Video('#example'); + + state.videoEl = $('video, iframe'); videoPlayer = state.videoPlayer; player = videoPlayer.player; videoControl = state.videoControl; @@ -17,6 +19,19 @@ videoProgressSlider = state.videoProgressSlider; videoSpeedControl = state.videoSpeedControl; videoVolumeControl = state.videoVolumeControl; + + state.resizer = (function () { + var methods = [ + 'align', 'alignByWidthOnly', 'alignByHeightOnly', 'setParams', 'setMode' + ], + obj = {}; + + $.each(methods, function(index, method) { + obj[method] = jasmine.createSpy(method).andReturn(obj); + }); + + return obj; + })(); } function initializeYouTube() { @@ -557,6 +572,7 @@ it('tell VideoCaption to resize', function() { expect(videoCaption.resize).toHaveBeenCalled(); + expect(state.resizer.setMode).toHaveBeenCalled(); }); }); @@ -583,6 +599,7 @@ it('tell VideoCaption to resize', function() { expect(videoCaption.resize).toHaveBeenCalled(); + expect(state.resizer.setMode).toHaveBeenCalledWith('width'); }); }); }); diff --git a/common/lib/xmodule/xmodule/js/src/video/00_resizer.js b/common/lib/xmodule/xmodule/js/src/video/00_resizer.js new file mode 100644 index 0000000000..e715fc930f --- /dev/null +++ b/common/lib/xmodule/xmodule/js/src/video/00_resizer.js @@ -0,0 +1,134 @@ +(function (requirejs, require, define) { + +define( +'video/00_resizer.js', +[], +function () { + + var Resizer = function (params) { + var defaults = { + container: window, + element: null, + containerRatio: null, + elementRatio: null + }, + mode = null, + config; + + var initialize = function (params) { + if (config) { + config = $.extend(true, config, params); + } else { + config = $.extend(true, {}, defaults, params); + } + + if (!config.element) { + console.log('Required parameter `element` is not passed.'); + } + + return this; + }; + + var getData = function () { + var container = $(config.container), + containerWidth = container.width(), + containerHeight = container.height(), + containerRatio = config.containerRatio, + + element = $(config.element), + elementRatio = config.elementRatio; + + if (!containerRatio) { + containerRatio = containerWidth/containerHeight; + } + + if (!elementRatio) { + elementRatio = element.width()/element.height(); + } + + return { + containerWidth: containerWidth, + containerHeight: containerHeight, + containerRatio: containerRatio, + element: element, + elementRatio: elementRatio + }; + }; + + var align = function() { + var data = getData(); + + switch (mode) { + case 'height': + alignByHeightOnly(); + break; + + case 'width': + alignByWidthOnly(); + break; + + default: + if (data.containerRatio >= data.elementRatio) { + alignByHeightOnly(); + + } else { + alignByWidthOnly(); + } + break; + } + + return this; + }; + + var alignByWidthOnly = function () { + var data = getData(), + height = data.containerWidth/data.elementRatio; + + data.element.css({ + 'height': height, + 'width': data.containerWidth, + 'top': 0.5*(data.containerHeight - height), + 'left': 0 + }); + + return this; + }; + + var alignByHeightOnly = function () { + var data = getData(), + width = data.containerHeight*data.elementRatio; + + data.element.css({ + 'height': data.containerHeight, + 'width': data.containerHeight*data.elementRatio, + 'top': 0, + 'left': 0.5*(data.containerWidth - width) + }); + + return this; + }; + + var setMode = function (param) { + if (_.isString(param)) { + mode = param; + align(); + } + + return this; + }; + + initialize.apply(this, arguments); + + return { + align: align, + alignByWidthOnly: alignByWidthOnly, + alignByHeightOnly: alignByHeightOnly, + setParams: initialize, + setMode: setMode + }; + }; + + return Resizer; +}); + +}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); diff --git a/common/lib/xmodule/xmodule/js/src/video/02_html5_video.js b/common/lib/xmodule/xmodule/js/src/video/02_html5_video.js index 541c5b0bf4..6cd38b30bd 100644 --- a/common/lib/xmodule/xmodule/js/src/video/02_html5_video.js +++ b/common/lib/xmodule/xmodule/js/src/video/02_html5_video.js @@ -216,10 +216,6 @@ function () { // currently doing. this.videoEl = $(this.video); - this.videoEl.css({ - 'width': '100%' - }); - this.playerState = HTML5Video.PlayerState.UNSTARTED; // this.callStateChangeCallback(); @@ -318,6 +314,6 @@ function () { // HTML5Video object - what this module exports. return HTML5Video; -}) +}); }(RequireJS.requirejs, RequireJS.require, RequireJS.define)); diff --git a/common/lib/xmodule/xmodule/js/src/video/03_video_player.js b/common/lib/xmodule/xmodule/js/src/video/03_video_player.js index 0fcbe42578..455d00c120 100644 --- a/common/lib/xmodule/xmodule/js/src/video/03_video_player.js +++ b/common/lib/xmodule/xmodule/js/src/video/03_video_player.js @@ -3,8 +3,8 @@ // VideoPlayer module. define( 'video/03_video_player.js', -['video/02_html5_video.js'], -function (HTML5Video) { +['video/02_html5_video.js', 'video/00_resizer.js' ], +function (HTML5Video, Resizer) { // VideoPlayer() function - what this module "exports". return function (state) { @@ -343,7 +343,8 @@ function (HTML5Video) { } function onReady() { - var availablePlaybackRates, baseSpeedSubs, _this; + var availablePlaybackRates, baseSpeedSubs, _this, + player, videoWidth, videoHeight; this.videoPlayer.log('load_video'); @@ -420,6 +421,27 @@ function (HTML5Video) { this.videoPlayer.player.setPlaybackRate(this.speed); } + if (this.videoType === 'html5') { + player = this.videoEl = this.videoPlayer.player.videoEl; + videoWidth = player[0].videoWidth || player.width(); + videoHeight = player[0].videoHeight || player.height(); + } else { + player = this.videoEl = this.el.find('iframe'); + videoWidth = player.attr('width') || player.width(); + videoHeight = player.attr('height') || player.height(); + } + + this.resizer = new Resizer({ + element: this.videoEl, + elementRatio: videoWidth/videoHeight, + container: this.videoEl.parent() + }) + .setMode('width'); + + this.trigger('videoCaption.resize', null); + $(window).bind('resize', _.debounce(this.resizer.align, 100)); + + /* The following has been commented out to make sure autoplay is disabled for students. if ( diff --git a/common/lib/xmodule/xmodule/js/src/video/04_video_control.js b/common/lib/xmodule/xmodule/js/src/video/04_video_control.js index 8ba7490cef..9710502aae 100644 --- a/common/lib/xmodule/xmodule/js/src/video/04_video_control.js +++ b/common/lib/xmodule/xmodule/js/src/video/04_video_control.js @@ -170,22 +170,41 @@ function () { function toggleFullScreen(event) { event.preventDefault(); - var fullScreenClassNameEl = this.el.add(document.documentElement); + var fullScreenClassNameEl = this.el.add(document.documentElement), + win = $(window), + text; if (this.videoControl.fullScreenState) { - this.videoControl.fullScreenState = false; + this.videoControl.fullScreenState = this.isFullScreen = false; fullScreenClassNameEl.removeClass('video-fullscreen'); - this.isFullScreen = false; - this.videoControl.fullScreenEl.attr('title', gettext('Fill browser')) - .text(gettext('Fill browser')); + text = gettext('Fill browser'); + + this.resizer + .setParams({ + container: this.videoEl.parent() + }) + .setMode('width'); + + win.scrollTop(this.scrollPos); } else { - this.videoControl.fullScreenState = true; + this.scrollPos = win.scrollTop(); + win.scrollTop(0); + this.videoControl.fullScreenState = this.isFullScreen = true; fullScreenClassNameEl.addClass('video-fullscreen'); - this.isFullScreen = true; - this.videoControl.fullScreenEl.attr('title', gettext('Exit full browser')) - .text(gettext('Exit full browser')); + text = gettext('Exit full browser'); + + this.resizer + .setParams({ + container: window + }) + .setMode('both'); + } + this.videoControl.fullScreenEl + .attr('title', text) + .text(text); + this.trigger('videoCaption.resize', null); } diff --git a/common/lib/xmodule/xmodule/js/src/video/09_video_caption.js b/common/lib/xmodule/xmodule/js/src/video/09_video_caption.js index 21cc64e81c..668281b533 100644 --- a/common/lib/xmodule/xmodule/js/src/video/09_video_caption.js +++ b/common/lib/xmodule/xmodule/js/src/video/09_video_caption.js @@ -700,7 +700,7 @@ function () { function hideCaptions(hide_captions, update_cookie) { var hideSubtitlesEl = this.videoCaption.hideSubtitlesEl, - type; + type, text; if (typeof update_cookie === 'undefined') { update_cookie = true; @@ -710,29 +710,33 @@ function () { type = 'hide_transcript'; this.captionsHidden = true; - hideSubtitlesEl - .attr('title', gettext('Turn on captions')) - .text(gettext('Turn on captions')); - this.el.addClass('closed'); + + text = gettext('Turn on captions'); } else { type = 'show_transcript'; this.captionsHidden = false; - hideSubtitlesEl - .attr('title', gettext('Turn off captions')) - .text(gettext('Turn off captions')); - this.el.removeClass('closed'); this.videoCaption.scrollCaption(); + + text = gettext('Turn off captions'); } + hideSubtitlesEl + .attr('title', text) + .text(gettext(text)); + if (this.videoPlayer) { this.videoPlayer.log(type, { currentTime: this.videoPlayer.currentTime }); } + if (this.resizer) { + this.resizer.alignByWidthOnly(); + } + this.videoCaption.setSubtitlesHeight(); if (update_cookie) { diff --git a/common/lib/xmodule/xmodule/video_module.py b/common/lib/xmodule/xmodule/video_module.py index 764456d582..944a86d73b 100644 --- a/common/lib/xmodule/xmodule/video_module.py +++ b/common/lib/xmodule/xmodule/video_module.py @@ -135,6 +135,7 @@ class VideoModule(VideoFields, XModule): js = { 'js': [ + resource_string(__name__, 'js/src/video/00_resizer.js'), resource_string(__name__, 'js/src/video/01_initialize.js'), resource_string(__name__, 'js/src/video/025_focus_grabber.js'), resource_string(__name__, 'js/src/video/02_html5_video.js'),