tao-test/app/tao/views/js/form/post-render-props.js

439 lines
17 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) 2018 (original work) Open Assessment Technologies SA ;
*/
define([
'jquery',
'i18n',
'ui/feedback',
'./depends-on-property',
'./secondary-property'
], function ($, __, feedback, dependsOn, secondaryProps) {
'use strict';
function _createCopyToClipboardHandler($field) {
var
successFeedback = $field.data('copy-success-feedback')
|| __('Resource Identifier has been copied to the clipboard'),
failureFeedback = $field.data('copy-failure-feedback')
|| __('Resource Identifier could not be copied to the clipboard');
return function () {
var success;
try {
$field.select();
success = document.execCommand('copy');
$field.blur();
if (success) {
feedback().success(successFeedback);
} else {
feedback().error(failureFeedback);
}
} catch (err) {
feedback().error(__('Your browser does not support copying to the clipboard'));
}
}
}
function _cloneField($field) {
return $field.clone()
// To make MS browsers happy, value needs to be removed and re-added
.val('')
.attr({readonly: true, type: 'text'});
}
/**
* Add a field with URI of an item etc and a button to copy it to the clipboard
* @param $container
* @private
*/
function _initializeCopyToClipboard($container) {
// Early return in case:
// 1. isInstanceForm that will not work with jquery|querySelector
// 2. The field has already been added
if (!document.getElementById('tao.forms.instance') || $('.uri-container').length) {
return;
}
$container.find('#id, .copy-to-clipboard').each(function () {
var $field = $(this),
$fieldCopy = _cloneField($field),
$button = $('<span>', {class: 'icon-clipboard clipboard-command', title: __('Copy to clipboard')}),
$label = $('<span>', {class: 'form_desc', text: __('Resource Identifier')}),
$fieldBox = $('<span>', {class: 'uri-container'}),
value = $field.val();
if ($field.attr('id') === 'id') {
$field.remove();
$field = $fieldCopy;
$fieldBox.append([$field, $button]);
$container.find('div')
.first()
.after($('<div>').append([$label, $fieldBox]));
$fieldBox.height($field.outerHeight());
} else {
$fieldBox.height($field.outerHeight());
$field.wrap($fieldBox).parent().append($button);
}
$button.on('click', _createCopyToClipboardHandler($field));
$field.addClass('final').val(value);
});
}
/**
* Toggle availability of mode switch (advanced/simple)
*
* @param newMode
* @private
*/
function _toggleModeBtn(newMode) {
var $modeToggle = $('.property-mode');
if (newMode === 'disabled') {
$modeToggle.addClass('disabled');
} else {
$modeToggle.removeClass('disabled');
}
}
/**
* Reposition the radio buttons or checkboxes of a property and make them look nice.
*
* @param $container the container in which to search and upgrade buttons
* @param type string the type of input we want to upgrade 'checkbox' or 'radio' by default we use radio
* @private
*/
function _upgradeButtons($container, type) {
//if the type is not radio or checkbox we put by default radio
if (type !== 'radio' && type !== 'checkbox') {
type = 'radio';
}
var selector = '.form_checklst';
var notSelector = '';
if (type === 'radio') {
selector = '.form_radlst';
notSelector = '.form_checklst, ';
}
$container.find(selector).not(notSelector + '.property-' + type + '-list').each(function () {
var $radioList = $(this);
$radioList.addClass('property-' + type + '-list');
$radioList.parent().addClass('property-' + type + '-list-box');
$radioList.each(function () {
var $block = $(this),
$inputs = $block.find('input');
if ($inputs.length <= 2) {
$block.find('br').remove();
}
$inputs.each(function () {
var $input = $(this),
$label = $block.find('label[for="' + this.id + '"]'),
$icon = $('<span>', {'class': 'icon-' + type});
$label.prepend($icon);
$label.prepend($input);
});
});
});
}
/**
* Get reference to property container. If it doesn't' exist create one and add it to the DOM.
*
* @returns {*|HTMLElement}
*/
function getPropertyContainer() {
var $propertyContainer = $('.content-block .property-container');
if ($propertyContainer.length) {
return $propertyContainer;
}
$propertyContainer = $('<div>', {'class': 'property-container'});
$('.content-block .form-group').first().before($propertyContainer);
return $propertyContainer;
}
/**
* Add properties to the designated container. Also add some CSS classes for easier access.
*
* @param $properties
* @private
*/
function _wrapPropsInContainer($properties) {
var $propertyContainer = getPropertyContainer(),
// the reason why this is not done via a simple counter is that
// the function could have been called multiple times, e.g. when
// properties are created dynamically.
hasAlreadyProperties = !!$propertyContainer.find('.property-block').length;
$properties.each(function () {
var $property = $(this);
if ($property.attr !== undefined) {
var type = (function () {
var $propertyMode = $('.property-mode');
switch ($property.attr('id').replace(/_?property_[\d]+/, '')) {
case 'ro':
return 'readonly-property';
case 'parent':
return 'parent-property';
default:
var $editIcon = $property.find('.icon-edit'),
$editContainer = $property.children('div:first');
var $indexIcon = $property.find('.icon-find');
$editContainer.addClass('property-edit-container');
_hideProperties($editContainer);
_hideIndexes($editContainer);
if ($propertyMode.hasClass('property-mode-simple')) {
$indexIcon.hide();
} else if ($propertyMode.hasClass('property-mode-advanced')) {
$indexIcon.show();
}
//on click on edit icon show property form or hide it
$editIcon.on('click', function () {
//form is close so open it (hide index, show property)
if (!$editContainer.parent().hasClass('property-edit-container-open')) {
//hide index and show properties
_hideIndexes($editContainer);
_showProperties($editContainer);
$editContainer.slideToggle(function () {
$editContainer.parent().toggleClass('property-edit-container-open');
});
}
//it is open so switch between index and property or close it
else {
// close form
if ($($('.property', $editContainer)[0]).is(':visible')) {
$editContainer.slideToggle(function () {
$editContainer.parent().toggleClass('property-edit-container-open');
//hide properties
_hideProperties($editContainer);
});
}
// hide index and show properties
else {
//hide index properties
_hideIndexes($editContainer);
//show properties
_showProperties($editContainer);
}
}
});
//on click on index icon show index form or hide it
$indexIcon.on('click', function () {
//if form property is simple we can show index form
if ($('.property-mode').hasClass('property-mode-advanced')) {
//form is close so open it (hide property, show index)
if (!$editContainer.parent().hasClass('property-edit-container-open')) {
//hide index and show properties
_hideProperties($editContainer);
_showIndexes($editContainer);
$editContainer.slideToggle(function () {
$editContainer.parent().toggleClass('property-edit-container-open');
});
}
//it is open so switch between index and property or close it
else {
// close form
if ($($('.index', $editContainer)[0]).is(':visible')) {
$editContainer.slideToggle(function () {
$editContainer.parent().toggleClass('property-edit-container-open');
//hide indexes
_hideIndexes($editContainer);
});
}
// hide properties and show indexes
else {
_hideProperties($editContainer);
//show properties
_showIndexes($editContainer);
}
}
}
});
return 'regular-property';
}
}());
$property.addClass(!hasAlreadyProperties ? 'property-block-first property-block ' + type : 'property-block ' + type);
$propertyContainer.append($property);
hasAlreadyProperties = true;
}
});
}
/**
* Make properties look nice
*
* @param $properties (optional)
*/
function init($properties) {
var $container = $('.content-block .xhtml_form:first form');
if (!$container.length) {
return;
}
_initializeCopyToClipboard($container);
// case no or empty argument -> find all properties not upgraded yet
if (!$properties || !$properties.length) {
$properties = $container.children('div[id*="property_"]').not('.property-block');
}
if (!$properties.length) {
if ($container.children('[name="tao.forms.instance"]').length) {
secondaryProps.init($container);
}
return;
}
_wrapPropsInContainer($properties);
_upgradeButtons($container, 'radio');
_upgradeButtons($container, 'checkbox');
_toggleModeBtn('disabled');
_showErrors($container);
_checkRegularPropertyLabels($properties);
}
function _showErrors($container) {
var $editContainer;
var $error = $container.find('.error');
if ($error.length) {
$editContainer = $error.closest('.property-edit-container');
if ($editContainer.length) {
_hideIndexes($editContainer);
_showProperties($editContainer);
$editContainer.slideToggle(function () {
$editContainer.parent().toggleClass('property-edit-container-open');
});
}
}
}
function _hideProperties($container) {
$('.property', $container).each(function () {
var $currentTarget = $(this);
while (!_.isEqual($currentTarget.parent()[0], $container[0])) {
$currentTarget = $currentTarget.parent();
}
$currentTarget.hide();
});
_toggleModeBtn('disabled');
}
function _showProperties($container) {
$('.property', $container).each(function () {
var $currentTarget = $(this);
while (!_.isEqual($currentTarget.parent()[0], $container[0])) {
$currentTarget = $currentTarget.parent();
}
if ($(this).hasClass('property-depends-on')) {
if ($(this)[0].length > 1) {
dependsOn.toggle($(this), $currentTarget, $container);
}
return;
}
$currentTarget.show();
});
//show or hide the list values select
var elt = $('[class*="property-type"]', $container).parent("div").next("div");
var propertiesTypes = ['list', 'tree'];
var re = new RegExp(propertiesTypes.join('$|').concat('$'));
if (re.test($('[class*="property-type"]', $container).val())) {
if (elt.css('display') === 'none') {
elt.show();
elt.find('select').removeAttr('disabled');
}
} else if (elt.css('display') !== 'none') {
elt.css('display', 'none');
elt.find('select').prop('disabled', "disabled");
}
_toggleModeBtn('enabled');
}
function _hideIndexes($container) {
$('.index', $container).each(function () {
var $currentTarget = $(this);
while (!_.isEqual($currentTarget.parent()[0], $container[0])) {
$currentTarget = $currentTarget.parent();
}
$currentTarget.hide();
});
$('.index-remover', $container).each(function () {
$(this).parent().hide();
});
}
function _showIndexes($container) {
$('.index', $container).each(function () {
var $currentTarget = $(this);
while (!_.isEqual($currentTarget.parent()[0], $container[0])) {
$currentTarget = $currentTarget.parent();
}
$currentTarget.show();
});
$('.index-remover', $container).each(function () {
$(this).parent().show();
});
}
/**
* Checks and updates property labels
* @param {Object} $properties - properties object
*/
function _checkRegularPropertyLabels($properties) {
$properties.each(function() {
if($(this).hasClass('regular-property')) {
var $parentHeadingLabel = $(this).find('.property-heading-label');
var $editBlockLabel = $(this).find('.property-edit-container input[name$="_label"]');
if ($editBlockLabel.val() !== '') {
$parentHeadingLabel.text($editBlockLabel.val());
}
}
});
}
return {
/**
* Initialize post renderer, this can be done multiple times
*/
init: init,
getPropertyContainer: getPropertyContainer
};
});