@@ -76,9 +76,13 @@ class CapaModule(XModule):
|
||||
'''
|
||||
icon_class = 'problem'
|
||||
|
||||
js = {'coffee': [resource_string(__name__, 'js/src/capa/display.coffee')],
|
||||
js = {'coffee': [resource_string(__name__, 'js/src/capa/display.coffee'),
|
||||
resource_string(__name__, 'js/src/collapsible.coffee'),
|
||||
resource_string(__name__, 'js/src/javascript_loader.coffee'),
|
||||
],
|
||||
'js': [resource_string(__name__, 'js/src/capa/imageinput.js'),
|
||||
resource_string(__name__, 'js/src/capa/schematic.js')]}
|
||||
|
||||
js_module_name = "Problem"
|
||||
css = {'scss': [resource_string(__name__, 'css/capa/display.scss')]}
|
||||
|
||||
|
||||
@@ -21,7 +21,11 @@ log = logging.getLogger("mitx.courseware")
|
||||
|
||||
|
||||
class HtmlModule(XModule):
|
||||
js = {'coffee': [resource_string(__name__, 'js/src/html/display.coffee')]}
|
||||
js = {'coffee': [resource_string(__name__, 'js/src/javascript_loader.coffee'),
|
||||
resource_string(__name__, 'js/src/collapsible.coffee'),
|
||||
resource_string(__name__, 'js/src/html/display.coffee')
|
||||
]
|
||||
}
|
||||
js_module_name = "HTMLModule"
|
||||
|
||||
def get_html(self):
|
||||
|
||||
@@ -27,11 +27,7 @@ class @Problem
|
||||
@$('section.action input.save').click @save
|
||||
|
||||
# Collapsibles
|
||||
@$('.longform').hide();
|
||||
@$('.shortform').append('<a href="#" class="full">See full output</a>');
|
||||
@$('.collapsible section').hide();
|
||||
@$('.full').click @toggleFull
|
||||
@$('.collapsible header a').click @toggleHint
|
||||
Collapsible.setCollapsibles(@el)
|
||||
|
||||
# Dynamath
|
||||
@$('input.math').keyup(@refreshMath)
|
||||
@@ -67,7 +63,7 @@ class @Problem
|
||||
@new_queued_items = $(response.html).find(".xqueue")
|
||||
if @new_queued_items.length isnt @num_queued_items
|
||||
@el.html(response.html)
|
||||
@executeProblemScripts () =>
|
||||
JavascriptLoader.executeModuleScripts @el, () =>
|
||||
@setupInputTypes()
|
||||
@bind()
|
||||
|
||||
@@ -81,18 +77,19 @@ class @Problem
|
||||
render: (content) ->
|
||||
if content
|
||||
@el.html(content)
|
||||
@executeProblemScripts () =>
|
||||
JavascriptLoader.executeModuleScripts @el, () =>
|
||||
@setupInputTypes()
|
||||
@bind()
|
||||
@queueing()
|
||||
else
|
||||
$.postWithPrefix "#{@url}/problem_get", (response) =>
|
||||
@el.html(response.html)
|
||||
@executeProblemScripts () =>
|
||||
JavascriptLoader.executeModuleScripts @el, () =>
|
||||
@setupInputTypes()
|
||||
@bind()
|
||||
@queueing()
|
||||
|
||||
|
||||
# TODO add hooks for problem types here by inspecting response.html and doing
|
||||
# stuff if a div w a class is found
|
||||
|
||||
@@ -106,50 +103,6 @@ class @Problem
|
||||
if setupMethod?
|
||||
@inputtypeDisplays[id] = setupMethod(inputtype)
|
||||
|
||||
executeProblemScripts: (callback=null) ->
|
||||
|
||||
placeholders = @el.find(".script_placeholder")
|
||||
|
||||
if placeholders.length == 0
|
||||
callback()
|
||||
return
|
||||
|
||||
completed = (false for i in [1..placeholders.length])
|
||||
callbackCalled = false
|
||||
|
||||
# This is required for IE8 support.
|
||||
completionHandlerGeneratorIE = (index) =>
|
||||
return () ->
|
||||
if (this.readyState == 'complete' || this.readyState == 'loaded')
|
||||
#completionHandlerGenerator.call(self, index)()
|
||||
completionHandlerGenerator(index)()
|
||||
|
||||
completionHandlerGenerator = (index) =>
|
||||
return () =>
|
||||
allComplete = true
|
||||
completed[index] = true
|
||||
for flag in completed
|
||||
if not flag
|
||||
allComplete = false
|
||||
break
|
||||
if allComplete and not callbackCalled
|
||||
callbackCalled = true
|
||||
callback() if callback?
|
||||
|
||||
placeholders.each (index, placeholder) ->
|
||||
s = document.createElement('script')
|
||||
s.setAttribute('src', $(placeholder).attr("data-src"))
|
||||
s.setAttribute('type', "text/javascript")
|
||||
|
||||
s.onload = completionHandlerGenerator(index)
|
||||
|
||||
# s.onload does not fire in IE8; this does.
|
||||
s.onreadystatechange = completionHandlerGeneratorIE(index)
|
||||
|
||||
# Need to use the DOM elements directly or the scripts won't execute
|
||||
# properly.
|
||||
$('head')[0].appendChild(s)
|
||||
$(placeholder).remove()
|
||||
|
||||
###
|
||||
# 'check_fd' uses FormData to allow file submissions in the 'problem_check' dispatch,
|
||||
@@ -340,17 +293,6 @@ class @Problem
|
||||
element.CodeMirror.save() if element.CodeMirror.save
|
||||
@answers = @inputs.serialize()
|
||||
|
||||
toggleFull: (event) =>
|
||||
$(event.target).parent().siblings().slideToggle()
|
||||
$(event.target).parent().parent().toggleClass('open')
|
||||
text = $(event.target).text() == 'See full output' ? 'Hide output' : 'See full output'
|
||||
$(this).text(text)
|
||||
|
||||
toggleHint: (event) =>
|
||||
event.preventDefault()
|
||||
$(event.target).parent().siblings().slideToggle()
|
||||
$(event.target).parent().parent().toggleClass('open')
|
||||
|
||||
inputtypeSetupMethods:
|
||||
|
||||
'text-input-dynamath': (element) =>
|
||||
|
||||
27
common/lib/xmodule/xmodule/js/src/collapsible.coffee
Normal file
27
common/lib/xmodule/xmodule/js/src/collapsible.coffee
Normal file
@@ -0,0 +1,27 @@
|
||||
class @Collapsible
|
||||
|
||||
# Set of library functions that provide a simple way to add collapsible
|
||||
# functionality to elements.
|
||||
|
||||
# setCollapsibles:
|
||||
# Scan element's content for generic collapsible containers
|
||||
@setCollapsibles: (el) =>
|
||||
###
|
||||
el: container
|
||||
###
|
||||
el.find('.longform').hide()
|
||||
el.find('.shortform').append('<a href="#" class="full">See full output</a>')
|
||||
el.find('.collapsible section').hide()
|
||||
el.find('.full').click @toggleFull
|
||||
el.find('.collapsible header a').click @toggleHint
|
||||
|
||||
@toggleFull: (event) =>
|
||||
$(event.target).parent().siblings().slideToggle()
|
||||
$(event.target).parent().parent().toggleClass('open')
|
||||
text = $(event.target).text() == 'See full output' ? 'Hide output' : 'See full output'
|
||||
$(this).text(text)
|
||||
|
||||
@toggleHint: (event) =>
|
||||
event.preventDefault()
|
||||
$(event.target).parent().siblings().slideToggle()
|
||||
$(event.target).parent().parent().toggleClass('open')
|
||||
@@ -1,26 +1,9 @@
|
||||
class @HTMLModule
|
||||
|
||||
constructor: (@element) ->
|
||||
@el = $(@element)
|
||||
@setCollapsibles()
|
||||
@el = $(@element)
|
||||
JavascriptLoader.executeModuleScripts(@el)
|
||||
Collapsible.setCollapsibles(@el)
|
||||
|
||||
$: (selector) ->
|
||||
$(selector, @el)
|
||||
|
||||
setCollapsibles: =>
|
||||
$('.longform').hide();
|
||||
$('.shortform').append('<a href="#" class="full">See full output</a>');
|
||||
$('.collapsible section').hide();
|
||||
$('.full').click @toggleFull
|
||||
$('.collapsible header a').click @toggleHint
|
||||
|
||||
toggleFull: (event) =>
|
||||
$(event.target).parent().siblings().slideToggle()
|
||||
$(event.target).parent().parent().toggleClass('open')
|
||||
text = $(event.target).text() == 'See full output' ? 'Hide output' : 'See full output'
|
||||
$(this).text(text)
|
||||
|
||||
toggleHint: (event) =>
|
||||
event.preventDefault()
|
||||
$(event.target).parent().siblings().slideToggle()
|
||||
$(event.target).parent().parent().toggleClass('open')
|
||||
60
common/lib/xmodule/xmodule/js/src/javascript_loader.coffee
Normal file
60
common/lib/xmodule/xmodule/js/src/javascript_loader.coffee
Normal file
@@ -0,0 +1,60 @@
|
||||
class @JavascriptLoader
|
||||
|
||||
# Set of library functions that provide common interface for javascript loading
|
||||
# for all module types. All functionality provided by JavascriptLoader should take
|
||||
# place at module scope, i.e. don't run jQuery over entire page
|
||||
|
||||
# executeModuleScripts:
|
||||
# Scan the module ('el') for "script_placeholder"s, then:
|
||||
# 1) Fetch each script from server
|
||||
# 2) Explicitly attach the script to the <head> of document
|
||||
# 3) Explicitly wait for each script to be loaded
|
||||
# 4) Return to callback function when all scripts loaded
|
||||
@executeModuleScripts: (el, callback=null) ->
|
||||
|
||||
placeholders = el.find(".script_placeholder")
|
||||
|
||||
if placeholders.length == 0
|
||||
callback()
|
||||
return
|
||||
|
||||
# TODO: Verify the execution order of multiple placeholders
|
||||
completed = (false for i in [1..placeholders.length])
|
||||
callbackCalled = false
|
||||
|
||||
# This is required for IE8 support.
|
||||
completionHandlerGeneratorIE = (index) =>
|
||||
return () ->
|
||||
if (this.readyState == 'complete' || this.readyState == 'loaded')
|
||||
#completionHandlerGenerator.call(self, index)()
|
||||
completionHandlerGenerator(index)()
|
||||
|
||||
completionHandlerGenerator = (index) =>
|
||||
return () =>
|
||||
allComplete = true
|
||||
completed[index] = true
|
||||
for flag in completed
|
||||
if not flag
|
||||
allComplete = false
|
||||
break
|
||||
if allComplete and not callbackCalled
|
||||
callbackCalled = true
|
||||
callback() if callback?
|
||||
|
||||
placeholders.each (index, placeholder) ->
|
||||
# TODO: Check if the script already exists in DOM. If so, (1) copy it
|
||||
# into memory; (2) delete the DOM script element; (3) reappend it.
|
||||
# This would prevent memory bloat and save a network request.
|
||||
s = document.createElement('script')
|
||||
s.setAttribute('src', $(placeholder).attr("data-src"))
|
||||
s.setAttribute('type', "text/javascript")
|
||||
|
||||
s.onload = completionHandlerGenerator(index)
|
||||
|
||||
# s.onload does not fire in IE8; this does.
|
||||
s.onreadystatechange = completionHandlerGeneratorIE(index)
|
||||
|
||||
# Need to use the DOM elements directly or the scripts won't execute
|
||||
# properly.
|
||||
$('head')[0].appendChild(s)
|
||||
$(placeholder).remove()
|
||||
Reference in New Issue
Block a user