Allow multiple client-side runtimes on a single page

Make XBlock client-side runtimes proper classes, so that handlerUrl can
be defined in a per-runtime way, and we can have multiple runtimes on a
single page.

[LMS-1630][LMS-1421][LMS-1517]
This commit is contained in:
Calen Pennington
2013-12-13 07:47:21 -05:00
parent 0f8919a6ba
commit 49217ebe8b
19 changed files with 122 additions and 124 deletions

View File

@@ -15,6 +15,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
from lms.lib.xblock.runtime import quote_slashes
log = logging.getLogger(__name__)
@@ -29,26 +30,28 @@ def wrap_fragment(fragment, new_content):
return wrapper_frag
def wrap_xblock(handler_prefix, block, view, frag, context, display_name_only=False): # pylint: disable=unused-argument
def wrap_xblock(runtime_class, block, view, frag, context, display_name_only=False, extra_data=None): # pylint: disable=unused-argument
"""
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.
:param handler_prefix: A function that takes a block and returns the url prefix for
the javascript handler_url. This prefix should be able to have {handler_name}/{suffix}?{query}
appended to it to return a valid handler_url
:param runtime_class: The name of the javascript runtime class to use to load this block
: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`
:param extra_data: A dictionary with extra data values to be set on the wrapper
"""
if extra_data is None:
extra_data = {}
# If any mixins have been applied, then use the unmixed class
class_name = getattr(block, 'unmixed_class', block.__class__).__name__
data = {}
data.update(extra_data)
css_classes = ['xblock', 'xblock-' + view]
if isinstance(block, (XModule, XModuleDescriptor)):
@@ -65,9 +68,10 @@ def wrap_xblock(handler_prefix, block, view, frag, context, display_name_only=Fa
if frag.js_init_fn:
data['init'] = frag.js_init_fn
data['runtime-class'] = runtime_class
data['runtime-version'] = frag.js_init_version
data['handler-prefix'] = handler_prefix(block)
data['block-type'] = block.scope_ids.block_type
data['usage-id'] = quote_slashes(str(block.scope_ids.usage_id))
template_context = {
'content': block.display_name if display_name_only else frag.content,

View File

@@ -1,18 +1,19 @@
@XBlock =
runtime: {}
Runtime: {}
initializeBlock: (element) ->
$element = $(element)
children = @initializeBlocks($element)
runtime = $element.data("runtime-class")
version = $element.data("runtime-version")
initFnName = $element.data("init")
if version? and initFnName?
runtime = @runtime["v#{version}"](element, children)
if runtime? and version? and initFnName?
runtime = new window[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")
console.log("Block #{elementTag} is missing data-runtime, data-runtime-version or data-init, and can't be initialized")
block = {}
block.element = element

View File

@@ -1,24 +1,5 @@
@XBlock.runtime.v1 = (element, children) ->
childMap = {}
$.each children, (idx, child) ->
childMap[child.name] = child
return {
# Generate the handler url for the specified handler.
#
# element is the html element containing the xblock requesting the url
# handlerName is the name of the handler
# suffix is the optional url suffix to include in the handler url
# query is an optional query-string (note, this should not include a preceding ? or &)
handlerUrl: (element, handlerName, suffix, query) ->
handlerPrefix = $(element).data("handler-prefix")
suffix = if suffix? then "/#{suffix}" else ''
query = if query? then "?#{query}" else ''
"#{handlerPrefix}/#{handlerName}#{suffix}#{query}"
# A list of xblock children of this element
children: children
# A map of name -> child for the xblock children of this element
childMap: childMap
}
class XBlock.Runtime.v1
constructor: (@element, @children) ->
@childMap = {}
$.each @children, (idx, child) =>
@childMap[child.name] = child