import { Tinymce } from './tinymce';
import { isObject, isString, isBoolean } from './isType';
import getConstant from '../constants';
import * as validator from './isValidInputType';
const isValidatableField = ctx => $(ctx).attr('data-validatable') === 'true';
const validatableFields = (ctx) => {
if (isObject(ctx)) {
return $(ctx).find('[data-validation], [aria-required="true"]');
}
return [];
};
const getValidationTypeForElement = (el) => {
const validation = $(el).attr('data-validation');
// if the specified validation type is defined
if (validation) {
return validation;
// Otherwise if the element is required validate based on its type
} else if ($(el).attr('aria-required') === 'true') {
if ($(el).is('input')) {
return $(el).attr('type'); // available types at https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Form_<input>_types
} else if ($(el).is('select')) {
return 'select';
} else if ($(el).is('.tinymce')) {
return 'tinymce';
} else if ($(el).is('textarea')) {
return 'textarea';
}
}
return false;
};
const blockHelp = (id, msg) => {
if (isString(id) && isString(msg)) {
return `<span id="${id}" class="help-block" style="display:none;">${msg}</span>`;
}
return '';
};
const addAsterisk = (el) => {
const asterisk = '<span class="red" title="This field is required.">* </span>';
if (isObject(el)) {
switch (getValidationTypeForElement(el)) {
case 'checkbox': {
el.after(asterisk);
break;
}
default: {
const label = el.closest('.form-group').find('label');
if (isObject(label)) {
$(label[0]).before(asterisk);
}
break;
}
}
}
};
const ariaInvalid = (value) => {
if (isBoolean(value)) {
return { 'aria-invalid': value };
}
return { 'aria-invalid': false };
};
const validationStates = {
hasWarning: 'has-warning',
hasError: 'has-error',
hasSuccess: 'has-success',
};
const getValue = (type, el) => {
switch (type) {
case 'radio':
return $(el).closest('form').find(`input[name="${$(el).attr('name')}"]:checked`).val();
case 'select':
return $(el).find(':selected').val();
case 'tinymce':
return Tinymce.findEditorById($(el).attr('id')).getContent();
case 'checkbox':
return ($(el).is(':checked') ? 'checked' : '');
default:
return $(el).val();
}
};
const isValid = (type, value) => {
// TODO add more validation for each new type coming along by:
// 1. defining a function at dmproadmap.utils.validate
// 2. adding the case in the switch below
// See if a specific data-validation was specified
switch (type) {
case 'text':
return validator.isValidText(value);
case 'textarea':
return validator.isValidText(value);
case 'tinymce':
return validator.isValidText(value);
case 'number':
return validator.isValidNumber(value);
case 'email':
return validator.isValidEmail(value);
case 'url':
return validator.isValidUrl(value);
case 'password':
return validator.isValidPassword(value);
case 'radio':
return validator.isValidText(value);
case 'select':
case 'checkbox':
return validator.isValidText(value);
case 'js-combobox':
return validator.isValidText(value);
default:
return false;
}
};
const getDefaultValidationMessage = (type) => {
switch (type) {
case 'text':
return getConstant('VALIDATION_MESSAGE_TEXT');
case 'textarea':
return getConstant('VALIDATION_MESSAGE_TEXT');
case 'number':
return getConstant('VALIDATION_MESSAGE_NUMBER');
case 'email':
return getConstant('VALIDATION_MESSAGE_EMAIL');
case 'url':
return getConstant('VALIDATION_MESSAGE_URL');
case 'password':
return getConstant('VALIDATION_MESSAGE_PASSWORD');
case 'radio':
return getConstant('VALIDATION_MESSAGE_RADIO');
case 'checkbox':
return getConstant('VALIDATION_MESSAGE_CHECKBOX');
case 'js-combobox':
return getConstant('VALIDATION_MESSAGE_SELECT');
default:
return getConstant('VALIDATION_MESSAGE_DEFAULT');
}
};
const getValidationMessage = (el) => {
if ($(el).attr('data-validation-error')) {
return $(el).attr('data-validation-error');
}
// Use the default validation error message if none was specified
return getDefaultValidationMessage(getValidationTypeForElement(el));
};
const valid = (el) => {
$(el).parent().removeClass(validationStates.hasError);
$(el).attr(ariaInvalid(false));
$(el).next().hide();
};
const invalid = (el) => {
$(el).parent().addClass(validationStates.hasError);
$(el).attr(ariaInvalid(true));
$(el).next().show();
};
const addValidationMessage = (el, excludeAsterisks) => {
const target = $(el);
if (isString(target.attr('id'))) {
const id = target.attr('id');
if (!isString(target.attr('aria-describedby'))) {
target.after(blockHelp(`help-${id}`, getValidationMessage(el)));
target.attr('aria-describedby', `help-${id}`);
target.attr('data-validatable', 'true');
}
}
if (target.attr('aria-required') === 'true' && !excludeAsterisks) {
addAsterisk(target);
}
};
const removeValidationMessage = (el) => {
const target = $(el);
if (isString(target.attr('id'))) {
const parent = target.parent();
parent.removeClass(validationStates.hasError);
parent.find('.help-block').remove();
target.removeAttr('aria-describedby');
target.removeAttr('data-validatable');
}
};
const checkValidations = (el) => {
const type = getValidationTypeForElement(el);
const value = getValue(type, el);
// A field is validatable if has data-validatable attribute set to true
if (isValidatableField(el)) {
if (isValid(type, value)) {
valid(el);
return true;
}
invalid(el);
return false;
}
return true;
};
export const enableValidations = (ctx, excludeAsterisks = false) => {
if (isObject(ctx)) {
if ($(ctx).is('input')) {
addValidationMessage(ctx, excludeAsterisks);
} else {
validatableFields(ctx).each((i, el) => {
addValidationMessage(el, excludeAsterisks);
});
}
}
};
export const disableValidations = (ctx) => {
if (isObject(ctx)) {
if ($(ctx).is('input')) {
removeValidationMessage(ctx);
} else {
validatableFields(ctx).each((i, el) => {
removeValidationMessage(el);
});
}
}
};
export const validate = (ctx) => {
let anyInvalid = false;
if (isObject(ctx)) {
if ($(ctx).is('input')) {
anyInvalid = !checkValidations(ctx);
} else {
validatableFields(ctx).each((i, el) => {
if (!checkValidations(el)) {
anyInvalid = true;
}
});
}
}
return !anyInvalid;
};