Files
edx-platform/cms/static/js/views/modals/edit_xblock.js
Andy Armstrong d451c3e91d Fix Studio to gracefully handle xblock JavaScript errors
TNL-46

I've changed Studio to catch JavaScript errors when rendering xblocks, log the error, but to then continue as normal. This means that the user is still able to interact with the xblock to delete, duplicate etc. This seems reasonable as the xblock is only rendered as a WYSIWYG representation so if it isn't fully interactive that shouldn't be a big problem.
2014-08-21 17:31:19 -04:00

220 lines
8.9 KiB
JavaScript

/**
* The EditXBlockModal is a Backbone view that shows an xblock editor in a modal window.
* It is invoked using the edit method which is passed an existing rendered xblock,
* and upon save an optional refresh function can be invoked to update the display.
*/
define(["jquery", "underscore", "gettext", "js/views/modals/base_modal", "js/views/utils/view_utils",
"js/models/xblock_info", "js/views/xblock_editor"],
function($, _, gettext, BaseModal, ViewUtils, XBlockInfo, XBlockEditorView) {
var EditXBlockModal = BaseModal.extend({
events : {
"click .action-save": "save",
"click .action-modes a": "changeMode"
},
options: $.extend({}, BaseModal.prototype.options, {
modalName: 'edit-xblock',
addSaveButton: true,
viewSpecificClasses: 'modal-editor confirm'
}),
initialize: function() {
BaseModal.prototype.initialize.call(this);
this.events = _.extend({}, BaseModal.prototype.events, this.events);
this.template = this.loadTemplate('edit-xblock-modal');
this.editorModeButtonTemplate = this.loadTemplate('editor-mode-button');
},
/**
* Show an edit modal for the specified xblock
* @param xblockElement The element that contains the xblock to be edited.
* @param rootXBlockInfo An XBlockInfo model that describes the root xblock on the page.
* @param options A standard options object.
*/
edit: function(xblockElement, rootXBlockInfo, options) {
this.xblockElement = xblockElement;
this.xblockInfo = this.findXBlockInfo(xblockElement, rootXBlockInfo);
this.options.modalType = this.xblockInfo.get('category');
this.editOptions = options;
this.render();
this.show();
// Hide the action bar until we know which buttons we want
this.getActionBar().hide();
// Display the xblock after the modal is shown as there are some xblocks
// that depend upon being visible when they initialize, e.g. the problem xmodule.
this.displayXBlock();
},
getContentHtml: function() {
return this.template({
xblockInfo: this.xblockInfo
});
},
displayXBlock: function() {
this.editorView = new XBlockEditorView({
el: this.$('.xblock-editor'),
model: this.xblockInfo
});
this.editorView.render({
success: _.bind(this.onDisplayXBlock, this)
});
},
onDisplayXBlock: function() {
var editorView = this.editorView,
title = this.getTitle();
// Notify the runtime that the modal has been shown
editorView.notifyRuntime('modal-shown', this);
// Update the modal's header
if (editorView.hasCustomTabs()) {
// Hide the modal's header as the custom editor provides its own
this.$('.modal-header').hide();
// Update the custom editor's title
editorView.$('.component-name').text(title);
} else {
this.$('.modal-window-title').text(title);
if (editorView.getDataEditor() && editorView.getMetadataEditor()) {
this.addDefaultModes();
this.selectMode(editorView.mode);
}
}
// If the xblock is not using custom buttons then choose which buttons to show
if (!editorView.hasCustomButtons()) {
// If the xblock does not support save then disable the save button
if (!editorView.xblock.save) {
this.disableSave();
}
this.getActionBar().show();
}
// Resize the modal to fit the window
this.resize();
},
disableSave: function() {
var saveButton = this.getActionButton('save'),
cancelButton = this.getActionButton('cancel');
saveButton.hide();
cancelButton.text(gettext('OK'));
cancelButton.addClass('action-primary');
},
getTitle: function() {
var displayName = this.xblockInfo.get('display_name');
if (!displayName) {
displayName = gettext('Component');
}
return interpolate(gettext("Editing: %(title)s"), { title: displayName }, true);
},
addDefaultModes: function() {
var defaultModes, i, mode;
defaultModes = this.editorView.getDefaultModes();
for (i = 0; i < defaultModes.length; i++) {
mode = defaultModes[i];
this.addModeButton(mode.id, mode.name);
}
},
changeMode: function(event) {
this.removeCheatsheetVisibility();
var parent = $(event.target.parentElement),
mode = parent.data('mode');
event.preventDefault();
this.selectMode(mode);
},
selectMode: function(mode) {
var editorView = this.editorView,
buttonSelector;
editorView.selectMode(mode);
this.$('.editor-modes a').removeClass('is-set');
if (mode) {
buttonSelector = '.' + mode + '-button';
this.$(buttonSelector).addClass('is-set');
}
},
save: function(event) {
var self = this,
editorView = this.editorView,
xblockInfo = this.xblockInfo,
data = editorView.getXModuleData();
event.preventDefault();
if (data) {
ViewUtils.runOperationShowingMessage(gettext('Saving&hellip;'),
function() {
return xblockInfo.save(data);
}).done(function() {
self.onSave();
});
}
},
onSave: function() {
var refresh = this.editOptions.refresh;
this.hide();
if (refresh) {
refresh(this.xblockInfo);
}
},
hide: function() {
BaseModal.prototype.hide.call(this);
// Notify the runtime that the modal has been hidden
this.editorView.notifyRuntime('modal-hidden');
},
findXBlockInfo: function(xblockWrapperElement, defaultXBlockInfo) {
var xblockInfo = defaultXBlockInfo,
xblockElement,
displayName;
if (xblockWrapperElement.length > 0) {
xblockElement = xblockWrapperElement.find('.xblock');
displayName = xblockWrapperElement.find('.xblock-header .header-details .xblock-display-name').text().trim();
// If not found, try looking for the old unit page style rendering.
// Only used now by static pages.
if (!displayName) {
displayName = this.xblockElement.find('.component-header').text().trim();
}
xblockInfo = new XBlockInfo({
id: xblockWrapperElement.data('locator'),
courseKey: xblockWrapperElement.data('course-key'),
category: xblockElement.data('block-type'),
display_name: displayName
});
}
return xblockInfo;
},
addModeButton: function(mode, displayName) {
var buttonPanel = this.$('.editor-modes');
buttonPanel.append(this.editorModeButtonTemplate({
mode: mode,
displayName: displayName
}));
},
removeCheatsheetVisibility: function() {
var cheatsheet = $('article.simple-editor-open-ended-cheatsheet');
if (cheatsheet.length === 0) {
cheatsheet = $('article.simple-editor-cheatsheet');
}
if (cheatsheet.hasClass('shown')) {
cheatsheet.removeClass('shown');
$('.modal-content').removeClass('cheatsheet-is-shown');
}
}
});
return EditXBlockModal;
});