This commit implements STUD-1490, allowing creation of components on the container page. It also enables the delete and duplicate buttons now that new content can be created that would benefit. Note that it also creates shared functionality for adding components, and refactors the unit page to use it too.
171 lines
6.9 KiB
JavaScript
171 lines
6.9 KiB
JavaScript
define(["jquery", "underscore", "js/views/baseview", "xblock/runtime.v1"],
|
|
function ($, _, BaseView, XBlock) {
|
|
|
|
var XBlockView = BaseView.extend({
|
|
// takes XBlockInfo as a model
|
|
|
|
initialize: function() {
|
|
BaseView.prototype.initialize.call(this);
|
|
this.view = this.options.view;
|
|
},
|
|
|
|
render: function(options) {
|
|
var self = this,
|
|
view = this.view,
|
|
xblockInfo = this.model,
|
|
xblockUrl = xblockInfo.url();
|
|
return $.ajax({
|
|
url: decodeURIComponent(xblockUrl) + "/" + view,
|
|
type: 'GET',
|
|
headers: { Accept: 'application/json' },
|
|
success: function(fragment) {
|
|
self.handleXBlockFragment(fragment, options);
|
|
}
|
|
});
|
|
},
|
|
|
|
handleXBlockFragment: function(fragment, options) {
|
|
var self = this,
|
|
wrapper = this.$el,
|
|
xblockElement,
|
|
success = options ? options.success : null,
|
|
xblock,
|
|
fragmentsRendered;
|
|
|
|
fragmentsRendered = this.renderXBlockFragment(fragment, wrapper);
|
|
fragmentsRendered.done(function() {
|
|
xblockElement = self.$('.xblock').first();
|
|
xblock = XBlock.initializeBlock(xblockElement);
|
|
self.xblock = xblock;
|
|
self.xblockReady(xblock);
|
|
if (success) {
|
|
success(xblock);
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* This method is called upon successful rendering of an xblock.
|
|
*/
|
|
xblockReady: function(xblock) {
|
|
// Do nothing
|
|
},
|
|
|
|
/**
|
|
* Returns true if the specified xblock has children.
|
|
*/
|
|
hasChildXBlocks: function() {
|
|
return this.$('.wrapper-xblock').length > 0;
|
|
},
|
|
|
|
/**
|
|
* Renders an xblock fragment into the specified element. The fragment has two attributes:
|
|
* html: the HTML to be rendered
|
|
* resources: any JavaScript or CSS resources that the HTML depends upon
|
|
* Note that the XBlock is rendered asynchronously, and so a promise is returned that
|
|
* represents this process.
|
|
* @param fragment The fragment returned from the xblock_handler
|
|
* @param element The element into which to render the fragment (defaults to this.$el)
|
|
* @returns {*} A promise representing the rendering process
|
|
*/
|
|
renderXBlockFragment: function(fragment, element) {
|
|
var html = fragment.html,
|
|
resources = fragment.resources;
|
|
if (!element) {
|
|
element = this.$el;
|
|
}
|
|
|
|
// Render the HTML first as the scripts might depend upon it, and then
|
|
// asynchronously add the resources to the page.
|
|
this.updateHtml(element, html);
|
|
return this.addXBlockFragmentResources(resources);
|
|
},
|
|
|
|
/**
|
|
* Updates an element to have the specified HTML. The default method sets the HTML
|
|
* as child content, but this can be overridden.
|
|
* @param element The element to be updated
|
|
* @param html The desired HTML.
|
|
*/
|
|
updateHtml: function(element, html) {
|
|
element.html(html);
|
|
},
|
|
|
|
/**
|
|
* Dynamically loads all of an XBlock's dependent resources. This is an asynchronous
|
|
* process so a promise is returned.
|
|
* @param resources The resources to be rendered
|
|
* @returns {*} A promise representing the rendering process
|
|
*/
|
|
addXBlockFragmentResources: function(resources) {
|
|
var self = this,
|
|
applyResource,
|
|
numResources,
|
|
deferred;
|
|
numResources = resources.length;
|
|
deferred = $.Deferred();
|
|
applyResource = function(index) {
|
|
var hash, resource, head, value, promise;
|
|
if (index >= numResources) {
|
|
deferred.resolve();
|
|
return;
|
|
}
|
|
value = resources[index];
|
|
hash = value[0];
|
|
if (!window.loadedXBlockResources) {
|
|
window.loadedXBlockResources = [];
|
|
}
|
|
if (_.indexOf(window.loadedXBlockResources, hash) < 0) {
|
|
resource = value[1];
|
|
promise = self.loadResource(resource);
|
|
window.loadedXBlockResources.push(hash);
|
|
promise.done(function() {
|
|
applyResource(index + 1);
|
|
}).fail(function() {
|
|
deferred.reject();
|
|
});
|
|
} else {
|
|
applyResource(index + 1);
|
|
}
|
|
};
|
|
applyResource(0);
|
|
return deferred.promise();
|
|
},
|
|
|
|
/**
|
|
* Loads the specified resource into the page.
|
|
* @param resource The resource to be loaded.
|
|
* @returns {*} A promise representing the loading of the resource.
|
|
*/
|
|
loadResource: function(resource) {
|
|
var head = $('head'),
|
|
mimetype = resource.mimetype,
|
|
kind = resource.kind,
|
|
placement = resource.placement,
|
|
data = resource.data;
|
|
if (mimetype === "text/css") {
|
|
if (kind === "text") {
|
|
head.append("<style type='text/css'>" + data + "</style>");
|
|
} else if (kind === "url") {
|
|
head.append("<link rel='stylesheet' href='" + data + "' type='text/css'>");
|
|
}
|
|
} else if (mimetype === "application/javascript") {
|
|
if (kind === "text") {
|
|
head.append("<script>" + data + "</script>");
|
|
} else if (kind === "url") {
|
|
// Return a promise for the script resolution
|
|
return $.getScript(data);
|
|
}
|
|
} else if (mimetype === "text/html") {
|
|
if (placement === "head") {
|
|
head.append(data);
|
|
}
|
|
}
|
|
// Return an already resolved promise for synchronous updates
|
|
return $.Deferred().resolve().promise();
|
|
}
|
|
});
|
|
|
|
return XBlockView;
|
|
}); // end define();
|