diff --git a/cms/djangoapps/contentstore/features/component.py b/cms/djangoapps/contentstore/features/component.py
index f8425a3600..e99f6e2ae5 100644
--- a/cms/djangoapps/contentstore/features/component.py
+++ b/cms/djangoapps/contentstore/features/component.py
@@ -42,7 +42,7 @@ def add_a_multi_step_component(step, is_advanced, category):
def see_a_multi_step_component(step, category):
# Wait for all components to finish rendering
- selector = 'li.component section.xmodule_display'
+ selector = 'li.component section.xblock-student_view'
world.wait_for(lambda _: len(world.css_find(selector)) == len(step.hashes))
for idx, step_hash in enumerate(step.hashes):
diff --git a/cms/djangoapps/contentstore/views/preview.py b/cms/djangoapps/contentstore/views/preview.py
index 12df3fcf7a..2d9a66be1e 100644
--- a/cms/djangoapps/contentstore/views/preview.py
+++ b/cms/djangoapps/contentstore/views/preview.py
@@ -8,7 +8,7 @@ from django.core.urlresolvers import reverse
from django.contrib.auth.decorators import login_required
from mitxmako.shortcuts import render_to_response, render_to_string
-from xmodule_modifiers import replace_static_urls, wrap_xmodule
+from xmodule_modifiers import replace_static_urls, wrap_xblock
from xmodule.error_module import ErrorDescriptor
from xmodule.errortracker import exc_info_to_str
from xmodule.exceptions import NotFoundError, ProcessingError
@@ -77,7 +77,7 @@ def preview_component(request, location):
component = modulestore().get_item(location)
# Wrap the generated fragment in the xmodule_editor div so that the javascript
# can bind to it correctly
- component.runtime.wrappers.append(partial(wrap_xmodule, 'xmodule_edit.html'))
+ component.runtime.wrappers.append(wrap_xblock)
try:
content = component.render('studio_view').content
@@ -105,11 +105,6 @@ def preview_module_system(request, preview_id, descriptor):
course_id = get_course_for_item(descriptor.location).location.course_id
- if descriptor.location.category == 'static_tab':
- wrapper_template = 'xmodule_tab_display.html'
- else:
- wrapper_template = 'xmodule_display.html'
-
return ModuleSystem(
static_url=settings.STATIC_URL,
ajax_url=reverse('preview_dispatch', args=[preview_id, descriptor.location.url(), '']).rstrip('/'),
@@ -129,7 +124,7 @@ def preview_module_system(request, preview_id, descriptor):
# Set up functions to modify the fragment produced by student_view
wrappers=(
# This wrapper wraps the module in the template specified above
- partial(wrap_xmodule, wrapper_template),
+ partial(wrap_xblock, display_name_only=descriptor.location.category == 'static_tab'),
# This wrapper replaces urls in the output that start with /static
# with the correct course-specific url for the static content
diff --git a/cms/static/coffee/spec/main.coffee b/cms/static/coffee/spec/main.coffee
index 4251307892..aba1e5405a 100644
--- a/cms/static/coffee/spec/main.coffee
+++ b/cms/static/coffee/spec/main.coffee
@@ -16,6 +16,7 @@ requirejs.config({
"jquery.fileupload": "xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload",
"jquery.iframe-transport": "xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.iframe-transport",
"jquery.inputnumber": "xmodule_js/common_static/js/vendor/html5-input-polyfills/number-polyfill",
+ "jquery.immediateDescendents": "xmodule_js/common_static/coffee/src/jquery.immediateDescendents",
"datepair": "xmodule_js/common_static/js/vendor/timepicker/datepair",
"date": "xmodule_js/common_static/js/vendor/date",
"underscore": "xmodule_js/common_static/js/vendor/underscore-min",
@@ -25,6 +26,7 @@ requirejs.config({
"tinymce": "xmodule_js/common_static/js/vendor/tiny_mce/tiny_mce",
"jquery.tinymce": "xmodule_js/common_static/js/vendor/tiny_mce/jquery.tinymce",
"xmodule": "xmodule_js/src/xmodule",
+ "xblock": "xmodule_js/common_static/coffee/src/xblock",
"utility": "xmodule_js/common_static/js/src/utility",
"sinon": "xmodule_js/common_static/js/vendor/sinon-1.7.1",
"squire": "xmodule_js/common_static/js/vendor/Squire",
@@ -129,6 +131,14 @@ requirejs.config({
deps: ["jasmine"],
exports: "AsyncSpec"
},
+ "xblock/core": {
+ exports: "XBlock",
+ deps: ["jquery", "jquery.immediateDescendents"]
+ },
+ "xblock/runtime.v1": {
+ exports: "XBlock",
+ deps: ["xblock/core"]
+ },
"coffee/src/main": {
deps: ["coffee/src/ajax_prefix"]
diff --git a/cms/static/coffee/spec/main_squire.coffee b/cms/static/coffee/spec/main_squire.coffee
index ee23792509..928e10c61f 100644
--- a/cms/static/coffee/spec/main_squire.coffee
+++ b/cms/static/coffee/spec/main_squire.coffee
@@ -16,6 +16,7 @@ requirejs.config({
"jquery.fileupload": "xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload",
"jquery.iframe-transport": "xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.iframe-transport",
"jquery.inputnumber": "xmodule_js/common_static/js/vendor/html5-input-polyfills/number-polyfill",
+ "jquery.immediateDescendents": "xmodule_js/common_static/coffee/src/jquery.immediateDescendents",
"datepair": "xmodule_js/common_static/js/vendor/timepicker/datepair",
"date": "xmodule_js/common_static/js/vendor/date",
"underscore": "xmodule_js/common_static/js/vendor/underscore-min",
@@ -25,6 +26,7 @@ requirejs.config({
"tinymce": "xmodule_js/common_static/js/vendor/tiny_mce/tiny_mce",
"jquery.tinymce": "xmodule_js/common_static/js/vendor/tiny_mce/jquery.tinymce",
"xmodule": "xmodule_js/src/xmodule",
+ "xblock": "xmodule_js/common_static/coffee/src/xblock",
"utility": "xmodule_js/common_static/js/src/utility",
"sinon": "xmodule_js/common_static/js/vendor/sinon-1.7.1",
"squire": "xmodule_js/common_static/js/vendor/Squire",
@@ -129,6 +131,14 @@ requirejs.config({
deps: ["jasmine"],
exports: "AsyncSpec"
},
+ "xblock/core": {
+ exports: "XBlock",
+ deps: ["jquery", "jquery.immediateDescendents"]
+ },
+ "xblock/runtime.v1": {
+ exports: "XBlock",
+ deps: ["xblock/core"]
+ },
"coffee/src/main": {
deps: ["coffee/src/ajax_prefix"]
diff --git a/cms/static/coffee/spec/views/module_edit_spec.coffee b/cms/static/coffee/spec/views/module_edit_spec.coffee
index 157fb18e6a..f108e5046e 100644
--- a/cms/static/coffee/spec/views/module_edit_spec.coffee
+++ b/cms/static/coffee/spec/views/module_edit_spec.coffee
@@ -19,7 +19,7 @@ define ["coffee/src/views/module_edit", "xmodule"], (ModuleEdit) ->
Delete
-
+
@@ -66,10 +66,9 @@ define ["coffee/src/views/module_edit", "xmodule"], (ModuleEdit) ->
describe "loadDisplay", ->
beforeEach ->
- spyOn(XModule, 'loadModule')
+ spyOn(XBlock, 'initializeBlock')
@moduleEdit.loadDisplay()
it "loads the .xmodule-display inside the module editor", ->
- expect(XModule.loadModule).toHaveBeenCalled()
- expect(XModule.loadModule.mostRecentCall.args[0]).toBe($('.xmodule_display'))
-
+ expect(XBlock.initializeBlock).toHaveBeenCalled()
+ expect(XBlock.initializeBlock.mostRecentCall.args[0]).toBe($('.xblock-student_view'))
diff --git a/cms/static/coffee/src/views/module_edit.coffee b/cms/static/coffee/src/views/module_edit.coffee
index 041fc97c47..2b6cdcec0b 100644
--- a/cms/static/coffee/src/views/module_edit.coffee
+++ b/cms/static/coffee/src/views/module_edit.coffee
@@ -1,7 +1,7 @@
-define ["backbone", "jquery", "underscore", "gettext", "xmodule",
+define ["backbone", "jquery", "underscore", "gettext", "xblock/runtime.v1",
"js/views/feedback_notification", "js/views/metadata", "js/collections/metadata"
- "jquery.inputnumber"],
-(Backbone, $, _, gettext, XModule, NotificationView, MetadataView, MetadataCollection) ->
+ "jquery.inputnumber", "xmodule"],
+(Backbone, $, _, gettext, XBlock, NotificationView, MetadataView, MetadataCollection) ->
class ModuleEdit extends Backbone.View
tagName: 'li'
className: 'component'
@@ -21,11 +21,11 @@ define ["backbone", "jquery", "underscore", "gettext", "xmodule",
$component_editor: => @$el.find('.component-editor')
loadDisplay: ->
- XModule.loadModule(@$el.find('.xmodule_display'))
+ XBlock.initializeBlock(@$el.find('.xblock-student_view'))
loadEdit: ->
if not @module
- @module = XModule.loadModule(@$el.find('.xmodule_edit'))
+ @module = XBlock.initializeBlock(@$el.find('.xblock-studio_view'))
# At this point, metadata-edit.html will be loaded, and the metadata (as JSON) is available.
metadataEditor = @$el.find('.metadata_edit')
metadataData = metadataEditor.data('metadata')
diff --git a/cms/static/js_test.yml b/cms/static/js_test.yml
index 0e163b61d1..3a85b3a3cf 100644
--- a/cms/static/js_test.yml
+++ b/cms/static/js_test.yml
@@ -54,6 +54,8 @@ lib_paths:
- xmodule_js/common_static/js/vendor/date.js
- xmodule_js/common_static/js/vendor/domReady.js
- xmodule_js/common_static/js/vendor/jquery.smooth-scroll.min.js
+ - xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js
+ - xmodule_js/common_static/coffee/src/xblock
# Paths to source JavaScript files
src_paths:
diff --git a/cms/static/js_test_squire.yml b/cms/static/js_test_squire.yml
index 2284319997..2b37e4792f 100644
--- a/cms/static/js_test_squire.yml
+++ b/cms/static/js_test_squire.yml
@@ -49,6 +49,7 @@ lib_paths:
- xmodule_js/common_static/js/vendor/jasmine.async.js
- xmodule_js/common_static/js/vendor/CodeMirror/codemirror.js
- xmodule_js/src/xmodule.js
+ - xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js
- xmodule_js/common_static/js/test/i18n.js
# Paths to source JavaScript files
diff --git a/cms/templates/base.html b/cms/templates/base.html
index ff2fcf9b01..04aa735747 100644
--- a/cms/templates/base.html
+++ b/cms/templates/base.html
@@ -54,6 +54,7 @@ var require = {
"jquery.fileupload": "js/vendor/jQuery-File-Upload/js/jquery.fileupload",
"jquery.iframe-transport": "js/vendor/jQuery-File-Upload/js/jquery.iframe-transport",
"jquery.inputnumber": "js/vendor/html5-input-polyfills/number-polyfill",
+ "jquery.immediateDescendents": "coffee/src/jquery.immediateDescendents",
"datepair": "js/vendor/timepicker/datepair",
"date": "js/vendor/date",
"tzAbbr": "js/vendor/tzAbbr",
@@ -64,6 +65,7 @@ var require = {
"tinymce": "js/vendor/tiny_mce/tiny_mce",
"jquery.tinymce": "js/vendor/tiny_mce/jquery.tinymce",
"xmodule": "/xmodule/xmodule",
+ "xblock": "coffee/src/xblock",
"utility": "js/src/utility",
"draggabilly": "js/vendor/draggabilly.pkgd",
@@ -159,6 +161,14 @@ var require = {
"mathjax": {
exports: "MathJax"
},
+ "xblock/core": {
+ exports: "XBlock",
+ deps: ["jquery", "jquery.immediateDescendents"]
+ },
+ "xblock/runtime.v1": {
+ exports: "XBlock",
+ deps: ["xblock/core"]
+ },
"coffee/src/main": {
deps: ["coffee/src/ajax_prefix"]
diff --git a/cms/templates/xmodule_tab_display.html b/cms/templates/xmodule_tab_display.html
deleted file mode 100644
index 3b6ecc9593..0000000000
--- a/cms/templates/xmodule_tab_display.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/common/djangoapps/terrain/ui_helpers.py b/common/djangoapps/terrain/ui_helpers.py
index be52c4bd3d..30d2452908 100644
--- a/common/djangoapps/terrain/ui_helpers.py
+++ b/common/djangoapps/terrain/ui_helpers.py
@@ -115,6 +115,7 @@ def wait_for_js_variable_truthy(variable):
def wait_for_xmodule():
"Wait until the XModule Javascript has loaded on the page."
world.wait_for_js_variable_truthy("XModule")
+ world.wait_for_js_variable_truthy("XBlock")
@world.absorb
diff --git a/common/djangoapps/xmodule_modifiers.py b/common/djangoapps/xmodule_modifiers.py
index 43942f3edf..50e98ee994 100644
--- a/common/djangoapps/xmodule_modifiers.py
+++ b/common/djangoapps/xmodule_modifiers.py
@@ -14,6 +14,7 @@ from xblock.fragment import Fragment
from xmodule.seq_module import SequenceModule
from xmodule.vertical_module import VerticalModule
+from xmodule.x_module import shim_xmodule_js, XModuleDescriptor, XModule
log = logging.getLogger(__name__)
@@ -28,32 +29,50 @@ def wrap_fragment(fragment, new_content):
return wrapper_frag
-def wrap_xmodule(template, block, view, frag, context): # pylint: disable=unused-argument
+def wrap_xblock(block, view, frag, context, display_name_only=False): # pylint: disable=unused-argument
"""
- Wraps the results of get_html in a standard with identifying
+ Wraps the results of rendering an XBlock view in a standard with identifying
data so that the appropriate javascript module can be loaded onto it.
- get_html: An XModule.get_html method or an XModuleDescriptor.get_html method
- module: An XModule
- template: A template that takes the variables:
- content: the results of get_html,
- display_name: the display name of the xmodule, if available (None otherwise)
- class_: the module class name
- module_name: the js_module_name of the module
+ :param block: An XBlock (that may be an XModule or XModuleDescriptor)
+ :param view: The name of the view that rendered the fragment being wrapped
+ :param frag: The :class:`Fragment` to be wrapped
+ :param context: The context passed to the view being rendered
+ :param display_name_only: If true, don't render the fragment content at all.
+ Instead, just render the `display_name` of `block`
"""
- # If XBlock generated this class, then use the first baseclass
- # as the name (since that's the original, unmixed class)
+ # If any mixins have been applied, then use the unmixed class
class_name = getattr(block, 'unmixed_class', block.__class__).__name__
+ data = {}
+ css_classes = ['xblock', 'xblock-' + view]
+
+ if isinstance(block, (XModule, XModuleDescriptor)):
+ if view == 'student_view':
+ # The block is acting as an XModule
+ css_classes.append('xmodule_display')
+ elif view == 'studio_view':
+ # The block is acting as an XModuleDescriptor
+ css_classes.append('xmodule_edit')
+
+ css_classes.append('xmodule_' + class_name)
+ data['type'] = block.js_module_name
+ shim_xmodule_js(frag)
+
+ if frag.js_init_fn:
+ data['init'] = frag.js_init_fn
+ data['runtime-version'] = frag.js_init_version
+ data['usage-id'] = block.scope_ids.usage_id
+ data['block-type'] = block.scope_ids.block_type
+
template_context = {
- 'content': frag.content,
- 'display_name': block.display_name,
- 'class_': class_name,
- 'module_name': block.js_module_name,
+ 'content': block.display_name if display_name_only else frag.content,
+ 'classes': css_classes,
+ 'data_attributes': ' '.join('data-{}="{}"'.format(key, value) for key, value in data.items()),
}
- return wrap_fragment(frag, render_to_string(template, template_context))
+ return wrap_fragment(frag, render_to_string('xblock_wrapper.html', template_context))
def replace_jump_to_id_urls(course_id, jump_to_id_base_url, block, view, frag, context): # pylint: disable=unused-argument
diff --git a/common/lib/xmodule/xmodule/js/fixtures/annotatable.html b/common/lib/xmodule/xmodule/js/fixtures/annotatable.html
index 61020d95e8..04b61ce9ee 100644
--- a/common/lib/xmodule/xmodule/js/fixtures/annotatable.html
+++ b/common/lib/xmodule/xmodule/js/fixtures/annotatable.html
@@ -1,4 +1,4 @@
-