@oat-sa/tao-item-runner-qti
Version:
TAO QTI Item Runner modules
189 lines (169 loc) • 6.61 kB
JavaScript
/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2015 (original work) Open Assessment Technologies SA
*
*/
/**
* Portable Info Control Common Renderer
*/
import _ from 'lodash';
import tpl from 'taoQtiItem/qtiCommonRenderer/tpl/infoControl';
import containerHelper from 'taoQtiItem/qtiCommonRenderer/helpers/container';
import PortableElement from 'taoQtiItem/qtiCommonRenderer/helpers/PortableElement';
import qtiInfoControlContext from 'qtiInfoControlContext';
import util from 'taoQtiItem/qtiItem/helper/util';
import icRegistry from 'taoQtiItem/portableElementRegistry/icRegistry';
/**
* Get the PIC instance associated to the infoControl object
* If none exists, create a new one based on the PIC typeIdentifier
*
* @param {Object} infoControl - the js object representing the infoControl
* @returns {Object} PIC instance
*/
var _getPic = function(infoControl) {
var typeIdentifier,
pic = infoControl.data('pic') || undefined;
if (!pic) {
typeIdentifier = infoControl.typeIdentifier;
pic = qtiInfoControlContext.createPciInstance(typeIdentifier);
if (pic) {
//binds the PIC instance to TAO infoControl object and vice versa
infoControl.data('pic', pic);
pic._taoInfoControl = infoControl;
} else {
throw 'no custom infoControl hook found for the type ' + typeIdentifier;
}
}
return pic;
};
/**
* Execute javascript codes to bring the infoControl to life.
* At this point, the html markup must already be ready in the document.
*
* It is done in 5 steps :
* 1. ensure the context is configured correctly
* 2. require all required libs
* 3. create a pic instance based on the infoControl model
* 4. initialize the rendering
* 5. restore full state if applicable
*
* @param {Object} infoControl
* @param {Object} [options]
*/
var render = function(infoControl, options) {
var self = this;
options = options || {};
return new Promise(function(resolve, reject) {
var state = {}; //@todo pass state and response to renderer here:
var id = infoControl.attr('id');
var typeIdentifier = infoControl.typeIdentifier;
var config = infoControl.properties;
var $dom = containerHelper.get(infoControl).children();
var assetManager = self.getAssetManager();
icRegistry
.loadRuntimes()
.then(function() {
var requireEntries = [];
var runtime = icRegistry.getRuntime(typeIdentifier);
if (!runtime) {
return reject('The runtime for the pic cannot be found : ' + typeIdentifier);
}
//load the entrypoint, becomes optional per IMS PCI v1
if (runtime.hook) {
requireEntries.push(runtime.hook.replace(/\.js$/, ''));
}
//load required libraries
_.forEach(runtime.libraries, function(module) {
requireEntries.push(module.replace(/\.js$/, ''));
});
//load stylesheets
_.forEach(runtime.stylesheets, function(stylesheet) {
requireEntries.push('css!' + stylesheet.replace(/\.css$/, ''));
});
//load the entrypoint
window.require(requireEntries, function() {
var pic = _getPic(infoControl);
var picAssetManager = {
resolve: function resolve(url) {
var resolved = assetManager.resolveBy('portableElementLocation', url);
if (resolved === url) {
return assetManager.resolveBy('baseUrl', url);
} else {
return resolved;
}
}
};
if (pic) {
//call pic initialize() to render the pic
pic.initialize(id, $dom[0], config, picAssetManager);
//restore context (state + response)
pic.setSerializedState(state);
return resolve();
}
return reject('Unable to initialize pic : ' + id);
}, reject);
})
.catch(function(error) {
reject('Error loading runtime : ' + id);
});
});
};
/**
* Reverse operation performed by render()
* After this function is executed, only the inital naked markup remains
* Event listeners are removed and the state and the response are reset
*
* @param {Object} infoControl
*/
var destroy = function destroy(infoControl) {
_getPic(infoControl).destroy();
};
/**
* Restore the state of the infoControl from the serializedState.
*
* @param {Object} infoControl - the element instance
* @param {Object} state - the state to set
*/
var setState = function setState(infoControl, state) {
_getPic(infoControl).setSerializedState(state);
};
/**
* Get the current state of the infoControl as a string.
* It enables saving the state for later usage.
*
* @param {Object} infoControl - the element instance
* @returns {Object} the state
*/
var getState = function getState(infoControl) {
return _getPic(infoControl).getSerializedState();
};
export default {
qtiClass: 'infoControl',
template: tpl,
getData: function(infoControl, data) {
//remove ns + fix media file path
var markup = data.markup;
markup = util.removeMarkupNamespaces(markup);
markup = PortableElement.fixMarkupMediaSources(markup, this);
data.markup = markup;
return data;
},
render: render,
getContainer: containerHelper.get,
destroy: destroy,
getState: getState,
setState: setState
};