Load XBlocks with webpack rather than RequireJS

This commit is contained in:
Calen Pennington
2018-02-15 21:56:25 -05:00
parent 94557900f8
commit d08e199f79
67 changed files with 595 additions and 322 deletions

View File

@@ -247,7 +247,6 @@ def create_unit_from_course_outline():
world.css_click(selector)
world.wait_for_mathjax()
world.wait_for_xmodule()
world.wait_for_loading()
assert world.is_css_present('ul.new-component-type')

View File

@@ -52,9 +52,9 @@ define(
return deferred.promise();
}
if (!window.xmoduleUrls) {
throw Error('window.xmoduleUrls must be defined');
}
return requireQueue(window.xmoduleUrls);
// if (!window.xmoduleUrls) {
// throw Error('window.xmoduleUrls must be defined');
// }
return requireQueue([]);
}
);

View File

@@ -110,10 +110,6 @@ FEATURES['ENABLE_DISCUSSION_SERVICE'] = False
# We do not yet understand why this occurs. Setting this to true is a stopgap measure
USE_I18N = True
# Override the test stub webpack_loader that is installed in test.py.
INSTALLED_APPS = [app for app in INSTALLED_APPS if app != 'openedx.tests.util.webpack_loader']
INSTALLED_APPS.append('webpack_loader')
# Include the lettuce app for acceptance testing, including the 'harvest' django-admin command
# django.contrib.staticfiles used to be loaded by lettuce, now we must add it ourselves
# django.contrib.staticfiles is not added to lms as there is a ^/static$ route built in to the app

View File

@@ -54,8 +54,6 @@ TEST_ROOT = path('test_root')
# Want static files in the same dir for running on jenkins.
STATIC_ROOT = TEST_ROOT / "staticfiles"
INSTALLED_APPS = [app for app in INSTALLED_APPS if app != 'webpack_loader']
INSTALLED_APPS.append('openedx.tests.util.webpack_loader')
WEBPACK_LOADER['DEFAULT']['STATS_FILE'] = STATIC_ROOT / "webpack-stats.json"
GITHUB_REPO_ROOT = TEST_ROOT / "data"

View File

@@ -0,0 +1,55 @@
/* eslint-env node */
// Karma config for cms 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 = {
includeCommonFiles: true,
libraryFiles: [],
libraryFilesToInclude: [
],
// 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: 'js/factories/login.js', webpack: true},
// {pattern: 'js/factories/xblock_validation.js', webpack: true},
// {pattern: 'js/factories/container.js', webpack: true},
// {pattern: 'js/factories/context_course.js', webpack: true},
// {pattern: 'js/factories/edit_tabs.js', webpack: true},
// {pattern: 'js/factories/library.js', webpack: true},
// {pattern: 'js/factories/textbooks.js', webpack: true},
// ],
// All spec files should be imported in main_webpack.js, rather than being listed here
specFiles: [],
fixtureFiles: [
{pattern: '../templates/js/**/*.underscore'},
{pattern: 'templates/**/*.underscore'}
],
runFiles: [
{pattern: 'cms/js/spec/main_webpack.js', webpack: true},
{pattern: 'jasmine.cms.conf.js', included: 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);
};

View File

@@ -43,20 +43,18 @@ from openedx.core.djangolib.markup import HTML, Text
</%block>
<%block name="page_bundle">
<script type="text/javascript">
<%! from pipeline_js.utils import get_xmodule_urls %>
window.xmoduleUrls = ${get_xmodule_urls() | n, dump_js_escaped_json};
</script>
<%static:invoke_page_bundle page_name="js/pages/container" class_name="ContainerFactory">
${component_templates | n, dump_js_escaped_json},
${xblock_info | n, dump_js_escaped_json},
"${action | n, js_escaped_string}",
{
isUnitPage: ${is_unit_page | n, dump_js_escaped_json},
canEdit: true,
outlineURL: "${outline_url | n, js_escaped_string}"
}
</%static:invoke_page_bundle>
<%static:webpack entry="js/factories/container">
ContainerFactory(
${component_templates | n, dump_js_escaped_json},
${xblock_info | n, dump_js_escaped_json},
"${action | n, js_escaped_string}",
{
isUnitPage: ${is_unit_page | n, dump_js_escaped_json},
canEdit: true,
outlineURL: "${outline_url | n, js_escaped_string}"
}
);
</%static:webpack>
</%block>
<%block name="content">

View File

@@ -19,8 +19,8 @@
% endfor
</%block>
<%block name="requirejs">
require(["js/factories/edit_tabs"], function (EditTabsFactory) {
<%block name="page_bundle">
<%static:webpack entry="js/factories/edit_tabs">
EditTabsFactory("${context_course.location | n, js_escaped_string}", "${reverse('tabs_handler', kwargs={'course_key_string': context_course.id})}");
});
</%block>

View File

@@ -25,8 +25,8 @@ from openedx.core.djangolib.markup import HTML, Text
</script>
</%block>
<%block name="requirejs">
require(["js/factories/library"], function(LibraryFactory) {
<%block name="page_bundle">
<%static:webpack entry="js/factories/library">
LibraryFactory(
${component_templates | n, dump_js_escaped_json},
${xblock_info | n, dump_js_escaped_json},

View File

@@ -7,6 +7,7 @@ from lms.lib.utils import is_unit
from openedx.core.djangolib.js_utils import (
dump_js_escaped_json, js_escaped_string
)
from xmodule.x_module import XModule, XModuleDescriptor
%>
<%
xblock_url = xblock_studio_url(xblock)
@@ -38,6 +39,10 @@ block_is_unit = is_unit(xblock)
});
</script>
% if isinstance(xblock, (XModule, XModuleDescriptor)):
<%static:webpack entry="${getattr(xblock.__class__, 'unmixed_class', xblock.__class__).__name__}"/>
% endif
% if not is_root:
% if is_reorderable:
<li class="studio-xblock-wrapper is-draggable" data-locator="${xblock.location}" data-course-key="${xblock.location.course_key}">

View File

@@ -3,3 +3,16 @@
# Patch the xml libs before anything else.
from safe_lxml import defuse_xml_libs
defuse_xml_libs()
import pytest
@pytest.fixture(autouse=True)
def no_webpack_loader(monkeypatch):
monkeypatch.setattr(
"webpack_loader.templatetags.webpack_loader.render_bundle",
lambda entry, extension=None, config='DEFAULT', attrs='': ''
)
monkeypatch.setattr(
"webpack_loader.utils.get_as_tags",
lambda entry, extension=None, config='DEFAULT', attrs='': []
)

View File

@@ -1,7 +1,6 @@
/* JavaScript for Vertical Student View. */
/* global Set:false */ // false means do not assign to Set
/* global ViewedEventTracker:false */
// The vertical marks blocks complete if they are completable by viewing. The
// global variable SEEN_COMPLETABLES tracks blocks between separate loads of
@@ -9,62 +8,61 @@
// navigates back within a given sequential) to protect against duplicate calls
// to the server.
import BookmarkButton from 'course_bookmarks/js/views/bookmark_button';
import {ViewedEventTracker} from '../../../../../../../../lms/static/completion/js/ViewedEvent.js';
var SEEN_COMPLETABLES = new Set();
window.VerticalStudentView = function(runtime, element) {
'use strict';
RequireJS.require(['course_bookmarks/js/views/bookmark_button'], function(BookmarkButton) {
var $element = $(element);
var $bookmarkButtonElement = $element.find('.bookmark-button');
return new BookmarkButton({
el: $bookmarkButtonElement,
bookmarkId: $bookmarkButtonElement.data('bookmarkId'),
usageId: $element.data('usageId'),
bookmarked: $element.parent('#seq_content').data('bookmarked'),
apiUrl: $bookmarkButtonElement.data('bookmarksApiUrl')
});
var $element = $(element);
var $bookmarkButtonElement = $element.find('.bookmark-button');
return new BookmarkButton({
el: $bookmarkButtonElement,
bookmarkId: $bookmarkButtonElement.data('bookmarkId'),
usageId: $element.data('usageId'),
bookmarked: $element.parent('#seq_content').data('bookmarked'),
apiUrl: $bookmarkButtonElement.data('bookmarksApiUrl')
});
RequireJS.require(['bundles/ViewedEvent'], function() {
var tracker, vertical, viewedAfter;
var completableBlocks = [];
var vertModDivs = element.getElementsByClassName('vert-mod');
if (vertModDivs.length === 0) {
return;
}
vertical = vertModDivs[0];
$(element).find('.vert').each(function(idx, block) {
if (block.dataset.completableByViewing !== undefined) {
completableBlocks.push(block);
}
});
if (completableBlocks.length > 0) {
viewedAfter = parseInt(vertical.dataset.completionDelayMs, 10);
if (!(viewedAfter >= 0)) {
// parseInt will return NaN if it fails to parse, which is not >= 0.
viewedAfter = 5000;
}
tracker = new ViewedEventTracker(completableBlocks, viewedAfter);
tracker.addHandler(function(block, event) {
var blockKey = block.dataset.id;
if (blockKey && !SEEN_COMPLETABLES.has(blockKey)) {
if (event.elementHasBeenViewed) {
$.ajax({
type: 'POST',
url: runtime.handlerUrl(element, 'publish_completion'),
data: JSON.stringify({
block_key: blockKey,
completion: 1.0
})
}).then(
function() {
SEEN_COMPLETABLES.add(blockKey);
}
);
}
}
});
var tracker, vertical, viewedAfter;
var completableBlocks = [];
var vertModDivs = element.getElementsByClassName('vert-mod');
if (vertModDivs.length === 0) {
return;
}
vertical = vertModDivs[0];
$(element).find('.vert').each(function(idx, block) {
if (block.dataset.completableByViewing !== undefined) {
completableBlocks.push(block);
}
});
if (completableBlocks.length > 0) {
viewedAfter = parseInt(vertical.dataset.completionDelayMs, 10);
if (!(viewedAfter >= 0)) {
// parseInt will return NaN if it fails to parse, which is not >= 0.
viewedAfter = 5000;
}
tracker = new ViewedEventTracker(completableBlocks, viewedAfter);
tracker.addHandler(function(block, event) {
var blockKey = block.dataset.id;
if (blockKey && !SEEN_COMPLETABLES.has(blockKey)) {
if (event.elementHasBeenViewed) {
$.ajax({
type: 'POST',
url: runtime.handlerUrl(element, 'publish_completion'),
data: JSON.stringify({
block_key: blockKey,
completion: 1.0
})
}).then(
function() {
SEEN_COMPLETABLES.add(blockKey);
}
);
}
}
});
}
};

View File

@@ -13,17 +13,18 @@
<span tabindex="0" class="spinner" aria-hidden="false" aria-label="${_('Loading video player')}"></span>
<span tabindex="-1" class="btn-play fa fa-youtube-play fa-2x is-hidden" aria-hidden="true" aria-label="${_('Play video')}"></span>
<div class="video-player-pre"></div>
<section class="video-player">
<div class="video-player">
<iframe id="id"></iframe>
</section>
</div>
<div class="video-player-post"></div>
<section class="video-controls is-hidden">
<div class="closed-captions"></div>
<div class="video-controls is-hidden">
<div class="slider"></div>
<div>
<div class="vcr"><div class="vidtime">0:00 / 0:00</div></div>
<div class="secondary-controls"></div>
</div>
</section>
</div>
</article>
</div>
<div class="focus_grabber last"></div>

View File

@@ -19,6 +19,7 @@
<iframe id="id"></iframe>
</section>
<div class="video-player-post"></div>
<div class="closed-captions"></div>
<section class="video-controls is-hidden">
<div class="slider"></div>
<div>

View File

@@ -19,6 +19,7 @@
<iframe id="id"></iframe>
</section>
<div class="video-player-post"></div>
<div class="closed-captions"></div>
<section class="video-controls is-hidden">
<div class="slider"></div>
<div>

View File

@@ -12,12 +12,15 @@
<article class="video-wrapper">
<span tabindex="0" class="spinner" aria-hidden="false" aria-label="${_('Loading video player')}"></span>
<span tabindex="-1" class="btn-play fa fa-youtube-play fa-2x is-hidden" aria-hidden="true" aria-label="${_('Play video')}"></span>
<div class="video-player-pre"></div>
<section class="video-player">
<div id="id"></div>
<h4 class="hd hd-4 video-hls-error is-hidden">
Your browser does not support this video format. Try using a different browser.
</h4>
</section>
<div class="video-player-post"></div>
<div class="closed-captions"></div>
<section class="video-controls is-hidden"></section>
</article>
</div>

View File

@@ -12,9 +12,12 @@
<article class="video-wrapper">
<span tabindex="0" class="spinner" aria-hidden="false" aria-label="${_('Loading video player')}"></span>
<span tabindex="-1" class="btn-play fa fa-youtube-play fa-2x is-hidden" aria-hidden="true" aria-label="${_('Play video')}"></span>
<div class="video-player-pre"></div>
<section class="video-player">
<div id="id"></div>
</section>
<div class="video-player-post"></div>
<div class="closed-captions"></div>
<section class="video-controls is-hidden"></section>
</article>
</div>

View File

@@ -12,9 +12,12 @@
<article class="video-wrapper">
<span tabindex="0" class="spinner" aria-hidden="false" aria-label="${_('Loading video player')}"></span>
<span tabindex="-1" class="btn-play fa fa-youtube-play fa-2x is-hidden" aria-hidden="true" aria-label="${_('Play video')}"></span>
<div class="video-player-pre"></div>
<section class="video-player">
<iframe id="id"></iframe>
</section>
<div class="video-player-post"></div>
<div class="closed-captions"></div>
<section class="video-controls is-hidden"></section>
</article>
</div>

View File

@@ -19,6 +19,7 @@
<iframe id="id"></iframe>
</section>
<div class="video-player-post"></div>
<div class="closed-captions"></div>
<section class="video-controls is-hidden">
<div class="slider"></div>
<div>

View File

@@ -17,6 +17,7 @@
<iframe id="id1"></iframe>
</section>
<div class="video-player-post"></div>
<div class="closed-captions"></div>
<section class="video-controls is-hidden">
<div class="slider"></div>
<div>

View File

@@ -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);
};

View File

@@ -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);
};

View File

@@ -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);
};

View File

@@ -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;

View File

@@ -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');
});
});
'use strict';
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');
});
});
import * as Time from 'time.js';
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('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('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 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('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('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');
});
});
});
}).call(this);
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('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');
});
});
});

View File

@@ -78,4 +78,4 @@ function(AsyncProcess) {
});
});
});
}(RequireJS.require));
}(require));

View File

@@ -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');
});
});
});

View File

@@ -11,6 +11,8 @@
oldOTBD = window.onTouchBasedDevice;
window.onTouchBasedDevice = jasmine
.createSpy('onTouchBasedDevice').and.returnValue(null);
state = jasmine.initializePlayer('video_html5.html');
});
afterEach(function() {

View File

@@ -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));

View File

@@ -100,4 +100,4 @@ function(Iterator) {
});
});
});
}(RequireJS.require));
}(require));

View File

@@ -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));

View File

@@ -64,4 +64,4 @@ function(Sjson) {
});
});
});
}(RequireJS.require));
}(require));

View File

@@ -1,3 +1,5 @@
import '../helper.js'
(function(undefined) {
'use strict';
var describeInfo, state, oldOTBD;

View File

@@ -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));

View File

@@ -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);
});

View File

@@ -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,

View File

@@ -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));

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
});

View File

@@ -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();

View File

@@ -1,51 +1,42 @@
(function(undefined) {
'use strict';
this.Time = {
format: format,
formatFull: formatFull,
convert: convert
};
function format(time, formatFull) {
var hours, minutes, seconds;
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);
}
if (!_.isFinite(time) || time < 0) {
time = 0;
}
function formatFull(time) {
// The returned value will not be user-facing. So no need for
// internationalization.
return format(time, true);
}
seconds = Math.floor(time);
minutes = Math.floor(seconds / 60);
hours = Math.floor(minutes / 60);
seconds = seconds % 60;
minutes = minutes % 60;
function convert(time, oldSpeed, newSpeed) {
return (time * oldSpeed / newSpeed).toFixed(3);
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 _pad(number) {
if (number < 10) {
return '0' + number;
} else {
return '' + number;
}
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;
}
}).call(this);
}
export { format, formatFull, convert }

View File

@@ -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 = {};

View File

@@ -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();

View File

@@ -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

View File

@@ -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.
*

View File

@@ -6,9 +6,11 @@ that is defined by XModules and XModuleDescriptors (javascript and css)
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
"""
contents = {}
js_fragments = set()
Returns a dictionary mapping class names to the files that they depend on.
"""
file_contents = {}
file_owners = defaultdict(list)
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['<output_root>'])
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__':

View File

@@ -221,7 +221,7 @@ class ContentTest(unittest.TestCase):
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())

View File

@@ -23,6 +23,7 @@ from xmodule.studio_editable import StudioEditableBlock
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 @@ class VerticalBlock(SequenceFields, XModuleFields, StudioEditableBlock, XmlParse
'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

View File

@@ -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'),
]
}

View File

@@ -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"

View File

@@ -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):
"""

View File

@@ -1,3 +1,9 @@
<%namespace name='static' file='static_content.html'/>
% if is_xmodule:
<%static:webpack entry="${class_name}"/>
% endif
<!-- This is the xblock wrapper ${is_xmodule} -->
<div class="${' '.join(classes) | n}" ${data_attributes}>
% if js_init_parameters:
<script type="json/xblock-args" class="xblock-json-init-args">

View File

@@ -0,0 +1,4 @@
<%namespace name='static' file='static_content.html'/>
<%static:webpack entry="XModuleShim"/>
${content}

View File

@@ -26,19 +26,20 @@ from common.test.acceptance.tests.helpers import (
)
VIDEO_SOURCE_PORT = 8777
VIDEO_HOSTNAME = os.environ.get('BOK_CHOY_HOSTNAME', 'localhost')
HTML5_SOURCES = [
'http://localhost:{0}/gizmo.mp4'.format(VIDEO_SOURCE_PORT),
'http://localhost:{0}/gizmo.webm'.format(VIDEO_SOURCE_PORT),
'http://localhost:{0}/gizmo.ogv'.format(VIDEO_SOURCE_PORT),
'http://{}:{}/gizmo.mp4'.format(VIDEO_HOSTNAME, VIDEO_SOURCE_PORT),
'http://{}:{}/gizmo.webm'.format(VIDEO_HOSTNAME, VIDEO_SOURCE_PORT),
'http://{}:{}/gizmo.ogv'.format(VIDEO_HOSTNAME, VIDEO_SOURCE_PORT),
]
HTML5_SOURCES_INCORRECT = [
'http://localhost:{0}/gizmo.mp99'.format(VIDEO_SOURCE_PORT),
'http://{}:{}/gizmo.mp99'.format(VIDEO_HOSTNAME, VIDEO_SOURCE_PORT),
]
HLS_SOURCES = [
'http://localhost:{0}/hls/history.m3u8'.format(VIDEO_SOURCE_PORT),
'http://{}:{}/hls/history.m3u8'.format(VIDEO_HOSTNAME, VIDEO_SOURCE_PORT),
]

View File

@@ -16,3 +16,7 @@ def no_webpack_loader(monkeypatch):
"webpack_loader.templatetags.webpack_loader.render_bundle",
lambda entry, extension=None, config='DEFAULT', attrs='': ''
)
monkeypatch.setattr(
"webpack_loader.utils.get_as_tags",
lambda entry, extension=None, config='DEFAULT', attrs='': []
)

View File

@@ -95,8 +95,6 @@ PARENTAL_CONSENT_AGE_LIMIT = 13
TEST_ROOT = path("test_root")
# Want static files in the same dir for running on jenkins.
STATIC_ROOT = TEST_ROOT / "staticfiles"
INSTALLED_APPS = [app for app in INSTALLED_APPS if app != 'webpack_loader']
INSTALLED_APPS.append('openedx.tests.util.webpack_loader')
WEBPACK_LOADER['DEFAULT']['STATS_FILE'] = STATIC_ROOT / "webpack-stats.json"
STATUS_MESSAGE_PATH = TEST_ROOT / "status_message.json"

View File

@@ -1,4 +1,4 @@
(function(define, undefined) {
(function(define) {
'use strict';
define([
'gettext', 'jquery', 'underscore', 'backbone', 'text!templates/fields/message_banner.underscore'

View File

@@ -108,10 +108,6 @@ from pipeline_mako import render_require_js_path_overrides
${render_require_js_path_overrides(settings.REQUIRE_JS_PATH_OVERRIDES) | n, decode.utf8}
</%block>
% if not disable_courseware_js:
<%static:js group='module-js'/>
% endif
<%block name="headextra"/>
<%block name="head_extra"/>

View File

@@ -134,6 +134,8 @@ def wrap_xblock(
'display_name': block.display_name_with_default_escaped,
'data_attributes': u' '.join(u'data-{}="{}"'.format(markupsafe.escape(key), markupsafe.escape(value))
for key, value in data.iteritems()),
'class_name': class_name,
'is_xmodule': isinstance(block, (XModule, XModuleDescriptor)),
}
if hasattr(frag, 'json_init_args') and frag.json_init_args is not None:
@@ -200,6 +202,7 @@ def wrap_xblock_aside(
'classes': css_classes,
'data_attributes': u' '.join(u'data-{}="{}"'.format(markupsafe.escape(key), markupsafe.escape(value))
for key, value in data.iteritems()),
'is_xmodule': False,
}
if hasattr(frag, 'json_init_args') and frag.json_init_args is not None:

View File

@@ -1,16 +0,0 @@
"""
This module mocks the external package django-webpack-loader within
python unittest execution, so python tests have no dependency on
frontend assets. See LEARNER-1938 for further details.
"""
from django import template
register = template.Library()
@register.simple_tag
def render_bundle(bundle_name):
"""
This is the only webpack_loader function we call directly.
"""
return ''

View File

@@ -646,6 +646,11 @@ def process_npm_assets():
copy_vendor_library(library, skip_if_missing=True)
@task
@needs(
'pavelib.prereqs.install_python_prereqs',
)
@no_help
def process_xmodule_assets():
"""
Process XModule static assets.

View File

@@ -16,6 +16,7 @@ __test__ = False # do not collect
@needs(
'pavelib.prereqs.install_node_prereqs',
'pavelib.utils.test.utils.clean_reports_dir',
'pavelib.assets.process_xmodule_assets',
)
@cmdopts([
("suite=", "s", "Test suite to run"),

View File

@@ -183,6 +183,7 @@ class Env(object):
REPO_ROOT / 'cms/static/karma_cms_squire.conf.js',
REPO_ROOT / 'lms/static/karma_lms.conf.js',
REPO_ROOT / 'common/lib/xmodule/xmodule/js/karma_xmodule.conf.js',
REPO_ROOT / 'common/lib/xmodule/xmodule/js/karma_xmodule_webpack.conf.js',
REPO_ROOT / 'common/static/karma_common.conf.js',
REPO_ROOT / 'common/static/karma_common_requirejs.conf.js',
]
@@ -192,6 +193,7 @@ class Env(object):
'cms-squire',
'lms',
'xmodule',
'xmodule-webpack',
'common',
'common-requirejs'
]

View File

@@ -9,7 +9,11 @@ module.exports = {
path.resolve(__dirname, '../common/static/common/js/components/views/feedback_alert.js'),
path.resolve(__dirname, '../common/static/common/js/components/views/paging_footer.js'),
path.resolve(__dirname, '../cms/static/js/views/paging.js'),
path.resolve(__dirname, '../common/static/common/js/components/utils/view_utils.js')
path.resolve(__dirname, '../common/static/common/js/components/utils/view_utils.js'),
/descriptors\/js/,
/modules\/js/,
/common\/lib\/xmodule\/xmodule\/js\/src\//,
path.resolve(__dirname, '../openedx/features/course_bookmarks/static/course_bookmarks/js/views/bookmark_button.js')
],
// These files are used by RequireJS as well, so we can't remove
@@ -90,11 +94,11 @@ module.exports = {
path.resolve(__dirname, '../openedx/features/course_search/static/course_search/js/spec/course_search_spec.js'),
path.resolve(
__dirname,
'openedx/features/course_search/static/course_search/js/views/course_search_results_view.js'
'../openedx/features/course_search/static/course_search/js/views/course_search_results_view.js'
),
path.resolve(
__dirname,
'openedx/features/course_search/static/course_search/js/views/dashboard_search_results_view.js'
'../openedx/features/course_search/static/course_search/js/views/dashboard_search_results_view.js'
),
path.resolve(__dirname, '../openedx/features/course_search/static/course_search/js/views/search_results_view.js'),
path.resolve(__dirname, '../openedx/features/learner_profile/static/learner_profile/js/views/badge_list_container.js'),
@@ -102,7 +106,7 @@ module.exports = {
path.resolve(__dirname, '../openedx/features/learner_profile/static/learner_profile/js/views/badge_view.js'),
path.resolve(
__dirname,
'openedx/features/learner_profile/static/learner_profile/js/views/learner_profile_fields.js'
'../openedx/features/learner_profile/static/learner_profile/js/views/learner_profile_fields.js'
),
path.resolve(__dirname, '../openedx/features/learner_profile/static/learner_profile/js/views/section_two_tab.js'),
path.resolve(__dirname, '../openedx/features/learner_profile/static/learner_profile/js/views/share_modal_view.js'),

View File

@@ -8,9 +8,13 @@ var BundleTracker = require('webpack-bundle-tracker');
var StringReplace = require('string-replace-webpack-plugin');
var files = require('./webpack-config/file-lists.js');
var xmoduleJS = require('./common/static/xmodule/webpack.xmodule.config.js');
var filesWithRequireJSBlocks = [
path.resolve(__dirname, 'common/static/common/js/components/utils/view_utils.js'),
/descriptors\/js/,
/modules\/js/,
/common\/lib\/xmodule\/xmodule\/js\/src\//,
];
var defineHeader = /\(function ?\(((define|require|requirejs|\$)(, )?)+\) ?\{/;
@@ -64,7 +68,10 @@ module.exports = {
CookiePolicyBanner: './common/static/js/src/CookiePolicyBanner.jsx',
// Common
ReactRenderer: './common/static/js/src/ReactRenderer.jsx'
ReactRenderer: './common/static/js/src/ReactRenderer.jsx',
XModuleShim: 'xmodule/js/src/xmodule.js',
VerticalStudentView: './common/lib/xmodule/xmodule/assets/vertical/public/js/vertical_student_view.js'
},
output: {
@@ -114,7 +121,7 @@ module.exports = {
// https://github.com/webpack/webpack/issues/304#issuecomment-272150177
// (I've tried every other suggestion solution on that page, this
// was the only one that worked.)
/\/sinon\.js|codemirror-compressed\.js/
/\/sinon\.js|codemirror-compressed\.js|hls\.js/
],
rules: [
{
@@ -196,24 +203,24 @@ module.exports = {
replacements: [
{
pattern: /\(function\(AjaxPrefix\) {/,
replacement: function() { return ''; }
replacement: function () { return ''; }
},
{
pattern: /], function\(domReady, \$, str, Backbone, gettext, NotificationView\) {/,
replacement: function() {
replacement: function () {
// eslint-disable-next-line
return '], function(domReady, $, str, Backbone, gettext, NotificationView, AjaxPrefix) {';
}
},
{
pattern: /'..\/..\/common\/js\/components\/views\/feedback_notification',/,
replacement: function() {
replacement: function () {
return "'../../common/js/components/views/feedback_notification', 'AjaxPrefix',";
}
},
{
pattern: /}\).call\(this, AjaxPrefix\);/,
replacement: function() { return ''; }
replacement: function () { return ''; }
}
]
}
@@ -229,11 +236,19 @@ module.exports = {
},
{
test: /xblock\/core/,
loader: 'exports-loader?this.XBlock!imports-loader?jquery,jquery.immediateDescendents'
loader: 'exports-loader?window.XBlock!imports-loader?jquery,jquery.immediateDescendents,this=>window'
},
{
test: /xblock\/runtime.v1/,
loader: 'exports-loader?XBlock!imports-loader?XBlock=xblock/core'
loader: 'exports-loader?window.XBlock!imports-loader?XBlock=xblock/core,this=>window'
},
{
test: /descriptors\/js/,
loader: 'imports-loader?this=>window'
},
{
test: /modules\/js/,
loader: 'imports-loader?this=>window'
},
{
test: /codemirror/,
@@ -242,6 +257,30 @@ module.exports = {
{
test: /tinymce/,
loader: 'imports-loader?this=>window'
},
{
test: /xmodule\/js\/src\/xmodule/,
loader: 'exports-loader?window.XModule!imports-loader?this=>window'
},
{
test: /mock-ajax/,
loader: 'imports-loader?exports=>false'
},
{
test: /d3.min/,
use: [
'babel-loader',
{
loader: 'exports-loader',
options: {
d3: true
}
}
]
},
{
test: /logger/,
loader: 'imports-loader?this=>window'
}
]
},
@@ -264,6 +303,7 @@ module.exports = {
'jquery.smoothScroll': 'jquery.smooth-scroll.min',
'jquery.timepicker': 'timepicker/jquery.timepicker',
'backbone.associations': 'backbone-associations/backbone-associations-min',
squire: 'Squire',
// See sinon/webpack interaction weirdness:
// https://github.com/webpack/webpack/issues/304#issuecomment-272150177
@@ -271,6 +311,7 @@ module.exports = {
// was the only one that worked.)
sinon: __dirname + '/node_modules/sinon/pkg/sinon.js',
WordCloudMain: 'xmodule/assets/word_cloud/public/js/word_cloud_main',
hls: 'hls.js/dist/hls.js'
},
modules: [
'cms/djangoapps/pipeline_js/js',