diff --git a/common/lib/xmodule/xmodule/js/karma_runner_webpack.js b/common/lib/xmodule/xmodule/js/karma_runner_webpack.js
new file mode 100644
index 000000000000..b3adb3687872
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/karma_runner_webpack.js
@@ -0,0 +1,82 @@
+
+import '../../../../static/js/src/ajax_prefix.js';
+import '../../../../static/common/js/vendor/underscore.js';
+import '../../../../static/common/js/vendor/backbone.js';
+import '../../../../static/js/vendor/CodeMirror/codemirror.js';
+import '../../../../static/js/vendor/draggabilly.js';
+import '../../../../static/common/js/vendor/jquery.js';
+import '../../../../static/common/js/vendor/jquery-migrate.js';
+import '../../../../static/js/vendor/jquery.cookie.js';
+import '../../../../static/js/vendor/jquery.leanModal.js';
+import '../../../../static/js/vendor/jquery.timeago.js';
+import '../../../../static/js/vendor/jquery-ui.min.js';
+import '../../../../static/js/vendor/jquery.ui.draggable.js';
+import '../../../../static/js/vendor/json2.js';
+// import '../../../../static/common/js/vendor/moment-with-locales.js';
+import '../../../../static/js/vendor/tinymce/js/tinymce/jquery.tinymce.min.js';
+import '../../../../static/js/vendor/tinymce/js/tinymce/tinymce.full.min.js';
+import '../../../../static/js/src/accessibility_tools.js';
+import '../../../../static/js/src/logger.js';
+import '../../../../static/js/src/utility.js';
+import '../../../../static/js/test/add_ajax_prefix.js';
+import '../../../../static/js/test/i18n.js';
+import '../../../../static/common/js/vendor/hls.js';
+import '../assets/vertical/public/js/vertical_student_view.js';
+
+
+import '../../../../static/js/vendor/jasmine-imagediff.js';
+import '../../../../static/common/js/spec_helpers/jasmine-waituntil.js';
+import '../../../../static/common/js/spec_helpers/jasmine-extensions.js';
+import '../../../../static/common/js/vendor/sinon.js';
+
+// These libraries are used by the tests (and the code under test)
+// but not explicitly imported
+import 'jquery.ui';
+
+// These
+import './src/video/10_main.js'
+import './spec/helper.js'
+import './spec/video_helper.js'
+
+// These are the tests that will be run
+import './spec/video/async_process_spec.js';
+import './spec/video/completion_spec.js';
+import './spec/video/events_spec.js';
+import './spec/video/general_spec.js';
+import './spec/video/html5_video_spec.js';
+import './spec/video/initialize_spec.js';
+import './spec/video/iterator_spec.js';
+import './spec/video/resizer_spec.js';
+import './spec/video/sjson_spec.js';
+import './spec/video/video_autoadvance_spec.js';
+import './spec/video/video_bumper_spec.js';
+import './spec/video/video_caption_spec.js';
+import './spec/video/video_context_menu_spec.js';
+import './spec/video/video_control_spec.js';
+import './spec/video/video_events_bumper_plugin_spec.js';
+import './spec/video/video_events_plugin_spec.js';
+import './spec/video/video_focus_grabber_spec.js';
+import './spec/video/video_full_screen_spec.js';
+import './spec/video/video_player_spec.js';
+import './spec/video/video_play_pause_control_spec.js';
+import './spec/video/video_play_placeholder_spec.js';
+import './spec/video/video_play_skip_control_spec.js';
+import './spec/video/video_poster_spec.js';
+import './spec/video/video_progress_slider_spec.js';
+import './spec/video/video_quality_control_spec.js';
+import './spec/video/video_save_state_plugin_spec.js';
+import './spec/video/video_skip_control_spec.js';
+import './spec/video/video_speed_control_spec.js';
+import './spec/video/video_storage_spec.js';
+import './spec/video/video_volume_control_spec.js';
+import './spec/time_spec.js';
+
+// overwrite the loaded method and manually start the karma after a delay
+// Somehow the code initialized in jQuery's onready doesn't get called before karma auto starts
+
+'use strict';
+window.__karma__.loaded = function () {
+ setTimeout(function () {
+ window.__karma__.start();
+ }, 1000);
+};
diff --git a/common/lib/xmodule/xmodule/js/karma_xmodule.conf.js b/common/lib/xmodule/xmodule/js/karma_xmodule.conf.js
index 46ce08ef9b43..c2afec826156 100644
--- a/common/lib/xmodule/xmodule/js/karma_xmodule.conf.js
+++ b/common/lib/xmodule/xmodule/js/karma_xmodule.conf.js
@@ -41,7 +41,6 @@ var options = {
{pattern: 'common_static/js/test/i18n.js', included: true},
{pattern: 'common_static/common/js/vendor/hls.js', included: true},
{pattern: 'public/js/split_test_staff.js', included: true},
- {pattern: 'public/js/vertical_student_view.js', included: true},
{pattern: 'src/word_cloud/d3.min.js', included: true},
// Load test utilities
@@ -69,13 +68,18 @@ var options = {
// Make sure the patterns in sourceFiles and specFiles do not match the same file.
// Otherwise Istanbul which is used for coverage tracking will cause tests to not run.
sourceFiles: [
- {pattern: 'src/xmodule.js', included: true, ignoreCoverage: true}, // To prevent getting instrumented twice.
- {pattern: 'src/**/*.js', included: true}
+ { pattern: 'src/xmodule.js', included: true, ignoreCoverage: true }, // To prevent getting instrumented twice.
+ // Load these before the xmodules that use them
+ { pattern: 'src/javascript_loader.js', included: true },
+ { pattern: 'src/collapsible.js', included: true },
+ // Load everything else
+ {pattern: 'src/**/!(video)/!(poll|time).js', included: true}
],
specFiles: [
{pattern: 'spec/helper.js', included: true, ignoreCoverage: true}, // Helper which depends on source files.
- {pattern: 'spec/**/*.js', included: true}
+ { pattern: 'spec/**/!(video)/*.js', included: true },
+ { pattern: 'spec/!(time_spec|video_helper).js', included: true }
],
fixtureFiles: [
@@ -88,6 +92,8 @@ var options = {
]
};
+
module.exports = function(config) {
configModule.configure(config, options);
};
+
diff --git a/common/lib/xmodule/xmodule/js/karma_xmodule_webpack.conf.js b/common/lib/xmodule/xmodule/js/karma_xmodule_webpack.conf.js
new file mode 100644
index 000000000000..550d0d66c551
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/karma_xmodule_webpack.conf.js
@@ -0,0 +1,45 @@
+/* eslint-env node */
+
+// Karma config for xmodule suite.
+// Docs and troubleshooting tips in common/static/common/js/karma.common.conf.js
+
+'use strict';
+var path = require('path');
+var configModule = require(path.join(__dirname, 'common_static/common/js/karma.common.conf.js'));
+
+var options = {
+
+ useRequireJs: false,
+
+ normalizePathsForCoverageFunc: function(appRoot, pattern) {
+ return pattern;
+ },
+
+ libraryFilesToInclude: [],
+ libraryFiles: [],
+ sourceFiles: [],
+ specFiles: [],
+
+ fixtureFiles: [
+ {pattern: 'fixtures/*.*'},
+ {pattern: 'fixtures/hls/**/*.*'}
+ ],
+
+ runFiles: [
+ {pattern: 'karma_runner_webpack.js', webpack: true}
+ ],
+
+ preprocessors: {}
+};
+
+options.runFiles
+ .filter(function(file) { return file.webpack; })
+ .forEach(function(file) {
+ options.preprocessors[file.pattern] = ['webpack'];
+ });
+
+
+module.exports = function(config) {
+ configModule.configure(config, options);
+};
+
diff --git a/common/lib/xmodule/xmodule/js/spec/helper.js b/common/lib/xmodule/xmodule/js/spec/helper.js
index c323209f04bf..8b12b51794d3 100644
--- a/common/lib/xmodule/xmodule/js/spec/helper.js
+++ b/common/lib/xmodule/xmodule/js/spec/helper.js
@@ -194,19 +194,6 @@
// Stub jQuery.scrollTo module.
$.fn.scrollTo = jasmine.createSpy('jQuery.scrollTo');
- // Stub window.Video.loadYouTubeIFrameAPI()
- window.Video.loadYouTubeIFrameAPI = jasmine.createSpy('window.Video.loadYouTubeIFrameAPI').and.returnValue(
- function(scriptTag) {
- var event = document.createEvent('Event');
- if (fixture === 'video.html') {
- event.initEvent('load', false, false);
- } else {
- event.initEvent('error', false, false);
- }
- scriptTag.dispatchEvent(event);
- }
- );
-
jasmine.initializePlayer = function(fixture, params) {
var state;
diff --git a/common/lib/xmodule/xmodule/js/spec/time_spec.js b/common/lib/xmodule/xmodule/js/spec/time_spec.js
index 7044805ecb30..e791aac62281 100644
--- a/common/lib/xmodule/xmodule/js/spec/time_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/time_spec.js
@@ -1,56 +1,57 @@
-(function(undefined) {
- 'use strict';
-
- describe('Time', function() {
- describe('format', function() {
- describe('with NAN', function() {
- it('return a correct time format', function() {
- expect(Time.format('string')).toEqual('0:00');
- expect(Time.format(void(0))).toEqual('0:00');
- });
- });
- describe('with duration more than or equal to 1 hour', function() {
- it('return a correct time format', function() {
- expect(Time.format(3600)).toEqual('1:00:00');
- expect(Time.format(7272)).toEqual('2:01:12');
- });
+'use strict';
+
+import * as Time from 'time.js';
+
+describe('Time', function() {
+ describe('format', function() {
+ describe('with NAN', function() {
+ it('return a correct time format', function() {
+ expect(Time.format('string')).toEqual('0:00');
+ expect(Time.format(void(0))).toEqual('0:00');
});
+ });
- describe('with duration less than 1 hour', function() {
- it('return a correct time format', function() {
- expect(Time.format(1)).toEqual('0:01');
- expect(Time.format(61)).toEqual('1:01');
- expect(Time.format(3599)).toEqual('59:59');
- });
+ describe('with duration more than or equal to 1 hour', function() {
+ it('return a correct time format', function() {
+ expect(Time.format(3600)).toEqual('1:00:00');
+ expect(Time.format(7272)).toEqual('2:01:12');
});
});
- describe('formatFull', function() {
- it('gives correct string for times', function() {
- var testTimes = [
- [0, '00:00:00'], [60, '00:01:00'],
- [488, '00:08:08'], [2452, '00:40:52'],
- [3600, '01:00:00'], [28800, '08:00:00'],
- [144532, '40:08:52'], [190360, '52:52:40'],
- [294008, '81:40:08'], [-5, '00:00:00']
- ];
-
- $.each(testTimes, function(index, times) {
- var timeInt = times[0],
- timeStr = times[1];
-
- expect(Time.formatFull(timeInt)).toBe(timeStr);
- });
+ describe('with duration less than 1 hour', function() {
+ it('return a correct time format', function() {
+ expect(Time.format(1)).toEqual('0:01');
+ expect(Time.format(61)).toEqual('1:01');
+ expect(Time.format(3599)).toEqual('59:59');
});
});
+ });
- describe('convert', function() {
- it('return a correct time based on speed modifier', function() {
- expect(Time.convert(0, 1, 1.5)).toEqual('0.000');
- expect(Time.convert(100, 1, 1.5)).toEqual('66.667');
- expect(Time.convert(100, 1.5, 1)).toEqual('150.000');
+ describe('formatFull', function() {
+ it('gives correct string for times', function() {
+ var testTimes = [
+ [0, '00:00:00'], [60, '00:01:00'],
+ [488, '00:08:08'], [2452, '00:40:52'],
+ [3600, '01:00:00'], [28800, '08:00:00'],
+ [144532, '40:08:52'], [190360, '52:52:40'],
+ [294008, '81:40:08'], [-5, '00:00:00']
+ ];
+
+ $.each(testTimes, function(index, times) {
+ var timeInt = times[0],
+ timeStr = times[1];
+
+ expect(Time.formatFull(timeInt)).toBe(timeStr);
});
});
});
-}).call(this);
+
+ describe('convert', function() {
+ it('return a correct time based on speed modifier', function() {
+ expect(Time.convert(0, 1, 1.5)).toEqual('0.000');
+ expect(Time.convert(100, 1, 1.5)).toEqual('66.667');
+ expect(Time.convert(100, 1.5, 1)).toEqual('150.000');
+ });
+ });
+});
diff --git a/common/lib/xmodule/xmodule/js/spec/video/async_process_spec.js b/common/lib/xmodule/xmodule/js/spec/video/async_process_spec.js
index 917f501313ec..f2d4b4e34987 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/async_process_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/async_process_spec.js
@@ -78,4 +78,4 @@ function(AsyncProcess) {
});
});
});
-}(RequireJS.require));
+}(require));
diff --git a/common/lib/xmodule/xmodule/js/spec/video/general_spec.js b/common/lib/xmodule/xmodule/js/spec/video/general_spec.js
index 9a156ecc18a9..00666cd8a151 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/general_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/general_spec.js
@@ -1,7 +1,5 @@
(function(undefined) {
describe('Video', function() {
- var oldOTBD, state;
-
afterEach(function() {
$('source').remove();
window.VideoState = {};
@@ -11,6 +9,8 @@
describe('constructor', function() {
describe('YT', function() {
+ var state;
+
beforeEach(function() {
loadFixtures('video.html');
$.cookie.and.returnValue('0.50');
@@ -18,24 +18,24 @@
describe('by default', function() {
beforeEach(function() {
- this.state = jasmine.initializePlayerYouTube('video_html5.html');
+ state = jasmine.initializePlayerYouTube('video_html5.html');
});
afterEach(function() {
- this.state.storage.clear();
- this.state.videoPlayer.destroy();
+ state.storage.clear();
+ state.videoPlayer.destroy();
});
it('check videoType', function() {
- expect(this.state.videoType).toEqual('youtube');
+ expect(state.videoType).toEqual('youtube');
});
it('set the elements', function() {
- expect(this.state.el).toEqual($('#video_id'));
+ expect(state.el).toEqual($('#video_id'));
});
it('parse the videos', function() {
- expect(this.state.videos).toEqual({
+ expect(state.videos).toEqual({
'0.50': '7tqY6eQzVhE',
'1.0': 'cogebirgzzM',
'1.50': 'abcdefghijkl'
@@ -43,11 +43,11 @@
});
it('parse available video speeds', function() {
- expect(this.state.speeds).toEqual(['0.50', '1.0', '1.50']);
+ expect(state.speeds).toEqual(['0.50', '1.0', '1.50']);
});
it('set current video speed via cookie', function() {
- expect(this.state.speed).toEqual('1.50');
+ expect(state.speed).toEqual('1.50');
});
});
});
diff --git a/common/lib/xmodule/xmodule/js/spec/video/html5_video_spec.js b/common/lib/xmodule/xmodule/js/spec/video/html5_video_spec.js
index 207b6a6bdcce..2dad74550588 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/html5_video_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/html5_video_spec.js
@@ -11,6 +11,8 @@
oldOTBD = window.onTouchBasedDevice;
window.onTouchBasedDevice = jasmine
.createSpy('onTouchBasedDevice').and.returnValue(null);
+
+ state = jasmine.initializePlayer('video_html5.html');
});
afterEach(function() {
diff --git a/common/lib/xmodule/xmodule/js/spec/video/initialize_spec.js b/common/lib/xmodule/xmodule/js/spec/video/initialize_spec.js
index 053b2ca45208..cef13bd3f528 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/initialize_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/initialize_spec.js
@@ -1,4 +1,4 @@
-(function(requirejs, require, define, undefined) {
+(function(require, define, undefined) {
'use strict';
require(
@@ -320,4 +320,4 @@ function(Initialize) {
});
});
});
-}(RequireJS.requirejs, RequireJS.require, RequireJS.define));
+}(require, define));
diff --git a/common/lib/xmodule/xmodule/js/spec/video/iterator_spec.js b/common/lib/xmodule/xmodule/js/spec/video/iterator_spec.js
index 6d689f0544bd..3797babee44d 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/iterator_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/iterator_spec.js
@@ -100,4 +100,4 @@ function(Iterator) {
});
});
});
-}(RequireJS.require));
+}(require));
diff --git a/common/lib/xmodule/xmodule/js/spec/video/resizer_spec.js b/common/lib/xmodule/xmodule/js/spec/video/resizer_spec.js
index 366c95cae4ab..9cf001fa7203 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/resizer_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/resizer_spec.js
@@ -1,4 +1,4 @@
-(function(requirejs, require, define, undefined) {
+(function(require, define, undefined) {
require(
['video/00_resizer.js'],
function(Resizer) {
@@ -261,4 +261,4 @@ function(Resizer) {
});
});
});
-}(RequireJS.requirejs, RequireJS.require, RequireJS.define));
+}(require, define));
diff --git a/common/lib/xmodule/xmodule/js/spec/video/sjson_spec.js b/common/lib/xmodule/xmodule/js/spec/video/sjson_spec.js
index 0e26f3f0d11a..f7992a76ca38 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/sjson_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/sjson_spec.js
@@ -64,4 +64,4 @@ function(Sjson) {
});
});
});
-}(RequireJS.require));
+}(require));
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_events_plugin_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_events_plugin_spec.js
index f6221117ec52..b3b19689f59c 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/video_events_plugin_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_events_plugin_spec.js
@@ -1,3 +1,5 @@
+import '../helper.js'
+
(function(undefined) {
'use strict';
var describeInfo, state, oldOTBD;
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 7303a1c53909..798c353d1218 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
@@ -1,4 +1,4 @@
-(function(requirejs, require, define, undefined) {
+(function(require, define, undefined) {
'use strict';
require(
@@ -1065,4 +1065,4 @@ function(VideoPlayer, HLS) {
});
});
});
-}(RequireJS.requirejs, RequireJS.require, RequireJS.define));
+}(require, define));
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_progress_slider_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_progress_slider_spec.js
index a904439aa3b8..059ad032dd34 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/video_progress_slider_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_progress_slider_spec.js
@@ -24,7 +24,7 @@
});
it('build the slider', function() {
- expect($('.slider')).toContain(state.videoProgressSlider.slider);
+ expect($('.slider').toArray()).toContain(state.videoProgressSlider.slider);
expect($.fn.slider).toHaveBeenCalledWith({
range: 'min',
min: 0,
@@ -35,7 +35,7 @@
});
it('build the seek handle', function() {
- expect($('.ui-slider-handle'))
+ expect($('.ui-slider-handle').toArray())
.toContain(state.videoProgressSlider.handle);
});
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_save_state_plugin_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_save_state_plugin_spec.js
index 0d100a98db77..83d26a552115 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/video_save_state_plugin_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_save_state_plugin_spec.js
@@ -1,5 +1,8 @@
+import * as Time from 'time.js';
+
(function(undefined) {
'use strict';
+
describe('VideoPlayer Save State plugin', function() {
var state, oldOTBD;
@@ -42,7 +45,6 @@
beforeEach(function() {
state.videoPlayer.currentTime = videoPlayerCurrentTime;
- spyOn(window.Time, 'formatFull').and.callThrough();
});
it('data is not an object, async is true', function() {
@@ -147,9 +149,7 @@
positionVal,
true
);
- expect(Time.formatFull).toHaveBeenCalledWith(
- positionVal
- );
+ expect(ajaxData.saved_video_position).toBe(Time.formatFull(positionVal));
}
expect($.ajax).toHaveBeenCalledWith({
url: state.config.saveStateUrl,
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_storage_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_storage_spec.js
index 51fd0b2edbd3..ee2d8965f32f 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/video_storage_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_storage_spec.js
@@ -1,4 +1,4 @@
-(function(requirejs, require, define, undefined) {
+(function(require, define, undefined) {
require(
['video/00_video_storage.js'],
function(VideoStorage) {
@@ -80,4 +80,4 @@ function(VideoStorage) {
});
});
});
-}(RequireJS.requirejs, RequireJS.require, RequireJS.define));
+}(require, define));
diff --git a/common/lib/xmodule/xmodule/js/spec/video_helper.js b/common/lib/xmodule/xmodule/js/spec/video_helper.js
new file mode 100644
index 000000000000..408f6580a7a7
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/video_helper.js
@@ -0,0 +1,16 @@
+(function() {
+ 'use strict';
+
+ // Stub window.Video.loadYouTubeIFrameAPI()
+ window.Video.loadYouTubeIFrameAPI = jasmine.createSpy('window.Video.loadYouTubeIFrameAPI').and.returnValue(
+ function(scriptTag) {
+ var event = document.createEvent('Event');
+ if (fixture === 'video.html') {
+ event.initEvent('load', false, false);
+ } else {
+ event.initEvent('error', false, false);
+ }
+ scriptTag.dispatchEvent(event);
+ }
+ );
+}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/src/capa/display.js b/common/lib/xmodule/xmodule/js/src/capa/display.js
index b65177148ff6..728b047bc7cf 100644
--- a/common/lib/xmodule/xmodule/js/src/capa/display.js
+++ b/common/lib/xmodule/xmodule/js/src/capa/display.js
@@ -155,7 +155,9 @@
return MathJax.Hub.Queue(['Typeset', MathJax.Hub, element]);
});
}
- window.update_schematics();
+ if (window.hasOwnProperty('update_schematics')) {
+ window.update_schematics();
+ }
problemPrefix = this.element_id.replace(/problem_/, '');
this.inputs = this.$('[id^="input_' + problemPrefix + '_"]');
this.$('div.action button').click(this.refreshAnswers);
diff --git a/common/lib/xmodule/xmodule/js/src/poll/poll.js b/common/lib/xmodule/xmodule/js/src/poll/poll.js
index ea29c843360e..018dbaf708a2 100644
--- a/common/lib/xmodule/xmodule/js/src/poll/poll.js
+++ b/common/lib/xmodule/xmodule/js/src/poll/poll.js
@@ -1,5 +1,11 @@
-window.Poll = function(el) {
- RequireJS.require(['PollMain'], function(PollMain) {
- new PollMain(el);
- });
-};
+define(['poll/poll_main.js'], function(PollMain) {
+ 'use strict';
+
+ function Poll(el) {
+ return new PollMain(el);
+ }
+
+ window.Poll = Poll;
+
+ return Poll;
+});
diff --git a/common/lib/xmodule/xmodule/js/src/sequence/display.js b/common/lib/xmodule/xmodule/js/src/sequence/display.js
index ff2d01eab14a..d49d3361a0c9 100644
--- a/common/lib/xmodule/xmodule/js/src/sequence/display.js
+++ b/common/lib/xmodule/xmodule/js/src/sequence/display.js
@@ -267,7 +267,9 @@
XBlock.initializeBlocks(this.content_container, this.requestToken);
// For embedded circuit simulator exercises in 6.002x
- window.update_schematics();
+ if (window.hasOwnProperty('update_schematics')) {
+ window.update_schematics();
+ }
this.position = newPosition;
this.toggleArrows();
this.hookUpContentStateChangeEvent();
diff --git a/common/lib/xmodule/xmodule/js/src/time.js b/common/lib/xmodule/xmodule/js/src/time.js
index 2373768e9fd5..7fdd588ff5fd 100644
--- a/common/lib/xmodule/xmodule/js/src/time.js
+++ b/common/lib/xmodule/xmodule/js/src/time.js
@@ -1,51 +1,42 @@
-(function(undefined) {
- 'use strict';
-
- this.Time = {
- format: format,
- formatFull: formatFull,
- convert: convert
- };
-
- return;
-
- function format(time, formatFull) {
- var hours, minutes, seconds;
-
- if (!_.isFinite(time) || time < 0) {
- time = 0;
- }
-
- seconds = Math.floor(time);
- minutes = Math.floor(seconds / 60);
- hours = Math.floor(minutes / 60);
- seconds = seconds % 60;
- minutes = minutes % 60;
-
- if (formatFull) {
- return '' + _pad(hours) + ':' + _pad(minutes) + ':' + _pad(seconds % 60);
- } else if (hours) {
- return '' + hours + ':' + _pad(minutes) + ':' + _pad(seconds % 60);
- } else {
- return '' + minutes + ':' + _pad(seconds % 60);
- }
- }
- function formatFull(time) {
- // The returned value will not be user-facing. So no need for
- // internationalization.
- return format(time, true);
- }
+function format(time, formatFull) {
+ var hours, minutes, seconds;
- function convert(time, oldSpeed, newSpeed) {
- return (time * oldSpeed / newSpeed).toFixed(3);
+ if (!_.isFinite(time) || time < 0) {
+ time = 0;
}
- function _pad(number) {
- if (number < 10) {
- return '0' + number;
- } else {
- return '' + number;
- }
+ seconds = Math.floor(time);
+ minutes = Math.floor(seconds / 60);
+ hours = Math.floor(minutes / 60);
+ seconds = seconds % 60;
+ minutes = minutes % 60;
+
+ if (formatFull) {
+ return '' + _pad(hours) + ':' + _pad(minutes) + ':' + _pad(seconds % 60);
+ } else if (hours) {
+ return '' + hours + ':' + _pad(minutes) + ':' + _pad(seconds % 60);
+ } else {
+ return '' + minutes + ':' + _pad(seconds % 60);
}
-}).call(this);
+}
+
+function formatFull(time) {
+ // The returned value will not be user-facing. So no need for
+ // internationalization.
+ return format(time, true);
+}
+
+function convert(time, oldSpeed, newSpeed) {
+ return (time * oldSpeed / newSpeed).toFixed(3);
+}
+
+function _pad(number) {
+ if (number < 10) {
+ return '0' + number;
+ } else {
+ return '' + number;
+ }
+}
+
+export { format, formatFull, convert }
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 debff1da8bd3..44591439d71d 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', 'video/02_html5_hls_video.js', 'video/00_resizer.js', 'hls', 'underscore'],
-function(HTML5Video, HTML5HLSVideo, Resizer, HLS, _) {
+['video/02_html5_video.js', 'video/02_html5_hls_video.js', 'video/00_resizer.js', 'hls', 'underscore', '../time.js'],
+function(HTML5Video, HTML5HLSVideo, Resizer, HLS, _, Time) {
var dfd = $.Deferred(),
VideoPlayer = function(state) {
state.videoPlayer = {};
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 84c3de664979..619fdd1e8b41 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
@@ -2,8 +2,8 @@
// VideoControl module.
define(
'video/04_video_control.js',
-[],
-function() {
+['time.js'],
+function(Time) {
// VideoControl() function - what this module "exports".
return function(state) {
var dfd = $.Deferred();
diff --git a/common/lib/xmodule/xmodule/js/src/video/09_save_state_plugin.js b/common/lib/xmodule/xmodule/js/src/video/09_save_state_plugin.js
index 7657d37a2a1d..3121961a2377 100644
--- a/common/lib/xmodule/xmodule/js/src/video/09_save_state_plugin.js
+++ b/common/lib/xmodule/xmodule/js/src/video/09_save_state_plugin.js
@@ -1,6 +1,6 @@
(function(define) {
'use strict';
- define('video/09_save_state_plugin.js', ['underscore'], function(_) {
+ define('video/09_save_state_plugin.js', ['underscore', 'time.js'], function(_, Time) {
/**
* Save state module.
* @exports video/09_save_state_plugin.js
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 91e53e0246af..70b8d5decf8c 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
@@ -6,8 +6,9 @@
'video/00_sjson.js',
'video/00_async_process.js',
'edx-ui-toolkit/js/utils/html-utils',
- 'draggabilly'
- ], function(Sjson, AsyncProcess, HtmlUtils, Draggabilly) {
+ 'draggabilly',
+ 'time.js',
+ ], function(Sjson, AsyncProcess, HtmlUtils, Draggabilly, Time) {
/**
* @desc VideoCaption module exports a function.
*
diff --git a/common/lib/xmodule/xmodule/static_content.py b/common/lib/xmodule/xmodule/static_content.py
index 88b104b88a87..0abb2a1b0a95 100755
--- a/common/lib/xmodule/xmodule/static_content.py
+++ b/common/lib/xmodule/xmodule/static_content.py
@@ -6,9 +6,11 @@
import errno
import hashlib
+import json
import logging
import os
import sys
+import textwrap
from collections import defaultdict
import django
@@ -117,28 +119,33 @@ def _write_js(output_root, classes):
Write the javascript fragments from all XModules in `classes`
into `output_root` as individual files, hashed by the contents to remove
duplicates
+
+ Returns a dictionary mapping class names to the files that they depend on.
"""
- contents = {}
+ file_contents = {}
+ file_owners = defaultdict(list)
- js_fragments = set()
+ fragment_owners = defaultdict(list)
for class_ in classes:
module_js = class_.get_javascript()
# It will enforce 000 prefix for xmodule.js.
- js_fragments.add((0, 'js', module_js.get('xmodule_js')))
+ fragment_owners[(0, 'js', module_js.get('xmodule_js'))].append(class_.__name__)
for filetype in ('coffee', 'js'):
for idx, fragment in enumerate(module_js.get(filetype, [])):
- js_fragments.add((idx + 1, filetype, fragment))
+ fragment_owners[(idx + 1, filetype, fragment)].append(class_.__name__)
- for idx, filetype, fragment in sorted(js_fragments):
+ for (idx, filetype, fragment), owners in sorted(fragment_owners.items()):
filename = "{idx:0=3d}-{hash}.{type}".format(
idx=idx,
hash=hashlib.md5(fragment).hexdigest(),
type=filetype)
- contents[filename] = fragment
+ file_contents[filename] = fragment
+ for owner in owners:
+ file_owners[owner].append(output_root / filename)
- _write_files(output_root, contents, {'.coffee': '.js'})
+ _write_files(output_root, file_contents, {'.coffee': '.js'})
- return [output_root / filename for filename in contents.keys()]
+ return file_owners
def _write_files(output_root, contents, generated_suffix_map=None):
@@ -182,6 +189,33 @@ def _write_files(output_root, contents, generated_suffix_map=None):
LOG.debug("%s unchanged, skipping", output_file)
+def write_webpack(output_file, module_files, descriptor_files):
+ """
+ Write all xmodule and xmodule descriptor javascript into module-specific bundles.
+
+ The output format should be suitable for smart-merging into an existing webpack configuration.
+ """
+ _ensure_dir(output_file.dirname())
+
+ config = {
+ 'entry': {}
+ }
+ for (owner, files) in module_files.items() + descriptor_files.items():
+ unique_files = sorted(set('./{}'.format(file) for file in files))
+ if len(unique_files) == 1:
+ unique_files = unique_files[0]
+ config['entry'][owner] = unique_files
+ # config['entry']['modules/js/all'] = sorted(set('./{}'.format(file) for file in sum(module_files.values(), [])))
+ # config['entry']['descriptors/js/all'] = sorted(set('./{}'.format(file) for file in sum(descriptor_files.values(), [])))
+
+ with output_file.open('w') as outfile:
+ outfile.write(
+ textwrap.dedent(u"""\
+ module.exports = {config_json};
+ """).format(config_json=json.dumps(config, indent=4))
+ )
+
+
def main():
"""
Generate
@@ -209,10 +243,11 @@ def main():
args = docopt(main.__doc__)
root = path(args['
'])
- write_descriptor_js(root / 'descriptors/js')
+ descriptor_files = write_descriptor_js(root / 'descriptors/js')
write_descriptor_styles(root / 'descriptors/css')
- write_module_js(root / 'modules/js')
+ module_files = write_module_js(root / 'modules/js')
write_module_styles(root / 'modules/css')
+ write_webpack(root / 'webpack.xmodule.config.js', module_files, descriptor_files)
if __name__ == '__main__':
diff --git a/common/lib/xmodule/xmodule/tests/test_content.py b/common/lib/xmodule/xmodule/tests/test_content.py
index 0b3ffeaec3d6..491eb292a05c 100644
--- a/common/lib/xmodule/xmodule/tests/test_content.py
+++ b/common/lib/xmodule/xmodule/tests/test_content.py
@@ -221,7 +221,7 @@ def test_static_content_write_js(self):
Test that only one filename starts with 000.
"""
output_root = path(u'common/static/xmodule/descriptors/js')
- js_file_paths = _write_js(output_root, _list_descriptors())
- js_file_paths = [file_path for file_path in js_file_paths if os.path.basename(file_path).startswith('000-')]
+ file_owners = _write_js(output_root, _list_descriptors())
+ js_file_paths = set(file_path for file_path in sum(file_owners.values(), []) if os.path.basename(file_path).startswith('000-'))
self.assertEqual(len(js_file_paths), 1)
- self.assertIn("XModule.Descriptor = (function() {", open(js_file_paths[0]).read())
+ self.assertIn("XModule.Descriptor = (function() {", open(js_file_paths.pop()).read())
diff --git a/common/lib/xmodule/xmodule/vertical_block.py b/common/lib/xmodule/xmodule/vertical_block.py
index 7a081c315b77..7755cc61ecf0 100644
--- a/common/lib/xmodule/xmodule/vertical_block.py
+++ b/common/lib/xmodule/xmodule/vertical_block.py
@@ -23,6 +23,7 @@
from xmodule.x_module import STUDENT_VIEW, XModuleFields
from xmodule.xml_module import XmlParserMixin
+import webpack_loader.utils
log = logging.getLogger(__name__)
@@ -133,7 +134,8 @@ def student_view(self, context):
'completion_delay_ms': self.get_completion_delay_ms(completion_service),
}))
- fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/vertical_student_view.js'))
+ for tag in webpack_loader.utils.get_as_tags('VerticalStudentView'):
+ fragment.add_resource(tag, mimetype='text/html', placement='head')
fragment.initialize_js('VerticalStudentView')
return fragment
diff --git a/common/lib/xmodule/xmodule/video_module/video_module.py b/common/lib/xmodule/xmodule/video_module/video_module.py
index ee5e29be1e65..f587ab7118d4 100644
--- a/common/lib/xmodule/xmodule/video_module/video_module.py
+++ b/common/lib/xmodule/xmodule/video_module/video_module.py
@@ -129,40 +129,6 @@ class VideoModule(VideoFields, VideoTranscriptsMixin, VideoStudentViewHandlers,
#TODO: For each of the following, ensure that any generated html is properly escaped.
js = {
'js': [
- resource_string(module, 'js/src/time.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_resizer.js'),
- resource_string(module, 'js/src/video/00_async_process.js'),
- resource_string(module, 'js/src/video/00_i18n.js'),
- resource_string(module, 'js/src/video/00_sjson.js'),
- resource_string(module, 'js/src/video/00_iterator.js'),
- resource_string(module, 'js/src/video/01_initialize.js'),
- resource_string(module, 'js/src/video/025_focus_grabber.js'),
- resource_string(module, 'js/src/video/02_html5_video.js'),
- resource_string(module, 'js/src/video/02_html5_hls_video.js'),
- resource_string(module, 'js/src/video/03_video_player.js'),
- resource_string(module, 'js/src/video/035_video_accessible_menu.js'),
- resource_string(module, 'js/src/video/04_video_control.js'),
- resource_string(module, 'js/src/video/04_video_full_screen.js'),
- resource_string(module, 'js/src/video/05_video_quality_control.js'),
- resource_string(module, 'js/src/video/06_video_progress_slider.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_auto_advance_control.js'),
- resource_string(module, 'js/src/video/09_video_caption.js'),
- resource_string(module, 'js/src/video/09_play_placeholder.js'),
- resource_string(module, 'js/src/video/09_play_pause_control.js'),
- resource_string(module, 'js/src/video/09_play_skip_control.js'),
- resource_string(module, 'js/src/video/09_skip_control.js'),
- resource_string(module, 'js/src/video/09_bumper.js'),
- resource_string(module, 'js/src/video/09_save_state_plugin.js'),
- resource_string(module, 'js/src/video/09_events_plugin.js'),
- resource_string(module, 'js/src/video/09_events_bumper_plugin.js'),
- resource_string(module, 'js/src/video/09_poster.js'),
- resource_string(module, 'js/src/video/09_completion.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'),
]
}
diff --git a/common/lib/xmodule/xmodule/word_cloud_module.py b/common/lib/xmodule/xmodule/word_cloud_module.py
index 6a7707a0c577..16383272bc65 100644
--- a/common/lib/xmodule/xmodule/word_cloud_module.py
+++ b/common/lib/xmodule/xmodule/word_cloud_module.py
@@ -91,11 +91,6 @@ class WordCloudFields(object):
class WordCloudModule(WordCloudFields, XModule):
"""WordCloud Xmodule"""
- js = {
- 'js': [
- resource_string(__name__, 'js/src/javascript_loader.js'),
- ],
- }
css = {'scss': [resource_string(__name__, 'css/word_cloud/display.scss')]}
js_module_name = "WordCloud"
diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py
index 84ae3349ce8d..7c4d7dc29b86 100644
--- a/common/lib/xmodule/xmodule/x_module.py
+++ b/common/lib/xmodule/xmodule/x_module.py
@@ -252,10 +252,17 @@ def shim_xmodule_js(block, fragment):
"""
Set up the XBlock -> XModule shim on the supplied :class:`web_fragments.fragment.Fragment`
"""
+ # Delay this import so that it is only used (and django settings are parsed) when
+ # they are required (rather than at startup)
+ import webpack_loader.utils
+
if not fragment.js_init_fn:
fragment.initialize_js('XBlockToXModuleShim')
fragment.json_init_args = {'xmodule-type': block.js_module_name}
+ for tag in webpack_loader.utils.get_as_tags('XModuleShim'):
+ fragment.add_resource(tag, mimetype='text/html', placement='head')
+
class XModuleFields(object):
"""
diff --git a/common/templates/xblock_wrapper.html b/common/templates/xblock_wrapper.html
index 998d00a90ad4..48b585737c93 100644
--- a/common/templates/xblock_wrapper.html
+++ b/common/templates/xblock_wrapper.html
@@ -1,3 +1,9 @@
+<%namespace name='static' file='static_content.html'/>
+
+% if is_xmodule:
+ <%static:webpack entry="${class_name}"/>
+% endif
+
% if js_init_parameters: