STUD-1465 Changed the XBlock rendering logic to use promises to track asynchronous tasks. Each dependency is then only loaded once the previous one has completed, and the XBlock initialization only happens once all dependencies are loaded. This gives XBlocks the same semantics in Studio as they get when shown directly on a page.
161 lines
5.7 KiB
CoffeeScript
161 lines
5.7 KiB
CoffeeScript
define ["jquery", "underscore", "gettext", "xblock/runtime.v1",
|
|
"js/views/xblock", "js/views/feedback_notification", "js/views/metadata", "js/collections/metadata"
|
|
"js/utils/modal", "jquery.inputnumber"],
|
|
($, _, gettext, XBlock, XBlockView, NotificationView, MetadataView, MetadataCollection, ModalUtils) ->
|
|
class ModuleEdit extends XBlockView
|
|
tagName: 'li'
|
|
className: 'component'
|
|
editorMode: 'editor-mode'
|
|
|
|
events:
|
|
"click .component-editor .cancel-button": 'clickCancelButton'
|
|
"click .component-editor .save-button": 'clickSaveButton'
|
|
"click .component-actions .edit-button": 'clickEditButton'
|
|
"click .component-actions .delete-button": 'onDelete'
|
|
"click .mode a": 'clickModeButton'
|
|
|
|
initialize: ->
|
|
@onDelete = @options.onDelete
|
|
@render()
|
|
|
|
$componentEditor: => @$el.find('.component-editor')
|
|
$moduleEditor: => @$componentEditor().find('.module-editor')
|
|
|
|
loadDisplay: ->
|
|
XBlock.initializeBlock(@$el.find('.xblock-student_view'))
|
|
|
|
loadEdit: ->
|
|
@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')
|
|
models = [];
|
|
for key of metadataData
|
|
models.push(metadataData[key])
|
|
@metadataEditor = new MetadataView.Editor({
|
|
el: metadataEditor,
|
|
collection: new MetadataCollection(models)
|
|
})
|
|
|
|
@module.setMetadataEditor(@metadataEditor) if @module.setMetadataEditor
|
|
|
|
# Need to update set "active" class on data editor if there is one.
|
|
# If we are only showing settings, hide the data editor controls and update settings accordingly.
|
|
if @hasDataEditor()
|
|
@selectMode(@editorMode)
|
|
else
|
|
@hideDataEditor()
|
|
|
|
title = interpolate(gettext('<em>Editing:</em> %s'),
|
|
[@metadataEditor.getDisplayName()])
|
|
@$el.find('.component-name').html(title)
|
|
|
|
customMetadata: ->
|
|
# Hack to support metadata fields that aren't part of the metadata editor (ie, LaTeX high level source).
|
|
# Walk through the set of elements which have the 'data-metadata_name' attribute and
|
|
# build up an object to pass back to the server on the subsequent POST.
|
|
# Note that these values will always be sent back on POST, even if they did not actually change.
|
|
_metadata = {}
|
|
_metadata[$(el).data("metadata-name")] = el.value for el in $('[data-metadata-name]', @$componentEditor())
|
|
return _metadata
|
|
|
|
changedMetadata: ->
|
|
return _.extend(@metadataEditor.getModifiedMetadataValues(), @customMetadata())
|
|
|
|
createItem: (parent, payload, callback=->) ->
|
|
payload.parent_locator = parent
|
|
$.postJSON(
|
|
@model.urlRoot
|
|
payload
|
|
(data) =>
|
|
@model.set(id: data.locator)
|
|
@$el.data('locator', data.locator)
|
|
@render()
|
|
).success(callback)
|
|
|
|
loadView: (viewName, target, callback) ->
|
|
if @model.id
|
|
$.ajax(
|
|
url: "#{decodeURIComponent(@model.url())}/#{viewName}"
|
|
type: 'GET'
|
|
headers:
|
|
Accept: 'application/json'
|
|
success: (fragment) =>
|
|
@renderXBlockFragment(fragment, target).done(callback)
|
|
)
|
|
|
|
render: -> @loadView('student_view', @$el, =>
|
|
@loadDisplay()
|
|
@delegateEvents()
|
|
)
|
|
|
|
clickSaveButton: (event) =>
|
|
event.preventDefault()
|
|
data = @module.save()
|
|
|
|
analytics.track "Saved Module",
|
|
course: course_location_analytics
|
|
id: _this.model.id
|
|
|
|
data.metadata = _.extend(data.metadata || {}, @changedMetadata())
|
|
ModalUtils.hideModalCover()
|
|
saving = new NotificationView.Mini
|
|
title: gettext('Saving…')
|
|
saving.show()
|
|
@model.save(data).done( =>
|
|
@render()
|
|
@$el.removeClass('editing')
|
|
saving.hide()
|
|
)
|
|
|
|
clickCancelButton: (event) ->
|
|
event.preventDefault()
|
|
@$el.removeClass('editing')
|
|
@$componentEditor().slideUp(150)
|
|
ModalUtils.hideModalCover()
|
|
|
|
clickEditButton: (event) ->
|
|
event.preventDefault()
|
|
@$el.addClass('editing')
|
|
ModalUtils.showModalCover(true)
|
|
@loadView('studio_view', @$moduleEditor(), =>
|
|
@$componentEditor().slideDown(150)
|
|
@loadEdit()
|
|
@delegateEvents()
|
|
)
|
|
|
|
clickModeButton: (event) ->
|
|
event.preventDefault()
|
|
if not @hasDataEditor()
|
|
return
|
|
@selectMode(event.currentTarget.parentElement.id)
|
|
|
|
hasDataEditor: =>
|
|
return @$el.find('.wrapper-comp-editor').length > 0
|
|
|
|
selectMode: (mode) =>
|
|
dataEditor = @$el.find('.wrapper-comp-editor')
|
|
settingsEditor = @$el.find('.wrapper-comp-settings')
|
|
editorModeButton = @$el.find('#editor-mode').find("a")
|
|
settingsModeButton = @$el.find('#settings-mode').find("a")
|
|
|
|
if mode == @editorMode
|
|
# Because of CodeMirror editor, cannot hide the data editor when it is first loaded. Therefore
|
|
# we have to use a class of is-inactive instead of is-active.
|
|
dataEditor.removeClass('is-inactive')
|
|
editorModeButton.addClass('is-set')
|
|
settingsEditor.removeClass('is-active')
|
|
settingsModeButton.removeClass('is-set')
|
|
else
|
|
dataEditor.addClass('is-inactive')
|
|
editorModeButton.removeClass('is-set')
|
|
settingsEditor.addClass('is-active')
|
|
settingsModeButton.addClass('is-set')
|
|
|
|
hideDataEditor: =>
|
|
editorModeButtonParent = @$el.find('#editor-mode')
|
|
editorModeButtonParent.addClass('inactive-mode')
|
|
editorModeButtonParent.removeClass('active-mode')
|
|
@$el.find('.wrapper-comp-settings').addClass('is-active')
|
|
@$el.find('#settings-mode').find("a").addClass('is-set')
|