345 lines
12 KiB
JavaScript
345 lines
12 KiB
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) 2016 (original work) Open Assessment Technologies SA;
|
|
*
|
|
*/
|
|
define([
|
|
'jquery',
|
|
'lodash',
|
|
'core/promise',
|
|
'core/logger',
|
|
'ui/tooltipster',
|
|
'taoQtiItem/portableElementRegistry/icRegistry',
|
|
'taoQtiItem/qtiCreator/helper/creatorRenderer',
|
|
'taoQtiItem/qtiCreator/model/helper/container',
|
|
'taoQtiItem/qtiCreator/editor/gridEditor/content',
|
|
'tpl!qtiItemPic/picManager/tpl/manager',
|
|
'css!qtiItemPicCss/pic-manager'
|
|
], function (
|
|
$,
|
|
_,
|
|
Promise,
|
|
loggerFactory,
|
|
tooltip,
|
|
icRegistry,
|
|
creatorRenderer,
|
|
containerHelper,
|
|
contentHelper,
|
|
managerTpl
|
|
) {
|
|
'use strict';
|
|
|
|
var _studentToolTag = 'student-tool';
|
|
var _studentToolbarId = 'studentToolbar';
|
|
var logger = loggerFactory('picManager');
|
|
|
|
/**
|
|
* Toggle the disabled state of checkboxes
|
|
*
|
|
* @param $checkBoxes
|
|
* @param state
|
|
*/
|
|
function toggleCheckboxState($checkBoxes, state) {
|
|
$checkBoxes.each(function () {
|
|
// @todo this is tmp code that needs to go as soon all tools are available
|
|
// see also further down above check event for another portion of the code
|
|
if (this.className.indexOf('not-available') > -1) {
|
|
return true;
|
|
}
|
|
// end tmp code
|
|
|
|
this.disabled = state;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create a dummy place holder
|
|
*
|
|
* @param typeIdentifier
|
|
* @returns {*|HTMLElement}
|
|
*/
|
|
function getNewInfoControlNode(typeIdentifier) {
|
|
return $('<span/>')
|
|
.addClass('widget-box sts-tmp-element sts-placeholder-' + typeIdentifier)
|
|
.attr('data-new', true)
|
|
.attr('data-qti-class', 'infoControl.' + typeIdentifier);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param $container
|
|
* @param $itemPanel
|
|
* @param itemUri
|
|
*/
|
|
function initStudentToolManager($container, $itemPanel, item) {
|
|
var $placeholder;
|
|
//get list of all info controls available
|
|
icRegistry.loadCreators().then(function(allInfoControls) {
|
|
//get item body container
|
|
//editor panel..
|
|
var $itemBody = $itemPanel.find('.qti-itemBody');
|
|
|
|
//prepare data for the tpl:
|
|
var tools = {},
|
|
toolArray,
|
|
alreadySet = _.pluck(item.getElements('infoControl'), 'typeIdentifier'),
|
|
allInfoControlsSize,
|
|
$managerPanel,
|
|
i = 0;
|
|
|
|
_.each(allInfoControls, function(creator) {
|
|
var name = creator.getTypeIdentifier(),
|
|
manifest = icRegistry.get(name),
|
|
controlExists = _.indexOf(alreadySet, name) > -1,
|
|
defaultProperties = creator.getDefaultProperties(),
|
|
position = defaultProperties.position || 100 + i;
|
|
|
|
if (manifest.disabled) {
|
|
return;
|
|
}
|
|
|
|
if (manifest.tags && manifest.tags[0] === _studentToolTag) {
|
|
tools[name] = {
|
|
label: manifest.label,
|
|
description: manifest.description,
|
|
checked: controlExists,
|
|
position: position,
|
|
name: name
|
|
};
|
|
}
|
|
|
|
// store the name also in the value for convenience
|
|
allInfoControls[name].name = name;
|
|
|
|
// determine where to position a tool on the toolbar
|
|
allInfoControls[name].position = position;
|
|
|
|
// on load we assume that everything that already exists is also checked
|
|
// this counts also for the toolbar which has no actual checkbox
|
|
allInfoControls[name].checked = controlExists;
|
|
|
|
// have the resources already been copied,
|
|
// i.e. is the tool currently installed
|
|
allInfoControls[name].installed = controlExists;
|
|
|
|
// must be false, even if tool has been installed
|
|
// before, resources might have changed
|
|
allInfoControls[name].copied = false;
|
|
|
|
i++;
|
|
});
|
|
|
|
toolArray = _.sortBy(tools, 'position');
|
|
|
|
allInfoControlsSize = _.size(allInfoControls);
|
|
|
|
$managerPanel = managerTpl({
|
|
tools: toolArray
|
|
});
|
|
$container.append($managerPanel);
|
|
|
|
//init tooltips
|
|
tooltip.lookup($container);
|
|
|
|
//init event listeners:
|
|
var $checkBoxes = $('[data-role="pic-manager"]').find('input:checkbox');
|
|
|
|
$checkBoxes.on('change.picmanager', function onChangePicManager(e) {
|
|
e.stopPropagation();
|
|
|
|
// install toolbar if required
|
|
if (this.checked && !allInfoControls[_studentToolbarId].installed) {
|
|
allInfoControls[_studentToolbarId].checked = true;
|
|
}
|
|
|
|
allInfoControls[this.name].checked = this.checked;
|
|
|
|
toggleCheckboxState($checkBoxes, true);
|
|
|
|
processAllControls();
|
|
});
|
|
|
|
/**
|
|
* Creates array of PIC controllers names in order which the controller will be processed
|
|
*
|
|
* @returns {String[]}
|
|
*/
|
|
function getOrderedPICNames() {
|
|
return _.keys(allInfoControls).reduce(function(ordered, infoContorolName) {
|
|
if (infoContorolName === _studentToolbarId) {
|
|
ordered.unshift(infoContorolName);
|
|
} else {
|
|
ordered.push(infoContorolName);
|
|
}
|
|
|
|
return ordered;
|
|
}, []);
|
|
}
|
|
/**
|
|
* Iterate over all controls and launch the actual installer/un-installer
|
|
*/
|
|
function processAllControls() {
|
|
var cnt = 0;
|
|
|
|
_.forEach(getOrderedPICNames(), function(controlName) {
|
|
const control = allInfoControls[controlName];
|
|
// is there any action required at all?
|
|
// if not and if there are still items
|
|
// left proceed to the next one
|
|
if (control.checked === control.installed) {
|
|
cnt++;
|
|
return true;
|
|
}
|
|
|
|
processControl(control);
|
|
|
|
// break here and wait for the next call
|
|
// to be executed from processControl()
|
|
return false;
|
|
});
|
|
|
|
if (cnt === allInfoControlsSize) {
|
|
toggleCheckboxState($checkBoxes, false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render tool|toolbar
|
|
*
|
|
* @param elt
|
|
*/
|
|
function renderControl(elt) {
|
|
var stsClassName = elt.typeIdentifier === _studentToolbarId ? 'sts-scope' : 'sts-scope sts-tmp-element';
|
|
|
|
//add the student tool css scope
|
|
elt.attr('class', stsClassName);
|
|
|
|
elt.prop('position', allInfoControls[elt.typeIdentifier].position);
|
|
elt.prop('toolbarId', 'sts-' + _studentToolbarId);
|
|
|
|
//render it
|
|
elt.setRenderer(creatorRenderer.get());
|
|
|
|
elt.render($placeholder);
|
|
|
|
$placeholder = null;
|
|
|
|
Promise.all(elt.postRender({}))
|
|
.then(function () {
|
|
var widget = elt.data('widget');
|
|
|
|
allInfoControls[elt.typeIdentifier].installed = true;
|
|
|
|
//inform height modification
|
|
widget.$container.trigger('contentChange.gridEdit');
|
|
widget.$container.trigger('resize.gridEdit');
|
|
|
|
if (elt.typeIdentifier !== _studentToolbarId) {
|
|
toggleCheckboxState($checkBoxes, false);
|
|
}
|
|
|
|
// continue with the next element of allInfoControls
|
|
processAllControls();
|
|
})
|
|
.catch(function (err) {
|
|
logger.error(err);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Remove a student tool
|
|
*
|
|
* @param control
|
|
*/
|
|
function removeControl(control) {
|
|
var infoControls = item.getElements('infoControl'),
|
|
remove = function (_control) {
|
|
var studentTool = _.find(infoControls, {
|
|
typeIdentifier: _control.name
|
|
});
|
|
//call ic hook destroy() method
|
|
studentTool.data('pic').destroy();
|
|
|
|
//remove the widget from dom
|
|
$('#sts-' + _control.name).remove();
|
|
$itemBody.find('.widget-box[data-serial=' + studentTool.serial + ']').remove();
|
|
|
|
//remove form model
|
|
item.removeElement(studentTool.serial);
|
|
|
|
allInfoControls[_control.name].checked = false;
|
|
allInfoControls[_control.name].installed = false;
|
|
};
|
|
|
|
//remove it
|
|
remove(control);
|
|
// in case there only two elements left, one of them
|
|
// must be the toolbar and needs to be removed too
|
|
if (_.size(infoControls) > 2) {
|
|
processAllControls();
|
|
return;
|
|
}
|
|
|
|
// reset to checked:true on click of any tool
|
|
remove(allInfoControls[_studentToolbarId]);
|
|
|
|
toggleCheckboxState($checkBoxes, false);
|
|
processAllControls();
|
|
}
|
|
|
|
/**
|
|
* install|un-install a single control
|
|
*
|
|
* @param control
|
|
* @returns {boolean}
|
|
*/
|
|
function processControl(control) {
|
|
// what needs to be done to the control? install|uninstall
|
|
if (!control.checked) {
|
|
removeControl(control);
|
|
return true;
|
|
}
|
|
|
|
//create an info control (student tool|toolbar) and add it to the them
|
|
$placeholder = getNewInfoControlNode(control.name);
|
|
$itemBody.prepend($placeholder);
|
|
|
|
// install procedure
|
|
containerHelper.createElements(item.getBody(), contentHelper.getContent($itemBody), function (newElts) {
|
|
// although newElts appears to be a collection it holds
|
|
// only _one_ element due to the callback mechanism
|
|
// between processControl() and processAllControls()
|
|
_.each(newElts, function (elt) {
|
|
// update look-up list
|
|
allInfoControls[control.name].copied = true;
|
|
renderControl(elt);
|
|
});
|
|
});
|
|
}
|
|
}, true);
|
|
}
|
|
|
|
return function picManager($container, $itemPanel, itemUri) {
|
|
//load infoControl model first into the creator renderer
|
|
creatorRenderer.get().load(
|
|
function () {
|
|
initStudentToolManager($container, $itemPanel, itemUri);
|
|
},
|
|
['infoControl']
|
|
);
|
|
};
|
|
});
|