Commit f627c9e9 by arjun810

Merge pull request #791 from MITx/kimth/js-refactor

Kimth/js refactor
parents 0ed735b7 845b921c
......@@ -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) =>
......
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')
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')
\ No newline at end of file
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()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment