Load XBlocks with webpack rather than RequireJS
This commit is contained in:
@@ -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')
|
||||
|
||||
@@ -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([]);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
55
cms/static/karma_cms_webpack.conf.js
Normal file
55
cms/static/karma_cms_webpack.conf.js
Normal 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);
|
||||
};
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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}">
|
||||
|
||||
@@ -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='': []
|
||||
)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
82
common/lib/xmodule/xmodule/js/karma_runner_webpack.js
Normal file
82
common/lib/xmodule/xmodule/js/karma_runner_webpack.js
Normal 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);
|
||||
};
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
45
common/lib/xmodule/xmodule/js/karma_xmodule_webpack.conf.js
Normal file
45
common/lib/xmodule/xmodule/js/karma_xmodule_webpack.conf.js
Normal 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);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -78,4 +78,4 @@ function(AsyncProcess) {
|
||||
});
|
||||
});
|
||||
});
|
||||
}(RequireJS.require));
|
||||
}(require));
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
oldOTBD = window.onTouchBasedDevice;
|
||||
window.onTouchBasedDevice = jasmine
|
||||
.createSpy('onTouchBasedDevice').and.returnValue(null);
|
||||
|
||||
state = jasmine.initializePlayer('video_html5.html');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -100,4 +100,4 @@ function(Iterator) {
|
||||
});
|
||||
});
|
||||
});
|
||||
}(RequireJS.require));
|
||||
}(require));
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -64,4 +64,4 @@ function(Sjson) {
|
||||
});
|
||||
});
|
||||
});
|
||||
}(RequireJS.require));
|
||||
}(require));
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import '../helper.js'
|
||||
|
||||
(function(undefined) {
|
||||
'use strict';
|
||||
var describeInfo, state, oldOTBD;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
|
||||
16
common/lib/xmodule/xmodule/js/spec/video_helper.js
Normal file
16
common/lib/xmodule/xmodule/js/spec/video_helper.js
Normal 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);
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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__':
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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">
|
||||
|
||||
4
common/templates/xmodule_shim.html
Normal file
4
common/templates/xmodule_shim.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<%namespace name='static' file='static_content.html'/>
|
||||
|
||||
<%static:webpack entry="XModuleShim"/>
|
||||
${content}
|
||||
@@ -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),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -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='': []
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(function(define, undefined) {
|
||||
(function(define) {
|
||||
'use strict';
|
||||
define([
|
||||
'gettext', 'jquery', 'underscore', 'backbone', 'text!templates/fields/message_banner.underscore'
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 ''
|
||||
@@ -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.
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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'
|
||||
]
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user