Merge pull request #1224 from cpennington/lms-xblock-js-execution
Add an XBlock javascript runtime, and use it to run XModules
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -19,7 +19,7 @@ define ["coffee/src/views/module_edit", "xmodule"], (ModuleEdit) ->
|
||||
<a href="#" class="delete-button"><span class="delete-icon white"></span>Delete</a>
|
||||
</div>
|
||||
<span class="drag-handle"></span>
|
||||
<section class="xmodule_display xmodule_stub" data-type="StubModule">
|
||||
<section class="xblock xblock-student_view xmodule_display xmodule_stub" data-type="StubModule">
|
||||
<div id="stub-module-content"/>
|
||||
</section>
|
||||
</li>
|
||||
@@ -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'))
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<section class="xmodule_display xmodule_${class_}" data-type="${module_name}">
|
||||
${display_name}
|
||||
</section>
|
||||
@@ -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
|
||||
|
||||
@@ -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 <section> with identifying
|
||||
Wraps the results of rendering an XBlock view in a standard <section> 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<section class='xmodule_display xmodule_AnnotatableModule' data-type='Annotatable'>
|
||||
<section class='xblock xblock-student_view xmodule_display xmodule_AnnotatableModule' data-type='Annotatable'>
|
||||
<div class="annotatable-wrapper">
|
||||
<div class="annotatable-header">
|
||||
<div class="annotatable-title">First Annotation Exercise</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<section class="course-content">
|
||||
<section class="xmodule_display xmodule_CombinedOpenEndedModule" data-type="CombinedOpenEnded">
|
||||
<section class="xblock xblock-student_view xmodule_display xmodule_CombinedOpenEndedModule" data-type="CombinedOpenEnded">
|
||||
<section id="combined-open-ended" class="combined-open-ended" data-ajax-url="/courses/MITx/6.002x/2012_Fall/modx/i4x://MITx/6.002x/combinedopenended/CombinedOE" data-allow_reset="False" data-state="assessing" data-task-count="2" data-task-number="1">
|
||||
<h2>Problem 1</h2>
|
||||
<div class="status-container">
|
||||
@@ -8,7 +8,7 @@
|
||||
<section id="combined-open-ended-status" class="combined-open-ended-status">
|
||||
<div class="statusitem" data-status-number="0">
|
||||
Step 1 (Problem complete) : 1 / 1
|
||||
<span class="correct" id="status"></span>
|
||||
<span class="correct" id="status"></span>
|
||||
</div>
|
||||
<div class="statusitem statusitem-current" data-status-number="1">
|
||||
Step 2 (Being scored) : None / 1
|
||||
@@ -109,14 +109,14 @@ location = i4x://MITx/6.002x/combinedopenended/CombinedOE
|
||||
github = <a href="https://github.com/MITx/content-mit-6002x/tree/master/combinedopenended/CombinedOE.xml">https://github.com/MITx/content-mit-6002x/tree/master/combinedopenended/CombinedOE.xml</a>
|
||||
definition = <pre>None</pre>
|
||||
metadata = {
|
||||
"showanswer": "attempted",
|
||||
"display_name": "Problem 1",
|
||||
"graceperiod": "1 day 12 hours 59 minutes 59 seconds",
|
||||
"xqa_key": "KUBrWtK3RAaBALLbccHrXeD3RHOpmZ2A",
|
||||
"rerandomize": "never",
|
||||
"start": "2012-09-05T12:00",
|
||||
"attempts": "10000",
|
||||
"data_dir": "content-mit-6002x",
|
||||
"showanswer": "attempted",
|
||||
"display_name": "Problem 1",
|
||||
"graceperiod": "1 day 12 hours 59 minutes 59 seconds",
|
||||
"xqa_key": "KUBrWtK3RAaBALLbccHrXeD3RHOpmZ2A",
|
||||
"rerandomize": "never",
|
||||
"start": "2012-09-05T12:00",
|
||||
"attempts": "10000",
|
||||
"data_dir": "content-mit-6002x",
|
||||
"max_score": "1"
|
||||
}
|
||||
category = CombinedOpenEndedModule
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<li id="vert-0" data-id="i4x://Me/19.002/crowdsource_hinter/crowdsource_hinter_def7a1142dd0">
|
||||
|
||||
|
||||
<section class="xmodule_display xmodule_CrowdsourceHinterModule" data-type="Hinter" id="hinter-root">
|
||||
<section class="xblock xblock-student_view xmodule_display xmodule_CrowdsourceHinterModule" data-type="Hinter" id="hinter-root">
|
||||
|
||||
|
||||
<section class="xmodule_display xmodule_CapaModule" data-type="Problem" id="problem">
|
||||
<section class="xblock xblock-student_view xmodule_display xmodule_CapaModule" data-type="Problem" id="problem">
|
||||
<section id="problem_i4x-Me-19_002-problem-Numerical_Input" class="problems-wrapper" data-problem-id="i4x://Me/19.002/problem/Numerical_Input" data-url="/courses/Me/19.002/Test/modx/i4x://Me/19.002/problem/Numerical_Input" data-progress_status="done" data-progress_detail="1/1">
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<section class='xmodule_display xmodule_CapaModule' data-type='Problem'>
|
||||
<section id='problem_1'
|
||||
<section class='xblock xblock-student_view xmodule_display xmodule_CapaModule' data-type='Problem'>
|
||||
<section id='problem_1'
|
||||
class='problems-wrapper'
|
||||
data-problem-id='i4x://edX/101/problem/Problem1'
|
||||
data-problem-id='i4x://edX/101/problem/Problem1'
|
||||
data-url='/problem/Problem1'>
|
||||
</section>
|
||||
</section>
|
||||
@@ -2,7 +2,7 @@ describe 'Annotatable', ->
|
||||
beforeEach ->
|
||||
loadFixtures 'annotatable.html'
|
||||
describe 'constructor', ->
|
||||
el = $('.xmodule_display.xmodule_AnnotatableModule')
|
||||
el = $('.xblock-student_view.xmodule_AnnotatableModule')
|
||||
beforeEach ->
|
||||
@annotatable = new Annotatable(el)
|
||||
it 'works', ->
|
||||
|
||||
@@ -25,7 +25,7 @@ describe 'Problem', ->
|
||||
|
||||
it 'set the element from html', ->
|
||||
@problem999 = new Problem ("
|
||||
<section class='xmodule_display xmodule_CapaModule' data-type='Problem'>
|
||||
<section class='xblock xblock-student_view xmodule_display xmodule_CapaModule' data-type='Problem'>
|
||||
<section id='problem_999'
|
||||
class='problems-wrapper'
|
||||
data-problem-id='i4x://edX/999/problem/Quiz'
|
||||
@@ -36,14 +36,14 @@ describe 'Problem', ->
|
||||
expect(@problem999.element_id).toBe 'problem_999'
|
||||
|
||||
it 'set the element from loadFixtures', ->
|
||||
@problem1 = new Problem($('.xmodule_display'))
|
||||
@problem1 = new Problem($('.xblock-student_view'))
|
||||
expect(@problem1.element_id).toBe 'problem_1'
|
||||
|
||||
describe 'bind', ->
|
||||
beforeEach ->
|
||||
spyOn window, 'update_schematics'
|
||||
MathJax.Hub.getAllJax.andReturn [@stubbedJax]
|
||||
@problem = new Problem($('.xmodule_display'))
|
||||
@problem = new Problem($('.xblock-student_view'))
|
||||
|
||||
it 'set mathjax typeset', ->
|
||||
expect(MathJax.Hub.Queue).toHaveBeenCalled()
|
||||
@@ -78,7 +78,7 @@ describe 'Problem', ->
|
||||
|
||||
describe 'renderProgressState', ->
|
||||
beforeEach ->
|
||||
@problem = new Problem($('.xmodule_display'))
|
||||
@problem = new Problem($('.xblock-student_view'))
|
||||
#@renderProgressState = @problem.renderProgressState
|
||||
|
||||
describe 'with a status of "none"', ->
|
||||
@@ -97,7 +97,7 @@ describe 'Problem', ->
|
||||
|
||||
describe 'render', ->
|
||||
beforeEach ->
|
||||
@problem = new Problem($('.xmodule_display'))
|
||||
@problem = new Problem($('.xblock-student_view'))
|
||||
@bind = @problem.bind
|
||||
spyOn @problem, 'bind'
|
||||
|
||||
@@ -130,7 +130,7 @@ describe 'Problem', ->
|
||||
|
||||
describe 'check', ->
|
||||
beforeEach ->
|
||||
@problem = new Problem($('.xmodule_display'))
|
||||
@problem = new Problem($('.xblock-student_view'))
|
||||
@problem.answers = 'foo=1&bar=2'
|
||||
|
||||
it 'log the problem_check event', ->
|
||||
@@ -177,7 +177,7 @@ describe 'Problem', ->
|
||||
|
||||
describe 'reset', ->
|
||||
beforeEach ->
|
||||
@problem = new Problem($('.xmodule_display'))
|
||||
@problem = new Problem($('.xblock-student_view'))
|
||||
|
||||
it 'log the problem_reset event', ->
|
||||
@problem.answers = 'foo=1&bar=2'
|
||||
@@ -198,7 +198,7 @@ describe 'Problem', ->
|
||||
|
||||
describe 'show', ->
|
||||
beforeEach ->
|
||||
@problem = new Problem($('.xmodule_display'))
|
||||
@problem = new Problem($('.xblock-student_view'))
|
||||
@problem.el.prepend '<div id="answer_1_1" /><div id="answer_1_2" />'
|
||||
|
||||
describe 'when the answer has not yet shown', ->
|
||||
@@ -331,7 +331,7 @@ describe 'Problem', ->
|
||||
|
||||
describe 'save', ->
|
||||
beforeEach ->
|
||||
@problem = new Problem($('.xmodule_display'))
|
||||
@problem = new Problem($('.xblock-student_view'))
|
||||
@problem.answers = 'foo=1&bar=2'
|
||||
|
||||
it 'log the problem_save event', ->
|
||||
@@ -353,7 +353,7 @@ describe 'Problem', ->
|
||||
|
||||
describe 'refreshMath', ->
|
||||
beforeEach ->
|
||||
@problem = new Problem($('.xmodule_display'))
|
||||
@problem = new Problem($('.xblock-student_view'))
|
||||
$('#input_example_1').val 'E=mc^2'
|
||||
@problem.refreshMath target: $('#input_example_1').get(0)
|
||||
|
||||
@@ -363,7 +363,7 @@ describe 'Problem', ->
|
||||
|
||||
describe 'updateMathML', ->
|
||||
beforeEach ->
|
||||
@problem = new Problem($('.xmodule_display'))
|
||||
@problem = new Problem($('.xblock-student_view'))
|
||||
@stubbedJax.root.toMathML.andReturn '<MathML>'
|
||||
|
||||
describe 'when there is no exception', ->
|
||||
@@ -383,7 +383,7 @@ describe 'Problem', ->
|
||||
|
||||
describe 'refreshAnswers', ->
|
||||
beforeEach ->
|
||||
@problem = new Problem($('.xmodule_display'))
|
||||
@problem = new Problem($('.xblock-student_view'))
|
||||
@problem.el.html '''
|
||||
<textarea class="CodeMirror" />
|
||||
<input id="input_1_1" name="input_1_1" class="schematic" value="one" />
|
||||
|
||||
@@ -32,4 +32,4 @@ class @Conditional
|
||||
else
|
||||
$(element).show()
|
||||
|
||||
XModule.loadModules @el
|
||||
XBlock.initializeBlocks @el
|
||||
|
||||
@@ -92,7 +92,7 @@ class @Sequence
|
||||
@el.trigger "sequence:change"
|
||||
@mark_active new_position
|
||||
@$('#seq_content').html @contents.eq(new_position - 1).text()
|
||||
XModule.loadModules(@$('#seq_content'))
|
||||
XBlock.initializeBlocks(@$('#seq_content'))
|
||||
|
||||
MathJax.Hub.Queue(["Typeset", MathJax.Hub, "seq_content"]) # NOTE: Actually redundant. Some other MathJax call also being performed
|
||||
window.update_schematics() # For embedded circuit simulator exercises in 6.002x
|
||||
|
||||
@@ -1,44 +1,31 @@
|
||||
@XModule =
|
||||
@XModule = {}
|
||||
|
||||
@XBlockToXModuleShim = (runtime, element) ->
|
||||
###
|
||||
Load a single module (either an edit module or a display module)
|
||||
from the supplied element, which should have a data-type attribute
|
||||
specifying the class to load
|
||||
###
|
||||
loadModule: (element) ->
|
||||
moduleType = $(element).data('type')
|
||||
if moduleType == 'None'
|
||||
return
|
||||
moduleType = $(element).data('type')
|
||||
if moduleType == 'None'
|
||||
return
|
||||
|
||||
try
|
||||
module = new window[moduleType](element)
|
||||
if $(element).hasClass('xmodule_edit')
|
||||
$(document).trigger('XModule.loaded.edit', [element, module])
|
||||
try
|
||||
module = new window[moduleType](element)
|
||||
if $(element).hasClass('xmodule_edit')
|
||||
$(document).trigger('XModule.loaded.edit', [element, module])
|
||||
|
||||
if $(element).hasClass('xmodule_display')
|
||||
$(document).trigger('XModule.loaded.display', [element, module])
|
||||
if $(element).hasClass('xmodule_display')
|
||||
$(document).trigger('XModule.loaded.display', [element, module])
|
||||
|
||||
return module
|
||||
return module
|
||||
|
||||
catch error
|
||||
if window.console and console.log
|
||||
console.error "Unable to load #{moduleType}: #{error.message}"
|
||||
else
|
||||
throw error
|
||||
|
||||
###
|
||||
Load all modules on the page of the specified type.
|
||||
If container is provided, only load modules inside that element
|
||||
Type is one of 'display' or 'edit'
|
||||
###
|
||||
loadModules: (container) ->
|
||||
selector = ".xmodule_edit, .xmodule_display"
|
||||
|
||||
if container?
|
||||
modules = $(container).find(selector)
|
||||
catch error
|
||||
if window.console and console.log
|
||||
console.error "Unable to load #{moduleType}: #{error.message}"
|
||||
else
|
||||
modules = $(selector)
|
||||
throw error
|
||||
|
||||
modules.each((idx, element) -> XModule.loadModule element)
|
||||
|
||||
class @XModule.Descriptor
|
||||
|
||||
|
||||
@@ -85,6 +85,14 @@ class HTMLSnippet(object):
|
||||
.format(self.__class__))
|
||||
|
||||
|
||||
def shim_xmodule_js(fragment):
|
||||
"""
|
||||
Set up the XBlock -> XModule shim on the supplied :class:`xblock.fragment.Fragment`
|
||||
"""
|
||||
if not fragment.js_init_fn:
|
||||
fragment.initialize_js('XBlockToXModuleShim')
|
||||
|
||||
|
||||
class XModuleMixin(XBlockMixin):
|
||||
"""
|
||||
Fields and methods used by XModules internally.
|
||||
@@ -92,6 +100,29 @@ class XModuleMixin(XBlockMixin):
|
||||
Adding this Mixin to an :class:`XBlock` allows it to cooperate with old-style :class:`XModules`
|
||||
"""
|
||||
|
||||
# Attributes for inspection of the descriptor
|
||||
|
||||
# This indicates whether the xmodule is a problem-type.
|
||||
# It should respond to max_score() and grade(). It can be graded or ungraded
|
||||
# (like a practice problem).
|
||||
has_score = False
|
||||
|
||||
# Class level variable
|
||||
|
||||
# True if this descriptor always requires recalculation of grades, for
|
||||
# example if the score can change via an extrnal service, not just when the
|
||||
# student interacts with the module on the page. A specific example is
|
||||
# FoldIt, which posts grade-changing updates through a separate API.
|
||||
always_recalculate_grades = False
|
||||
|
||||
# The default implementation of get_icon_class returns the icon_class
|
||||
# attribute of the class
|
||||
#
|
||||
# This attribute can be overridden by subclasses, and
|
||||
# the function can also be overridden if the icon class depends on the data
|
||||
# in the module
|
||||
icon_class = 'other'
|
||||
|
||||
display_name = String(
|
||||
display_name="Display Name",
|
||||
help="This name appears in the horizontal navigation at the top of the page.",
|
||||
@@ -335,13 +366,6 @@ class XModule(XModuleMixin, HTMLSnippet, XBlock): # pylint: disable=abstract-me
|
||||
See the HTML module for a simple example.
|
||||
"""
|
||||
|
||||
# The default implementation of get_icon_class returns the icon_class
|
||||
# attribute of the class
|
||||
#
|
||||
# This attribute can be overridden by subclasses, and
|
||||
# the function can also be overridden if the icon class depends on the data
|
||||
# in the module
|
||||
icon_class = 'other'
|
||||
|
||||
has_score = descriptor_attr('has_score')
|
||||
_field_data_cache = descriptor_attr('_field_data_cache')
|
||||
@@ -516,20 +540,6 @@ class XModuleDescriptor(XModuleMixin, HTMLSnippet, ResourceTemplates, XBlock):
|
||||
entry_point = "xmodule.v1"
|
||||
module_class = XModule
|
||||
|
||||
# Attributes for inspection of the descriptor
|
||||
|
||||
# This indicates whether the xmodule is a problem-type.
|
||||
# It should respond to max_score() and grade(). It can be graded or ungraded
|
||||
# (like a practice problem).
|
||||
has_score = False
|
||||
|
||||
# Class level variable
|
||||
|
||||
# True if this descriptor always requires recalculation of grades, for
|
||||
# example if the score can change via an extrnal service, not just when the
|
||||
# student interacts with the module on the page. A specific example is
|
||||
# FoldIt, which posts grade-changing updates through a separate API.
|
||||
always_recalculate_grades = False
|
||||
|
||||
# VS[compat]. Backwards compatibility code that can go away after
|
||||
# importing 2012 courses.
|
||||
@@ -862,9 +872,13 @@ class DescriptorSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable
|
||||
return result
|
||||
|
||||
def render(self, block, view_name, context=None):
|
||||
if isinstance(block, (XModule, XModuleDescriptor)) and view_name == 'student_view':
|
||||
if view_name == 'student_view':
|
||||
assert block.xmodule_runtime is not None
|
||||
return block.xmodule_runtime.render(block._xmodule, view_name, context)
|
||||
if isinstance(block, (XModule, XModuleDescriptor)):
|
||||
to_render = block._xmodule
|
||||
else:
|
||||
to_render = block
|
||||
return block.xmodule_runtime.render(to_render, view_name, context)
|
||||
else:
|
||||
return super(DescriptorSystem, self).render(block, view_name, context)
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
describe "$.immediateDescendents", ->
|
||||
beforeEach ->
|
||||
setFixtures """
|
||||
<div>
|
||||
<div class='xblock' id='child'>
|
||||
<div class='xblock' id='nested'/>
|
||||
</div>
|
||||
<div>
|
||||
<div class='xblock' id='grandchild'/>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
@descendents = $('#jasmine-fixtures').immediateDescendents(".xblock").get()
|
||||
|
||||
it "finds non-immediate children", ->
|
||||
expect(@descendents).toContain($('#grandchild').get(0))
|
||||
|
||||
it "finds immediate children", ->
|
||||
expect(@descendents).toContain($('#child').get(0))
|
||||
|
||||
it "skips nested descendents", ->
|
||||
expect(@descendents).not.toContain($('#nested').get(0))
|
||||
|
||||
it "finds 2 children", ->
|
||||
expect(@descendents.length).toBe(2)
|
||||
64
common/static/coffee/spec/xblock/core_spec.coffee
Normal file
64
common/static/coffee/spec/xblock/core_spec.coffee
Normal file
@@ -0,0 +1,64 @@
|
||||
describe "XBlock", ->
|
||||
beforeEach ->
|
||||
setFixtures """
|
||||
<div>
|
||||
<div class='xblock' id='vA' data-runtime-version="A" data-init="initFnA" data-name="a-name"/>
|
||||
<div>
|
||||
<div class='xblock' id='vZ' data-runtime-version="Z" data-init="initFnZ"/>
|
||||
</div>
|
||||
<div class='xblock' id='missing-version' data-init='initFnA' data-name='no-version'/>
|
||||
<div class='xblock' id='missing-init' data-runtime-version="A" data-name='no-init'/>
|
||||
</div>
|
||||
"""
|
||||
|
||||
describe "initializeBlock", ->
|
||||
beforeEach ->
|
||||
XBlock.runtime.vA = jasmine.createSpy().andReturn('runtimeA')
|
||||
XBlock.runtime.vZ = jasmine.createSpy().andReturn('runtimeZ')
|
||||
|
||||
window.initFnA = jasmine.createSpy()
|
||||
window.initFnZ = jasmine.createSpy()
|
||||
|
||||
@fakeChildren = ['list', 'of', 'children']
|
||||
spyOn(XBlock, 'initializeBlocks').andReturn(@fakeChildren)
|
||||
|
||||
@vABlock = XBlock.initializeBlock($('#vA')[0])
|
||||
@vZBlock = XBlock.initializeBlock($('#vZ')[0])
|
||||
@missingVersionBlock = XBlock.initializeBlock($('#missing-version')[0])
|
||||
@missingInitBlock = XBlock.initializeBlock($('#missing-init')[0])
|
||||
|
||||
it "loads the right runtime version", ->
|
||||
expect(XBlock.runtime.vA).toHaveBeenCalledWith($('#vA')[0], @fakeChildren)
|
||||
expect(XBlock.runtime.vZ).toHaveBeenCalledWith($('#vZ')[0], @fakeChildren)
|
||||
|
||||
it "loads the right init function", ->
|
||||
expect(window.initFnA).toHaveBeenCalledWith('runtimeA', $('#vA')[0])
|
||||
expect(window.initFnZ).toHaveBeenCalledWith('runtimeZ', $('#vZ')[0])
|
||||
|
||||
it "loads when missing versions", ->
|
||||
expect(@missingVersionBlock.element).toBe($('#missing-version'))
|
||||
expect(@missingVersionBlock.name).toBe('no-version')
|
||||
|
||||
it "loads when missing init fn", ->
|
||||
expect(@missingInitBlock.element).toBe($('#missing-init'))
|
||||
expect(@missingInitBlock.name).toBe('no-init')
|
||||
|
||||
it "adds names to blocks", ->
|
||||
expect(@vABlock.name).toBe('a-name')
|
||||
|
||||
it "leaves leaves missing names undefined", ->
|
||||
expect(@vZBlock.name).toBeUndefined()
|
||||
|
||||
it "attaches the element to the block", ->
|
||||
expect(@vABlock.element).toBe($('#vA')[0])
|
||||
expect(@vZBlock.element).toBe($('#vZ')[0])
|
||||
expect(@missingVersionBlock.element).toBe($('#missing-version')[0])
|
||||
expect(@missingInitBlock.element).toBe($('#missing-init')[0])
|
||||
|
||||
describe "initializeBlocks", ->
|
||||
it "initializes children", ->
|
||||
spyOn(XBlock, 'initializeBlock')
|
||||
|
||||
XBlock.initializeBlocks($('#jasmine-fixtures'))
|
||||
expect(XBlock.initializeBlock).toHaveBeenCalledWith($('#vA')[0])
|
||||
expect(XBlock.initializeBlock).toHaveBeenCalledWith($('#vZ')[0])
|
||||
22
common/static/coffee/spec/xblock/runtime.v1_spec.coffee
Normal file
22
common/static/coffee/spec/xblock/runtime.v1_spec.coffee
Normal file
@@ -0,0 +1,22 @@
|
||||
describe "XBlock.runtime.v1", ->
|
||||
beforeEach ->
|
||||
setFixtures """
|
||||
<div class='xblock' data-usage-id='fake-usage-id'/>
|
||||
"""
|
||||
@children = [
|
||||
{name: 'childA'},
|
||||
{name: 'childB'}
|
||||
]
|
||||
|
||||
@element = $('.xblock')[0]
|
||||
|
||||
@runtime = XBlock.runtime.v1(@element, @children)
|
||||
it "provides a handler url", ->
|
||||
expect(@runtime.handlerUrl('foo')).toBe('/xblock/handler/fake-usage-id/foo')
|
||||
|
||||
it "provides a list of children", ->
|
||||
expect(@runtime.children).toBe(@children)
|
||||
|
||||
it "maps children by name", ->
|
||||
expect(@runtime.childMap.childA).toBe(@children[0])
|
||||
expect(@runtime.childMap.childB).toBe(@children[1])
|
||||
11
common/static/coffee/src/jquery.immediateDescendents.coffee
Normal file
11
common/static/coffee/src/jquery.immediateDescendents.coffee
Normal file
@@ -0,0 +1,11 @@
|
||||
# Find all the children of an element that match the selector, but only
|
||||
# the first instance found down any path. For example, we'll find all
|
||||
# the ".xblock" elements below us, but not the ones that are themselves
|
||||
# contained somewhere inside ".xblock" elements.
|
||||
jQuery.fn.immediateDescendents = (selector) ->
|
||||
@children().map ->
|
||||
elem = jQuery(this)
|
||||
if elem.is(selector)
|
||||
this
|
||||
else
|
||||
elem.immediateDescendents(selector).get()
|
||||
26
common/static/coffee/src/xblock/core.coffee
Normal file
26
common/static/coffee/src/xblock/core.coffee
Normal file
@@ -0,0 +1,26 @@
|
||||
@XBlock =
|
||||
runtime: {}
|
||||
|
||||
initializeBlock: (element) ->
|
||||
$element = $(element)
|
||||
children = @initializeBlocks($element)
|
||||
version = $element.data("runtime-version")
|
||||
initFnName = $element.data("init")
|
||||
if version? and initFnName?
|
||||
runtime = @runtime["v#{version}"](element, children)
|
||||
initFn = window[initFnName]
|
||||
block = initFn(runtime, element) ? {}
|
||||
else
|
||||
elementTag = $('<div>').append($element.clone()).html();
|
||||
console.log("Block #{elementTag} is missing data-runtime-version or data-init, and can't be initialized")
|
||||
block = {}
|
||||
|
||||
block.element = element
|
||||
block.name = $element.data("name")
|
||||
|
||||
block
|
||||
|
||||
initializeBlocks: (element) ->
|
||||
$(element).immediateDescendents(".xblock").map((idx, elem) =>
|
||||
@initializeBlock elem
|
||||
).toArray()
|
||||
12
common/static/coffee/src/xblock/runtime.v1.coffee
Normal file
12
common/static/coffee/src/xblock/runtime.v1.coffee
Normal file
@@ -0,0 +1,12 @@
|
||||
@XBlock.runtime.v1 = (element, children) ->
|
||||
childMap = {}
|
||||
$.each children, (idx, child) ->
|
||||
childMap[child.name] = child
|
||||
|
||||
return {
|
||||
handlerUrl: (handlerName) ->
|
||||
usageId = $(element).data("usage-id")
|
||||
"/xblock/handler/#{usageId}/#{handlerName}"
|
||||
children: children
|
||||
childMap: childMap
|
||||
}
|
||||
@@ -35,6 +35,7 @@ lib_paths:
|
||||
- js/vendor/jquery.timeago.js
|
||||
- coffee/src/ajax_prefix.js
|
||||
- js/test/add_ajax_prefix.js
|
||||
- coffee/src/jquery.immediateDescendents.js
|
||||
|
||||
# Paths to source JavaScript files
|
||||
src_paths:
|
||||
|
||||
3
common/templates/xblock_wrapper.html
Normal file
3
common/templates/xblock_wrapper.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<section class="${' '.join(classes)}" ${data_attributes}>
|
||||
${content}
|
||||
</section>
|
||||
@@ -1,3 +0,0 @@
|
||||
<section class="xmodule_display xmodule_${class_}" data-type="${module_name}">
|
||||
${content}
|
||||
</section>
|
||||
@@ -1,3 +0,0 @@
|
||||
<section class="xmodule_edit xmodule_${class_}" data-type="${module_name}">
|
||||
${content}
|
||||
</section>
|
||||
@@ -25,7 +25,7 @@ from xmodule.modulestore import Location
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError
|
||||
from xmodule.x_module import ModuleSystem
|
||||
from xmodule_modifiers import replace_course_urls, replace_jump_to_id_urls, replace_static_urls, add_histogram, wrap_xmodule
|
||||
from xmodule_modifiers import replace_course_urls, replace_jump_to_id_urls, replace_static_urls, add_histogram, wrap_xblock
|
||||
|
||||
import static_replace
|
||||
from psychometrics.psychoanalyze import make_psychometrics_data_update_handler
|
||||
@@ -340,7 +340,7 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours
|
||||
# Wrap the output display in a single div to allow for the XModule
|
||||
# javascript to be bound correctly
|
||||
if wrap_xmodule_display is True:
|
||||
block_wrappers.append(partial(wrap_xmodule, 'xmodule_display.html'))
|
||||
block_wrappers.append(wrap_xblock)
|
||||
|
||||
# TODO (cpennington): When modules are shared between courses, the static
|
||||
# prefix is going to have to be specific to the module, not the directory
|
||||
|
||||
@@ -299,7 +299,7 @@ class TestHtmlModifiers(ModuleStoreTestCase):
|
||||
)
|
||||
result_fragment = module.render('student_view')
|
||||
|
||||
self.assertIn('section class="xmodule_display xmodule_HtmlModule"', result_fragment.content)
|
||||
self.assertIn('section class="xblock xblock-student_view xmodule_display xmodule_HtmlModule"', result_fragment.content)
|
||||
|
||||
def test_xmodule_display_wrapper_disabled(self):
|
||||
module = render.get_module(
|
||||
@@ -312,7 +312,7 @@ class TestHtmlModifiers(ModuleStoreTestCase):
|
||||
)
|
||||
result_fragment = module.render('student_view')
|
||||
|
||||
self.assertNotIn('section class="xmodule_display xmodule_HtmlModule"', result_fragment.content)
|
||||
self.assertNotIn('section class="xblock xblock-student_view xmodule_display xmodule_HtmlModule"', result_fragment.content)
|
||||
|
||||
def test_static_link_rewrite(self):
|
||||
module = render.get_module(
|
||||
|
||||
@@ -11,7 +11,7 @@ from django.utils.html import escape
|
||||
from django.http import Http404
|
||||
from django.conf import settings
|
||||
|
||||
from xmodule_modifiers import wrap_xmodule
|
||||
from xmodule_modifiers import wrap_xblock
|
||||
from xmodule.html_module import HtmlDescriptor
|
||||
from xmodule.modulestore import MONGO_MODULESTORE_TYPE
|
||||
from xmodule.modulestore.django import modulestore
|
||||
@@ -164,7 +164,7 @@ def _section_send_email(course_id, access, course):
|
||||
""" Provide data for the corresponding bulk email section """
|
||||
html_module = HtmlDescriptor(course.system, DictFieldData({'data': ''}), ScopeIds(None, None, None, None))
|
||||
fragment = course.system.render(html_module, 'studio_view')
|
||||
fragment = wrap_xmodule('xmodule_edit.html', html_module, 'studio_view', fragment, None)
|
||||
fragment = wrap_xblock(html_module, 'studio_view', fragment, None)
|
||||
email_editor = fragment.content
|
||||
section_data = {
|
||||
'section_key': 'send_email',
|
||||
|
||||
@@ -23,7 +23,7 @@ from django.core.urlresolvers import reverse
|
||||
from django.core.mail import send_mail
|
||||
from django.utils import timezone
|
||||
|
||||
from xmodule_modifiers import wrap_xmodule
|
||||
from xmodule_modifiers import wrap_xblock
|
||||
import xmodule.graders as xmgraders
|
||||
from xmodule.modulestore import MONGO_MODULESTORE_TYPE
|
||||
from xmodule.modulestore.django import modulestore
|
||||
@@ -832,8 +832,8 @@ def instructor_dashboard(request, course_id):
|
||||
# HTML editor for email
|
||||
if idash_mode == 'Email' and is_studio_course:
|
||||
html_module = HtmlDescriptor(course.system, DictFieldData({'data': html_message}), ScopeIds(None, None, None, None))
|
||||
fragment = course.system.render(html_module, 'studio_view')
|
||||
fragment = wrap_xmodule('xmodule_edit.html', html_module, 'studio_view', fragment, None)
|
||||
fragment = html_module.render('studio_view')
|
||||
fragment = wrap_xblock(html_module, 'studio_view', fragment, None)
|
||||
email_editor = fragment.content
|
||||
|
||||
# Enable instructor email only if the following conditions are met:
|
||||
|
||||
@@ -17,7 +17,7 @@ describe 'Courseware', ->
|
||||
spyOn(window, 'Histogram')
|
||||
spyOn(window, 'Problem')
|
||||
spyOn(window, 'Video')
|
||||
spyOn(XModule, 'loadModules')
|
||||
spyOn(XBlock, 'initializeBlocks')
|
||||
setFixtures """
|
||||
<div class="course-content">
|
||||
<div id="video_1" class="video" data-streams="1.0:abc1234"></div>
|
||||
@@ -30,7 +30,7 @@ describe 'Courseware', ->
|
||||
@courseware.render()
|
||||
|
||||
it 'ensure that the XModules have been loaded', ->
|
||||
expect(XModule.loadModules).toHaveBeenCalled()
|
||||
expect(XBlock.initializeBlocks).toHaveBeenCalled()
|
||||
|
||||
it 'detect the histrogram element and convert it', ->
|
||||
expect(window.Histogram).toHaveBeenCalledWith('3', [[0, 1]])
|
||||
|
||||
@@ -11,7 +11,7 @@ class @Courseware
|
||||
new Courseware
|
||||
|
||||
render: ->
|
||||
XModule.loadModules()
|
||||
XBlock.initializeBlocks($('.course-content'))
|
||||
$('.course-content .histogram').each ->
|
||||
id = $(this).attr('id').replace(/histogram_/, '')
|
||||
try
|
||||
|
||||
@@ -12,7 +12,7 @@ std_ajax_err = -> window.InstructorDashboard.util.std_ajax_err.apply this, argum
|
||||
class SendEmail
|
||||
constructor: (@$container) ->
|
||||
# gather elements
|
||||
@$emailEditor = XModule.loadModule($('.xmodule_edit'));
|
||||
@$emailEditor = XBlock.initializeBlock($('.xblock-studio_view'));
|
||||
@$send_to = @$container.find("select[name='send_to']'")
|
||||
@$subject = @$container.find("input[name='subject']'")
|
||||
@$btn_send = @$container.find("input[name='send']'")
|
||||
@@ -51,16 +51,16 @@ class SendEmail
|
||||
dataType: 'json'
|
||||
url: @$btn_send.data 'endpoint'
|
||||
data: send_data
|
||||
success: (data) =>
|
||||
success: (data) =>
|
||||
@display_response success_message
|
||||
|
||||
error: std_ajax_err =>
|
||||
|
||||
error: std_ajax_err =>
|
||||
@fail_with_error gettext('Error sending email.')
|
||||
|
||||
else
|
||||
@$task_response.empty()
|
||||
@$request_response_error.empty()
|
||||
|
||||
|
||||
fail_with_error: (msg) ->
|
||||
console.warn msg
|
||||
@$task_response.empty()
|
||||
|
||||
@@ -38,6 +38,8 @@ lib_paths:
|
||||
- xmodule_js/common_static/js/vendor/jquery.cookie.js
|
||||
- xmodule_js/common_static/js/vendor/flot/jquery.flot.js
|
||||
- xmodule_js/common_static/js/vendor/CodeMirror/codemirror.js
|
||||
- xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js
|
||||
- xmodule_js/common_static/coffee/src/xblock
|
||||
- xmodule_js/src/capa/
|
||||
- xmodule_js/src/video/
|
||||
- xmodule_js/src/xmodule.js
|
||||
|
||||
@@ -546,7 +546,7 @@ function goto( mode)
|
||||
<input type="submit" name="action" value="Send email">
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var emailEditor = XModule.loadModule($('.xmodule_edit'));
|
||||
var emailEditor = XBlock.initializeBlock($('.xblock-studio_view'));
|
||||
document.idashform.onsubmit = function() {
|
||||
this.message.value = emailEditor.save()['data'];
|
||||
return true;
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace :'test:js' do
|
||||
end
|
||||
|
||||
desc "Run the JavaScript tests in your default browser"
|
||||
task :dev, [:env] => [:clean_test_files, :'assets:coffee'] do |t, args|
|
||||
task :dev, [:env] => [:clean_test_files, :'assets:coffee:_watch'] do |t, args|
|
||||
if args[:env].nil?
|
||||
puts "Error: No test suite specified. Try one of these instead:"
|
||||
print_js_test_cmds('dev')
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
-e git+https://github.com/eventbrite/zendesk.git@d53fe0e81b623f084e91776bcf6369f8b7b63879#egg=zendesk
|
||||
|
||||
# Our libraries:
|
||||
-e git+https://github.com/edx/XBlock.git@cee38a15f#egg=XBlock
|
||||
-e git+https://github.com/edx/XBlock.git@74c1a2e9#egg=XBlock
|
||||
-e git+https://github.com/edx/codejail.git@0a1b468#egg=codejail
|
||||
-e git+https://github.com/edx/diff-cover.git@v0.2.6#egg=diff_cover
|
||||
-e git+https://github.com/edx/js-test-tool.git@v0.1.4#egg=js_test_tool
|
||||
|
||||
Reference in New Issue
Block a user