diff --git a/common/static/common/js/discussion/mathjax_include.js b/common/static/common/js/discussion/mathjax_include.js
new file mode 100644
index 0000000000..942dc54b58
--- /dev/null
+++ b/common/static/common/js/discussion/mathjax_include.js
@@ -0,0 +1,47 @@
+// See common/templates/mathjax_include.html for info on Fast Preview mode.
+var disableFastPreview = true,
+ vendorScript;
+if (typeof MathJax === 'undefined') {
+ if (disableFastPreview) {
+ window.MathJax = {
+ menuSettings: {CHTMLpreview: false}
+ };
+ }
+
+ vendorScript = document.createElement('script');
+ vendorScript.onload = function() {
+ 'use strict';
+ var MathJax = window.MathJax,
+ setMathJaxDisplayDivSettings;
+ MathJax.Hub.Config({
+ tex2jax: {
+ inlineMath: [
+ ['\\(', '\\)'],
+ ['[mathjaxinline]', '[/mathjaxinline]']
+ ],
+ displayMath: [
+ ['\\[', '\\]'],
+ ['[mathjax]', '[/mathjax]']
+ ]
+ }
+ });
+ if (disableFastPreview) {
+ MathJax.Hub.processSectionDelay = 0;
+ }
+ MathJax.Hub.signal.Interest(function(message) {
+ if (message[0] === 'End Math') {
+ setMathJaxDisplayDivSettings();
+ }
+ });
+ setMathJaxDisplayDivSettings = function() {
+ $('.MathJax_Display').each(function() {
+ this.setAttribute('tabindex', '0');
+ this.setAttribute('aria-live', 'off');
+ this.removeAttribute('role');
+ this.removeAttribute('aria-readonly');
+ });
+ };
+ };
+ vendorScript.src = 'https://cdn.mathjax.org/mathjax/2.6-latest/MathJax.js?config=TeX-MML-AM_SVG';
+ document.body.appendChild(vendorScript);
+}
diff --git a/common/static/common/js/spec/discussion/view/discussion_thread_profile_view_spec.js b/common/static/common/js/spec/discussion/view/discussion_thread_profile_view_spec.js
index 14eebbc1cd..6fd71e343a 100644
--- a/common/static/common/js/spec/discussion/view/discussion_thread_profile_view_spec.js
+++ b/common/static/common/js/spec/discussion/view/discussion_thread_profile_view_spec.js
@@ -18,12 +18,6 @@
created_at: '2014-09-09T20:11:08Z'
};
this.imageTag = '
';
- window.MathJax = {
- Hub: {
- Queue: function() {
- }
- }
- };
});
makeView = function(thread) {
var view;
diff --git a/lms/djangoapps/teams/templates/teams/teams.html b/lms/djangoapps/teams/templates/teams/teams.html
index 58a43a1b0b..fa58f64b76 100644
--- a/lms/djangoapps/teams/templates/teams/teams.html
+++ b/lms/djangoapps/teams/templates/teams/teams.html
@@ -15,6 +15,7 @@ from openedx.core.djangolib.js_utils import (
<%block name="headextra">
<%static:css group='style-course-vendor'/>
<%static:css group='style-course'/>
+<%static:css group='style-inline-discussion'/>
<%include file="../discussion/_js_head_dependencies.html" />
%block>
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 73c2d81795..4bc97dae7b 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -1290,6 +1290,7 @@ dashboard_js = (
sorted(rooted_glob(PROJECT_ROOT / 'static', 'js/dashboard/**/*.js'))
)
discussion_js = (
+ rooted_glob(COMMON_ROOT / 'static', 'common/js/discussion/mathjax_include.js') +
rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/customwmd.js') +
rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/mathjax_accessible.js') +
rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/mathjax_delay_renderer.js') +
@@ -1454,6 +1455,18 @@ PIPELINE_CSS = {
],
'output_filename': 'css/discussion/lms-discussion-main-rtl.css',
},
+ 'style-inline-discussion': {
+ 'source_filenames': [
+ 'css/discussion/inline-discussion.css',
+ ],
+ 'output_filename': 'css/discussion/inline-discussion.css',
+ },
+ 'style-inline-discussion-rtl': {
+ 'source_filenames': [
+ 'css/discussion/inline-discussion-rtl.css',
+ ],
+ 'output_filename': 'css/discussion/inline-discussion-rtl.css',
+ },
'style-xmodule-annotations': {
'source_filenames': [
'css/vendor/ova/annotator.css',
diff --git a/lms/static/sass/_build-base-v1-rtl.scss b/lms/static/sass/_build-base-v1-rtl.scss
new file mode 100644
index 0000000000..7396056d54
--- /dev/null
+++ b/lms/static/sass/_build-base-v1-rtl.scss
@@ -0,0 +1,9 @@
+// libs and resets *do not edit*
+@import 'bourbon/bourbon'; // lib - bourbon
+@import 'vendor/bi-app/bi-app-rtl'; // set the layout for right to left languages
+@import 'base/variables-rtl';
+
+// base - utilities
+@import 'base/reset';
+@import 'base/variables';
+@import 'base/mixins';
diff --git a/lms/static/sass/_build-base-v1.scss b/lms/static/sass/_build-base-v1.scss
new file mode 100644
index 0000000000..30979a5af9
--- /dev/null
+++ b/lms/static/sass/_build-base-v1.scss
@@ -0,0 +1,8 @@
+// libs and resets *do not edit*
+@import 'bourbon/bourbon'; // lib - bourbon
+@import 'vendor/bi-app/bi-app-ltr'; // set the layout for left to right languages
+
+// base - utilities
+@import 'base/reset';
+@import 'base/variables';
+@import 'base/mixins';
diff --git a/lms/static/sass/_build-course.scss b/lms/static/sass/_build-course.scss
index 94ec63ae4f..58b8b2ccd7 100644
--- a/lms/static/sass/_build-course.scss
+++ b/lms/static/sass/_build-course.scss
@@ -59,10 +59,6 @@
// course - ccx_coach
@import "course/ccx_coach/dashboard";
-// discussion
-@import "course/discussion/form-wmd-toolbar";
-@import "course/discussion/form";
-
// search
@import 'search/_search';
diff --git a/lms/static/sass/_build-lms-v1.scss b/lms/static/sass/_build-lms-v1.scss
index 190ce65c16..79880c4501 100644
--- a/lms/static/sass/_build-lms-v1.scss
+++ b/lms/static/sass/_build-lms-v1.scss
@@ -61,22 +61,6 @@
@import 'course/auto-cert';
@import 'views/api-access';
-// app - discussion
-@import "discussion/utilities/variables-v1";
-@import "discussion/mixins";
-@import 'discussion/discussion'; // Process old file after definitions but before everything else, partial is deprecated.
-@import 'discussion/discussion-v1'; // Non-Pattern Library styling
-@import "discussion/elements/actions";
-@import "discussion/elements/editor";
-@import "discussion/elements/labels";
-@import "discussion/elements/navigation";
-@import "discussion/views/home";
-@import "discussion/views/thread";
-@import "discussion/views/create-edit-post";
-@import "discussion/views/response";
-@import 'discussion/utilities/developer';
-@import 'discussion/utilities/shame';
-
// search
@import 'search/search';
diff --git a/lms/static/sass/discussion/_build-discussion.scss b/lms/static/sass/discussion/_build-discussion.scss
new file mode 100644
index 0000000000..abcc25c4f2
--- /dev/null
+++ b/lms/static/sass/discussion/_build-discussion.scss
@@ -0,0 +1,27 @@
+// ------------------------------
+// Discussions: Shared Build Compile
+
+// About: Sass compile for discussion elements that are shared between LTR and RTL UI. Configuration and vendor specific imports happen before this shared set of imports is compiled in the inline-discussion-*.scss files.
+
+// Set the relative path to the static root
+$static-path: '../..';
+
+// Styles for discussions
+@import "utilities/variables-v1";
+@import "mixins";
+@import 'discussion'; // Process old file after definitions but before everything else, partial is deprecated.
+@import 'discussion-v1'; // Non-Pattern Library styling
+@import "elements/actions";
+@import "elements/editor";
+@import "elements/labels";
+@import "elements/navigation";
+@import "views/home";
+@import "views/thread";
+@import "views/create-edit-post";
+@import "views/response";
+@import 'utilities/developer';
+@import 'utilities/shame';
+
+// Styles for WYSIWYG Markdown editor
+@import "../course/discussion/form-wmd-toolbar";
+@import "../course/discussion/form";
diff --git a/lms/static/sass/discussion/inline-discussion-rtl.scss b/lms/static/sass/discussion/inline-discussion-rtl.scss
new file mode 100644
index 0000000000..c8b887ab0b
--- /dev/null
+++ b/lms/static/sass/discussion/inline-discussion-rtl.scss
@@ -0,0 +1,11 @@
+// Discussions - CSS application architecture
+// Version 1 styling (pre-Pattern Library)
+// ====================
+
+@import '../build-base-v1-rtl';
+
+// base - elements
+@import '../elements/typography';
+
+// app - discussion
+@import 'build-discussion';
diff --git a/lms/static/sass/discussion/inline-discussion.scss b/lms/static/sass/discussion/inline-discussion.scss
new file mode 100644
index 0000000000..a8f048d9d7
--- /dev/null
+++ b/lms/static/sass/discussion/inline-discussion.scss
@@ -0,0 +1,11 @@
+// Discussions - CSS application architecture
+// Version 1 styling (pre-Pattern Library)
+// ====================
+
+@import '../build-base-v1';
+
+// base - elements
+@import '../elements/typography';
+
+// app - discussion
+@import 'build-discussion';
diff --git a/lms/static/sass/lms-main-v1-rtl.scss b/lms/static/sass/lms-main-v1-rtl.scss
index aa560e25a4..36e5f63566 100644
--- a/lms/static/sass/lms-main-v1-rtl.scss
+++ b/lms/static/sass/lms-main-v1-rtl.scss
@@ -2,17 +2,5 @@
// Version 1 styling (pre-Pattern Library)
// ====================
-// libs and resets *do not edit*
-@import 'bourbon/bourbon'; // lib - bourbon
-@import 'vendor/bi-app/bi-app-rtl'; // set the layout for right to left languages
-@import 'base/variables-rtl';
-
-// BASE *default edX offerings*
-// ====================
-
-// base - utilities
-@import 'base/reset';
-@import 'base/variables';
-@import 'base/mixins';
-
+@import 'build-base-v1-rtl';
@import 'build-lms-v1'; // shared app style assets/rendering
diff --git a/lms/static/sass/lms-main-v1.scss b/lms/static/sass/lms-main-v1.scss
index 6f629fa414..13ecbb358a 100644
--- a/lms/static/sass/lms-main-v1.scss
+++ b/lms/static/sass/lms-main-v1.scss
@@ -2,16 +2,5 @@
// Version 1 styling (pre-Pattern Library)
// ====================
-// libs and resets *do not edit*
-@import 'bourbon/bourbon'; // lib - bourbon
-@import 'vendor/bi-app/bi-app-ltr'; // set the layout for left to right languages
-
-// BASE *default edX offerings*
-// ====================
-
-// base - utilities
-@import 'base/reset';
-@import 'base/variables';
-@import 'base/mixins';
-
+@import 'build-base-v1';
@import 'build-lms-v1'; // shared app style assets/rendering
diff --git a/lms/templates/courseware/courseware-chromeless.html b/lms/templates/courseware/courseware-chromeless.html
index 644ca0839b..315302fd7a 100644
--- a/lms/templates/courseware/courseware-chromeless.html
+++ b/lms/templates/courseware/courseware-chromeless.html
@@ -40,7 +40,10 @@ ${static.get_page_title_breadcrumbs(course_name())}
<%static:css group='style-student-notes'/>
% endif
-<%include file="../discussion/_js_head_dependencies.html" />
+
+
+
+
${HTML(fragment.head_html())}
%block>
@@ -53,7 +56,7 @@ ${static.get_page_title_breadcrumbs(course_name())}
<%static:js group='courseware'/>
- <%include file="../discussion/_js_body_dependencies.html" />
+ <%include file="/mathjax_include.html" args="disable_fast_preview=True"/>
% if staff_access:
<%include file="xqa_interface.html"/>
% endif
diff --git a/lms/templates/courseware/courseware.html b/lms/templates/courseware/courseware.html
index 5d8c3e1467..bf2015fcc1 100644
--- a/lms/templates/courseware/courseware.html
+++ b/lms/templates/courseware/courseware.html
@@ -61,7 +61,10 @@ ${static.get_page_title_breadcrumbs(course_name())}
<%static:css group='style-student-notes'/>
% endif
-<%include file="../discussion/_js_head_dependencies.html" />
+
+
+
+
${HTML(fragment.head_html())}
%block>
@@ -73,7 +76,7 @@ ${static.get_page_title_breadcrumbs(course_name())}
<%static:js group='courseware'/>
- <%include file="../discussion/_js_body_dependencies.html" />
+ <%include file="/mathjax_include.html" args="disable_fast_preview=True"/>
% if settings.FEATURES.get('ENABLE_COURSEWARE_SEARCH'):
<%static:require_module module_name="js/search/course/course_search_factory" class_name="CourseSearchFactory">
diff --git a/lms/templates/discussion/_discussion_inline.html b/lms/templates/discussion/_discussion_inline.html
index 1dac1d0fb6..b616d9e8fe 100644
--- a/lms/templates/discussion/_discussion_inline.html
+++ b/lms/templates/discussion/_discussion_inline.html
@@ -1,5 +1,7 @@
<%page expression_filter="h"/>
+
<%include file="_underscore_templates.html" />
+
<%!
from django.utils.translation import ugettext as _
from json import dumps as json_dumps
diff --git a/openedx/core/lib/tests/test_xblock_utils.py b/openedx/core/lib/tests/test_xblock_utils.py
index fff35e495c..438ee59816 100644
--- a/openedx/core/lib/tests/test_xblock_utils.py
+++ b/openedx/core/lib/tests/test_xblock_utils.py
@@ -22,7 +22,11 @@ from openedx.core.lib.xblock_utils import (
replace_jump_to_id_urls,
replace_course_urls,
replace_static_urls,
- sanitize_html_id
+ sanitize_html_id,
+)
+from openedx.core.lib.xblock_builtin import (
+ get_css_dependencies,
+ get_js_dependencies,
)
@@ -181,3 +185,41 @@ class TestXblockUtils(SharedModuleStoreTestCase):
clean_string = sanitize_html_id(dirty_string)
self.assertEqual(clean_string, 'I_have_un_allowed_characters')
+
+ @ddt.data(
+ (True, ["combined.css"]),
+ (False, ["a.css", "b.css", "c.css"]),
+ )
+ @ddt.unpack
+ def test_get_css_dependencies(self, pipeline_enabled, expected_css_dependencies):
+ """
+ Verify that `get_css_dependencies` returns correct list of files.
+ """
+ pipeline_css = {
+ 'style-group': {
+ 'source_filenames': ["a.css", "b.css", "c.css"],
+ 'output_filename': "combined.css"
+ }
+ }
+ with self.settings(PIPELINE_ENABLED=pipeline_enabled, PIPELINE_CSS=pipeline_css):
+ css_dependencies = get_css_dependencies("style-group")
+ self.assertEqual(css_dependencies, expected_css_dependencies)
+
+ @ddt.data(
+ (True, ["combined.js"]),
+ (False, ["a.js", "b.js", "c.js"]),
+ )
+ @ddt.unpack
+ def test_get_js_dependencies(self, pipeline_enabled, expected_js_dependencies):
+ """
+ Verify that `get_js_dependencies` returns correct list of files.
+ """
+ pipeline_js = {
+ 'js-group': {
+ 'source_filenames': ["a.js", "b.js", "c.js"],
+ 'output_filename': "combined.js"
+ }
+ }
+ with self.settings(PIPELINE_ENABLED=pipeline_enabled, PIPELINE_JS=pipeline_js):
+ js_dependencies = get_js_dependencies("js-group")
+ self.assertEqual(js_dependencies, expected_js_dependencies)
diff --git a/openedx/core/lib/xblock_builtin/__init__.py b/openedx/core/lib/xblock_builtin/__init__.py
index e69de29bb2..539820d0dc 100644
--- a/openedx/core/lib/xblock_builtin/__init__.py
+++ b/openedx/core/lib/xblock_builtin/__init__.py
@@ -0,0 +1,31 @@
+"""
+Helper functions shared by built-in XBlocks.
+
+"""
+
+
+from django.conf import settings
+
+
+def get_css_dependencies(group):
+ """
+ Returns list of CSS dependencies belonging to `group` in settings.PIPELINE_JS.
+
+ Respects `PIPELINE_ENABLED` setting.
+ """
+ if settings.PIPELINE_ENABLED:
+ return [settings.PIPELINE_CSS[group]['output_filename']]
+ else:
+ return settings.PIPELINE_CSS[group]['source_filenames']
+
+
+def get_js_dependencies(group):
+ """
+ Returns list of JS dependencies belonging to `group` in settings.PIPELINE_JS.
+
+ Respects `PIPELINE_ENABLED` setting.
+ """
+ if settings.PIPELINE_ENABLED:
+ return [settings.PIPELINE_JS[group]['output_filename']]
+ else:
+ return settings.PIPELINE_JS[group]['source_filenames']
diff --git a/openedx/core/lib/xblock_builtin/xblock_discussion/xblock_discussion.py b/openedx/core/lib/xblock_builtin/xblock_discussion/xblock_discussion.py
index ad8623803e..6808781f53 100644
--- a/openedx/core/lib/xblock_builtin/xblock_discussion/xblock_discussion.py
+++ b/openedx/core/lib/xblock_builtin/xblock_discussion/xblock_discussion.py
@@ -4,6 +4,9 @@ Discussion XBlock
"""
import logging
+from django.templatetags.static import static
+from django.utils.translation import get_language_bidi
+
from xblockutils.resources import ResourceLoader
from xblockutils.studio_editable import StudioEditableXBlockMixin
from xmodule.raw_module import RawDescriptor
@@ -13,6 +16,9 @@ from xblock.fields import Scope, String, UNIQUE_ID
from xblock.fragment import Fragment
from xmodule.xml_module import XmlParserMixin
+from openedx.core.lib.xblock_builtin import get_css_dependencies, get_js_dependencies
+
+
log = logging.getLogger(__name__)
loader = ResourceLoader(__name__) # pylint: disable=invalid-name
@@ -88,6 +94,57 @@ class DiscussionXBlock(XBlock, StudioEditableXBlockMixin, XmlParserMixin):
return None
return user_service._django_user # pylint: disable=protected-access
+ @staticmethod
+ def vendor_js_dependencies():
+ """
+ Returns list of vendor JS files that this XBlock depends on.
+
+ The helper function that it uses to obtain the list of vendor JS files
+ works in conjunction with the Django pipeline to ensure that in development mode
+ the files are loaded individually, but in production just the single bundle is loaded.
+ """
+ return get_js_dependencies('discussion_vendor')
+
+ @staticmethod
+ def js_dependencies():
+ """
+ Returns list of JS files that this XBlock depends on.
+
+ The helper function that it uses to obtain the list of JS files
+ works in conjunction with the Django pipeline to ensure that in development mode
+ the files are loaded individually, but in production just the single bundle is loaded.
+ """
+ return get_js_dependencies('discussion')
+
+ @staticmethod
+ def css_dependencies():
+ """
+ Returns list of CSS files that this XBlock depends on.
+
+ The helper function that it uses to obtain the list of CSS files
+ works in conjunction with the Django pipeline to ensure that in development mode
+ the files are loaded individually, but in production just the single bundle is loaded.
+ """
+ if get_language_bidi():
+ return get_css_dependencies('style-inline-discussion-rtl')
+ else:
+ return get_css_dependencies('style-inline-discussion')
+
+ def add_resource_urls(self, fragment):
+ """
+ Adds URLs for JS and CSS resources that this XBlock depends on to `fragment`.
+ """
+ # Head dependencies
+ for vendor_js_file in self.vendor_js_dependencies():
+ fragment.add_resource_url(static(vendor_js_file), "application/javascript", "head")
+
+ for css_file in self.css_dependencies():
+ fragment.add_css_url(static(css_file))
+
+ # Body dependencies
+ for js_file in self.js_dependencies():
+ fragment.add_javascript_url(static(js_file))
+
def has_permission(self, permission):
"""
Encapsulates lms specific functionality, as `has_permission` is not
@@ -108,12 +165,11 @@ class DiscussionXBlock(XBlock, StudioEditableXBlockMixin, XmlParserMixin):
"""
fragment = Fragment()
- course = self.runtime.modulestore.get_course(self.course_key)
+ self.add_resource_urls(fragment)
context = {
'discussion_id': self.discussion_id,
'user': self.django_user,
- 'course': course,
'course_id': self.course_key,
'can_create_thread': self.has_permission("create_thread"),
'can_create_comment': self.has_permission("create_comment"),