Commit 2997a80a by polesye Committed by jmclaus

Video link obfuscation. [BLD-1230]

parent 7593ad3a
...@@ -17,11 +17,6 @@ ...@@ -17,11 +17,6 @@
.xmodule_VideoModule { .xmodule_VideoModule {
// display mode // display mode
&.xblock-student_view { &.xblock-student_view {
// full screen
.video-controls .add-fullscreen {
display: none !important; // nasty, but needed to override the bad specificity of the xmodule css selectors
}
.video-tracks { .video-tracks {
.a11y-menu-container { .a11y-menu-container {
.a11y-menu-list { .a11y-menu-list {
......
...@@ -131,3 +131,96 @@ $a11y--blue-s1: saturate($blue,15%); ...@@ -131,3 +131,96 @@ $a11y--blue-s1: saturate($blue,15%);
} }
} }
} }
.contextmenu, .submenu {
border: 1px solid #333;
background: #fff;
color: #333;
padding: 0;
margin: 0;
list-style: none;
position: absolute;
top: 0;
display: none;
z-index: 999999;
outline: none;
cursor: default;
white-space: nowrap;
&.is-opened {
display: block;
}
.menu-item, .submenu-item {
border-top: 1px solid #ccc;
padding: 5px 10px;
outline: none;
& > span {
color: #333;
}
&:first-child {
border-top: none;
}
&:focus {
background: #333;
color: #fff;
& > span {
color: #fff;
}
}
}
.submenu-item {
position: relative;
padding: 5px 20px 5px 10px;
&:after {
content: '\25B6';
position: absolute;
right: 5px;
line-height: 25px;
font-size: 10px;
}
.submenu {
display: none;
}
&.is-opened {
background: #333;
color: #fff;
& > span {
color: #fff;
}
& > .submenu {
display: block;
}
}
.is-selected {
font-weight: bold;
}
}
.is-disabled {
pointer-events: none;
color: #ccc;
}
}
.overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 900000;
background-color: transparent;
}
...@@ -689,8 +689,9 @@ div.video { ...@@ -689,8 +689,9 @@ div.video {
position: fixed; position: fixed;
top: 0; top: 0;
width: 100%; width: 100%;
z-index: 999; z-index: 9999;
vertical-align: middle; vertical-align: middle;
border-radius: 0;
&.closed { &.closed {
div.tc-wrapper { div.tc-wrapper {
......
(function (define) {
'use strict';
define('video/00_component.js', [],
function () {
/**
* Creates a new object with the specified prototype object and properties.
* @param {Object} o The object which should be the prototype of the
* newly-created object.
* @private
* @throws {TypeError, Error}
* @return {Object}
*/
var inherit = Object.create || (function () {
var F = function () {};
return function (o) {
if (arguments.length > 1) {
throw Error('Second argument not supported');
}
if (_.isNull(o) || _.isUndefined(o)) {
throw Error('Cannot set a null [[Prototype]]');
}
if (!_.isObject(o)) {
throw TypeError('Argument must be an object');
}
F.prototype = o;
return new F();
};
})();
/**
* Component module.
* @exports video/00_component.js
* @constructor
* @return {jquery Promise}
*/
var Component = function () {
if ($.isFunction(this.initialize)) {
return this.initialize.apply(this, arguments);
}
};
/**
* Returns new constructor that inherits form the current constructor.
* @static
* @param {Object} protoProps The object containing which will be added to
* the prototype.
* @return {Object}
*/
Component.extend = function (protoProps, staticProps) {
var Parent = this,
Child = function () {
if ($.isFunction(this.initialize)) {
return this.initialize.apply(this, arguments);
}
};
// Inherit methods and properties from the Parent prototype.
Child.prototype = inherit(Parent.prototype);
Child.constructor = Parent;
// Provide access to parent's methods and properties
Child.__super__ = Parent.prototype;
// Extends inherited methods and properties by methods/properties
// passed as argument.
if (protoProps) {
$.extend(Child.prototype, protoProps);
}
// Inherit static methods and properties
$.extend(Child, Parent, staticProps);
return Child;
};
return Component;
});
}(RequireJS.define));
...@@ -11,6 +11,13 @@ function() { ...@@ -11,6 +11,13 @@ function() {
*/ */
return { return {
'Play': gettext('Play'),
'Pause': gettext('Pause'),
'Mute': gettext('Mute'),
'Unmute': gettext('Unmute'),
'Exit full browser': gettext('Exit full browser'),
'Fill browser': gettext('Fill browser'),
'Speed': gettext('Speed'),
'Volume': gettext('Volume'), 'Volume': gettext('Volume'),
// Translators: Volume level equals 0%. // Translators: Volume level equals 0%.
'Muted': gettext('Muted'), 'Muted': gettext('Muted'),
......
...@@ -30,7 +30,7 @@ function () { ...@@ -30,7 +30,7 @@ function () {
// get the 'state' object as a context. // get the 'state' object as a context.
function _makeFunctionsPublic(state) { function _makeFunctionsPublic(state) {
var methodsDict = { var methodsDict = {
exitFullScreen: exitFullScreen, exitFullScreenHandler: exitFullScreenHandler,
hideControls: hideControls, hideControls: hideControls,
hidePlayPlaceholder: hidePlayPlaceholder, hidePlayPlaceholder: hidePlayPlaceholder,
pause: pause, pause: pause,
...@@ -39,6 +39,7 @@ function () { ...@@ -39,6 +39,7 @@ function () {
showControls: showControls, showControls: showControls,
showPlayPlaceholder: showPlayPlaceholder, showPlayPlaceholder: showPlayPlaceholder,
toggleFullScreen: toggleFullScreen, toggleFullScreen: toggleFullScreen,
toggleFullScreenHandler: toggleFullScreenHandler,
togglePlayback: togglePlayback, togglePlayback: togglePlayback,
updateControlsHeight: updateControlsHeight, updateControlsHeight: updateControlsHeight,
updateVcrVidTime: updateVcrVidTime updateVcrVidTime: updateVcrVidTime
...@@ -93,7 +94,7 @@ function () { ...@@ -93,7 +94,7 @@ function () {
// Bind any necessary function callbacks to DOM events (click, mousemove, etc.). // Bind any necessary function callbacks to DOM events (click, mousemove, etc.).
function _bindHandlers(state) { function _bindHandlers(state) {
state.videoControl.playPauseEl.on('click', state.videoControl.togglePlayback); state.videoControl.playPauseEl.on('click', state.videoControl.togglePlayback);
state.videoControl.fullScreenEl.on('click', state.videoControl.toggleFullScreen); state.videoControl.fullScreenEl.on('click', state.videoControl.toggleFullScreenHandler);
state.el.on('fullscreen', function (event, isFullScreen) { state.el.on('fullscreen', function (event, isFullScreen) {
var height = state.videoControl.updateControlsHeight(); var height = state.videoControl.updateControlsHeight();
...@@ -111,7 +112,7 @@ function () { ...@@ -111,7 +112,7 @@ function () {
} }
}); });
$(document).on('keyup', state.videoControl.exitFullScreen); $(document).on('keyup', state.videoControl.exitFullScreenHandler);
if ((state.videoType === 'html5') && (state.config.autohideHtml5)) { if ((state.videoType === 'html5') && (state.config.autohideHtml5)) {
state.el.on('mousemove', state.videoControl.showControls); state.el.on('mousemove', state.videoControl.showControls);
...@@ -246,19 +247,22 @@ function () { ...@@ -246,19 +247,22 @@ function () {
function togglePlayback(event) { function togglePlayback(event) {
event.preventDefault(); event.preventDefault();
this.videoCommands.execute('togglePlayback');
if (this.videoControl.isPlaying) {
this.trigger('videoPlayer.pause', null);
} else {
this.trigger('videoPlayer.play', null);
}
} }
function toggleFullScreen(event) { /**
* Event handler to toggle fullscreen mode.
* @param {jquery Event} event
*/
function toggleFullScreenHandler(event) {
event.preventDefault(); event.preventDefault();
this.videoCommands.execute('toggleFullScreen');
}
/** Toggle fullscreen mode. */
function toggleFullScreen() {
var fullScreenClassNameEl = this.el.add(document.documentElement), var fullScreenClassNameEl = this.el.add(document.documentElement),
win = $(window), win = $(window), text;
text;
if (this.videoControl.fullScreenState) { if (this.videoControl.fullScreenState) {
this.videoControl.fullScreenState = this.isFullScreen = false; this.videoControl.fullScreenState = this.isFullScreen = false;
...@@ -280,9 +284,14 @@ function () { ...@@ -280,9 +284,14 @@ function () {
this.el.trigger('fullscreen', [this.isFullScreen]); this.el.trigger('fullscreen', [this.isFullScreen]);
} }
function exitFullScreen(event) { /**
* Event handler to exit from fullscreen mode.
* @param {jquery Event} event
*/
function exitFullScreenHandler(event) {
if ((this.isFullScreen) && (event.keyCode === 27)) { if ((this.isFullScreen) && (event.keyCode === 27)) {
this.videoControl.toggleFullScreen(event); event.preventDefault();
this.videoCommands.execute('toggleFullScreen');
} }
} }
......
...@@ -198,7 +198,7 @@ function (Iterator) { ...@@ -198,7 +198,7 @@ function (Iterator) {
var speed = $(event.currentTarget).parent().data('speed'); var speed = $(event.currentTarget).parent().data('speed');
this.closeMenu(); this.closeMenu();
this.setSpeed(this.state.speedToString(speed)); this.state.videoCommands.execute('speed', speed);
return false; return false;
}, },
......
(function(define) {
'use strict';
// VideoCommands module.
define('video/10_commands.js', [], function() {
var VideoCommands, Command, playCommand, pauseCommand, togglePlaybackCommand,
muteCommand, unmuteCommand, toggleMuteCommand, toggleFullScreenCommand,
setSpeedCommand;
/**
* Video commands module.
* @exports video/10_commands.js
* @constructor
* @param {Object} state The object containing the state of the video
* @param {Object} i18n The object containing strings with translations.
* @return {jquery Promise}
*/
VideoCommands = function(state, i18n) {
if (!(this instanceof VideoCommands)) {
return new VideoCommands(state, i18n);
}
this.state = state;
this.state.videoCommands = this;
this.i18n = i18n;
this.commands = [];
this.initialize();
return $.Deferred().resolve().promise();
};
VideoCommands.prototype = {
/** Initializes the module. */
initialize: function() {
this.commands = this.getCommands();
},
execute: function (command) {
var args = [].slice.call(arguments, 1) || [];
if (_.has(this.commands, command)) {
this.commands[command].execute.apply(this, [this.state].concat(args));
} else {
console.log('Command "' + command + '" is not available.');
}
},
getCommands: function () {
var commands = {},
commandsList = [
playCommand, pauseCommand, togglePlaybackCommand,
toggleMuteCommand, toggleFullScreenCommand, setSpeedCommand
];
_.each(commandsList, function(command) {
commands[command.name] = command;
}, this);
return commands;
}
};
Command = function (name, execute) {
this.name = name;
this.execute = execute;
};
playCommand = new Command('play', function (state) {
state.videoPlayer.play();
});
pauseCommand = new Command('pause', function (state) {
state.videoPlayer.pause();
});
togglePlaybackCommand = new Command('togglePlayback', function (state) {
if (state.videoControl.isPlaying) {
pauseCommand.execute(state);
} else {
playCommand.execute(state);
}
});
toggleMuteCommand = new Command('toggleMute', function (state) {
state.videoVolumeControl.toggleMute();
});
toggleFullScreenCommand = new Command('toggleFullScreen', function (state) {
state.videoControl.toggleFullScreen();
});
setSpeedCommand = new Command('speed', function (state, speed) {
state.videoSpeedControl.setSpeed(state.speedToString(speed));
});
return VideoCommands;
});
}(RequireJS.define));
...@@ -43,7 +43,9 @@ ...@@ -43,7 +43,9 @@
'video/06_video_progress_slider.js', 'video/06_video_progress_slider.js',
'video/07_video_volume_control.js', 'video/07_video_volume_control.js',
'video/08_video_speed_control.js', 'video/08_video_speed_control.js',
'video/09_video_caption.js' 'video/09_video_caption.js',
'video/10_commands.js',
'video/095_video_context_menu.js'
], ],
function ( function (
initialize, initialize,
...@@ -54,7 +56,9 @@ ...@@ -54,7 +56,9 @@
VideoProgressSlider, VideoProgressSlider,
VideoVolumeControl, VideoVolumeControl,
VideoSpeedControl, VideoSpeedControl,
VideoCaption VideoCaption,
VideoCommands,
VideoContextMenu
) { ) {
var youtubeXhr = null, var youtubeXhr = null,
oldVideo = window.Video; oldVideo = window.Video;
...@@ -87,7 +91,9 @@ ...@@ -87,7 +91,9 @@
VideoProgressSlider, VideoProgressSlider,
VideoVolumeControl, VideoVolumeControl,
VideoSpeedControl, VideoSpeedControl,
VideoCaption VideoCaption,
VideoCommands,
VideoContextMenu
]; ];
state.youtubeXhr = youtubeXhr; state.youtubeXhr = youtubeXhr;
......
...@@ -67,6 +67,7 @@ class VideoModule(VideoFields, VideoStudentViewHandlers, XModule): ...@@ -67,6 +67,7 @@ class VideoModule(VideoFields, VideoStudentViewHandlers, XModule):
module = __name__.replace('.video_module', '', 2) module = __name__.replace('.video_module', '', 2)
js = { js = {
'js': [ 'js': [
resource_string(module, 'js/src/video/00_component.js'),
resource_string(module, 'js/src/video/00_video_storage.js'), resource_string(module, 'js/src/video/00_video_storage.js'),
resource_string(module, 'js/src/video/00_resizer.js'), resource_string(module, 'js/src/video/00_resizer.js'),
resource_string(module, 'js/src/video/00_async_process.js'), resource_string(module, 'js/src/video/00_async_process.js'),
...@@ -84,6 +85,8 @@ class VideoModule(VideoFields, VideoStudentViewHandlers, XModule): ...@@ -84,6 +85,8 @@ class VideoModule(VideoFields, VideoStudentViewHandlers, XModule):
resource_string(module, 'js/src/video/07_video_volume_control.js'), resource_string(module, 'js/src/video/07_video_volume_control.js'),
resource_string(module, 'js/src/video/08_video_speed_control.js'), resource_string(module, 'js/src/video/08_video_speed_control.js'),
resource_string(module, 'js/src/video/09_video_caption.js'), resource_string(module, 'js/src/video/09_video_caption.js'),
resource_string(module, 'js/src/video/095_video_context_menu.js'),
resource_string(module, 'js/src/video/10_commands.js'),
resource_string(module, 'js/src/video/10_main.js') resource_string(module, 'js/src/video/10_main.js')
] ]
} }
...@@ -93,7 +96,6 @@ class VideoModule(VideoFields, VideoStudentViewHandlers, XModule): ...@@ -93,7 +96,6 @@ class VideoModule(VideoFields, VideoStudentViewHandlers, XModule):
]} ]}
js_module_name = "Video" js_module_name = "Video"
def get_html(self): def get_html(self):
track_url = None track_url = None
download_video_link = None download_video_link = None
......
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