tao-test/app/qtiItemPic/views/js/picManager/picManager.js

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']
);
};
});