formiojs
Version:
Common js library for client side interaction with <form.io>
1,149 lines (1,120 loc) • 52.8 kB
JavaScript
"use strict";
require("core-js/modules/es.object.define-property.js");
require("core-js/modules/es.symbol.iterator.js");
require("core-js/modules/es.array.iterator.js");
require("core-js/modules/es.string.iterator.js");
require("core-js/modules/web.dom-collections.iterator.js");
require("core-js/modules/es.array.slice.js");
require("core-js/modules/es.function.name.js");
require("core-js/modules/es.array.from.js");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = exports.ValidationChecker = void 0;
require("core-js/modules/es.array.some.js");
require("core-js/modules/es.object.to-string.js");
require("core-js/modules/es.regexp.exec.js");
require("core-js/modules/es.string.split.js");
require("core-js/modules/es.array.reduce.js");
require("core-js/modules/es.array.concat.js");
require("core-js/modules/es.regexp.constructor.js");
require("core-js/modules/es.regexp.to-string.js");
require("core-js/modules/es.date.to-string.js");
require("core-js/modules/es.array.is-array.js");
require("core-js/modules/es.array.filter.js");
require("core-js/modules/es.array.includes.js");
require("core-js/modules/es.string.includes.js");
require("core-js/modules/es.array.join.js");
require("core-js/modules/es.array.map.js");
require("core-js/modules/es.parse-float.js");
require("core-js/modules/es.number.is-nan.js");
require("core-js/modules/es.number.constructor.js");
require("core-js/modules/es.object.keys.js");
require("core-js/modules/es.parse-int.js");
require("core-js/modules/es.string.trim.js");
require("core-js/modules/es.string.match.js");
require("core-js/modules/es.string.replace.js");
require("core-js/modules/es.array.for-each.js");
require("core-js/modules/web.dom-collections.for-each.js");
require("core-js/modules/es.object.values.js");
require("core-js/modules/es.symbol.to-primitive.js");
require("core-js/modules/es.date.to-primitive.js");
require("core-js/modules/es.symbol.js");
require("core-js/modules/es.symbol.description.js");
var _lodash = _interopRequireDefault(require("lodash"));
var _utils = require("../utils/utils");
var _moment = _interopRequireDefault(require("moment"));
var _nativePromiseOnly = _interopRequireDefault(require("native-promise-only"));
var _inputmask = _interopRequireDefault(require("inputmask"));
var _fetchPonyfill2 = _interopRequireDefault(require("fetch-ponyfill"));
var _calendarUtils = require("../utils/calendarUtils");
var _Rules = _interopRequireDefault(require("./Rules"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0) { ; } } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i["return"] && (_r = _i["return"](), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
var _fetchPonyfill = (0, _fetchPonyfill2["default"])({
Promise: _nativePromiseOnly["default"]
}),
fetch = _fetchPonyfill.fetch,
Headers = _fetchPonyfill.Headers,
Request = _fetchPonyfill.Request;
var ValidationChecker = /*#__PURE__*/function () {
function ValidationChecker() {
var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
_classCallCheck(this, ValidationChecker);
this.config = _lodash["default"].defaults(config, ValidationChecker.config);
this.validators = {
required: {
key: 'validate.required',
method: 'validateRequired',
hasLabel: true,
message: function message(component) {
return component.t(component.errorMessage('required'), {
field: component.errorLabel,
data: component.data
});
},
check: function check(component, setting, value) {
if (!(0, _utils.boolValue)(setting) || component.isValueHidden()) {
return true;
}
var isCalendar = component.validators.some(function (validator) {
return validator === 'calendar';
});
if (!value && isCalendar && component.widget.enteredDate) {
return !this.validators.calendar.check.call(this, component, setting, value);
}
return !component.isEmpty(value);
}
},
onlyAvailableItems: {
key: 'validate.onlyAvailableItems',
method: 'validateValueAvailability',
hasLabel: true,
message: function message(component) {
return component.t(component.errorMessage('valueIsNotAvailable'), {
field: component.errorLabel,
data: component.data
});
},
check: function check(component, setting) {
return !(0, _utils.boolValue)(setting);
}
},
unique: {
key: 'validate.unique',
hasLabel: true,
message: function message(component) {
return component.t(component.errorMessage('unique'), {
field: component.errorLabel,
data: component.data
});
},
check: function check(component, setting, value) {
var _this = this;
// Skip if setting is falsy
if (!(0, _utils.boolValue)(setting)) {
return true;
}
// Skip if value is empty object or falsy
if (!value || _lodash["default"].isObjectLike(value) && _lodash["default"].isEmpty(value)) {
return true;
}
// Skip if we don't have a database connection
if (!this.config.db) {
return true;
}
return new _nativePromiseOnly["default"](function (resolve) {
var form = _this.config.form;
var submission = _this.config.submission;
var path = "data.".concat(component.path);
var addPathQueryParams = function addPathQueryParams(pathQueryParams, query, path) {
var pathArray = path.split(/\[\d+\]?./);
var needValuesInArray = pathArray.length > 1;
var pathToValue = path;
if (needValuesInArray) {
pathToValue = pathArray.shift();
var pathQueryObj = {};
_lodash["default"].reduce(pathArray, function (pathQueryPath, pathPart, index) {
var isLastPathPart = index === pathArray.length - 1;
var obj = _lodash["default"].get(pathQueryObj, pathQueryPath, pathQueryObj);
var addedPath = "$elemMatch['".concat(pathPart, "']");
_lodash["default"].set(obj, addedPath, isLastPathPart ? pathQueryParams : {});
return pathQueryPath ? "".concat(pathQueryPath, ".").concat(addedPath) : addedPath;
}, '');
query[pathToValue] = pathQueryObj;
} else {
query[pathToValue] = pathQueryParams;
}
};
// Build the query
var query = {
form: form._id
};
var collationOptions = {};
if (_lodash["default"].isString(value)) {
var _component$component$;
if (component.component.dbIndex) {
addPathQueryParams(value, query, path);
}
// These are kind of hacky but provides for a more efficient "unique" validation when the string is an email,
// because we (by and large) only have to worry about ASCII and partial unicode; this way, we can use collation-
// aware indexes with case insensitive email searches to make things like login and registration a whole lot faster
else if (component.component.type === 'email' || component.component.type === 'textfield' && ((_component$component$ = component.component.validate) === null || _component$component$ === void 0 ? void 0 : _component$component$.pattern) === '[A-Za-z0-9]+') {
addPathQueryParams(value, query, path);
collationOptions = {
collation: {
locale: 'en',
strength: 2
}
};
} else {
addPathQueryParams({
$regex: new RegExp("^".concat((0, _utils.escapeRegExCharacters)(value), "$")),
$options: 'i'
}, query, path);
}
}
// FOR-213 - Pluck the unique location id
else if (_lodash["default"].isPlainObject(value) && value.address && value.address['address_components'] && value.address['place_id']) {
addPathQueryParams({
$regex: new RegExp("^".concat((0, _utils.escapeRegExCharacters)(value.address['place_id']), "$")),
$options: 'i'
}, query, "".concat(path, ".address.place_id"));
}
// Compare the contents of arrays vs the order.
else if (_lodash["default"].isArray(value)) {
addPathQueryParams({
$all: value
}, query, path);
} else if (_lodash["default"].isObject(value) || _lodash["default"].isNumber(value)) {
addPathQueryParams({
$eq: value
}, query, path);
}
// Only search for non-deleted items
query.deleted = {
$eq: null
};
query.state = 'submitted';
var uniqueValidationCallback = function uniqueValidationCallback(err, result) {
if (err) {
return resolve(false);
} else if (result) {
// Only OK if it matches the current submission
if (submission._id && result._id.toString() === submission._id) {
resolve(true);
} else {
component.conflictId = result._id.toString();
return resolve(false);
}
} else {
return resolve(true);
}
};
// Try to find an existing value within the form
_this.config.db.findOne(query, null, collationOptions, function (err, result) {
if (err && collationOptions.collation) {
// presume this error comes from db compatibility, try again as regex
delete query[path];
addPathQueryParams({
$regex: new RegExp("^".concat((0, _utils.escapeRegExCharacters)(value), "$")),
$options: 'i'
}, query, path);
_this.config.db.findOne(query, uniqueValidationCallback);
} else {
return uniqueValidationCallback(err, result);
}
});
})["catch"](function () {
return false;
});
}
},
multiple: {
key: 'validate.multiple',
hasLabel: true,
message: function message(component) {
var shouldBeArray = (0, _utils.boolValue)(component.component.multiple) || Array.isArray(component.emptyValue);
var isRequired = component.component.validate.required;
var messageKey = shouldBeArray ? isRequired ? 'array_nonempty' : 'array' : 'nonarray';
return component.t(component.errorMessage(messageKey), {
field: component.errorLabel,
data: component.data
});
},
check: function check(component, setting, value) {
// Skip multiple validation if the component tells us to
if (!component.validateMultiple()) {
return true;
}
var shouldBeArray = (0, _utils.boolValue)(setting);
var canBeArray = Array.isArray(component.emptyValue);
var isArray = Array.isArray(value);
var isRequired = component.component.validate.required;
if (shouldBeArray) {
if (isArray) {
return isRequired ? !!value.length : true;
} else {
// Null/undefined is ok if this value isn't required; anything else should fail
return _lodash["default"].isNil(value) ? !isRequired : false;
}
} else {
return canBeArray || !isArray;
}
}
},
select: {
key: 'validate.select',
hasLabel: true,
message: function message(component) {
return component.t(component.errorMessage('select'), {
field: component.errorLabel,
data: component.data
});
},
check: function check(component, setting, value, data, index, row, async) {
// Skip if setting is falsy
if (!(0, _utils.boolValue)(setting)) {
return true;
}
// Skip if value is empty
if (!value || _lodash["default"].isEmpty(value)) {
return true;
}
// Skip if we're not async-capable
if (!async) {
return true;
}
var schema = component.component;
// Initialize the request options
var requestOptions = {
url: setting,
method: 'GET',
qs: {},
json: true,
headers: {}
};
// If the url is a boolean value
if (_lodash["default"].isBoolean(requestOptions.url)) {
requestOptions.url = !!requestOptions.url;
if (!requestOptions.url || schema.dataSrc !== 'url' || !schema.data.url || !schema.searchField) {
return true;
}
// Get the validation url
requestOptions.url = schema.data.url;
// Add the search field
requestOptions.qs[schema.searchField] = value;
// Add the filters
if (schema.filter) {
requestOptions.url += (!requestOptions.url.includes('?') ? '?' : '&') + schema.filter;
}
// If they only wish to return certain fields.
if (schema.selectFields) {
requestOptions.qs.select = schema.selectFields;
}
}
if (!requestOptions.url) {
return true;
}
// Make sure to interpolate.
requestOptions.url = (0, _utils.interpolate)(requestOptions.url, {
data: component.data
});
// Add query string to URL
requestOptions.url += (requestOptions.url.includes('?') ? '&' : '?') + _lodash["default"].chain(requestOptions.qs).map(function (val, key) {
return "".concat(encodeURIComponent(key), "=").concat(encodeURIComponent(val));
}).join('&').value();
// Set custom headers.
if (schema.data && schema.data.headers) {
_lodash["default"].each(schema.data.headers, function (header) {
if (header.key) {
requestOptions.headers[header.key] = header.value;
}
});
}
// Set form.io authentication.
if (schema.authenticate && this.config.token) {
requestOptions.headers['x-jwt-token'] = this.config.token;
}
return fetch(new Request(requestOptions.url, {
headers: new Headers(requestOptions.headers)
})).then(function (response) {
if (!response.ok) {
return false;
}
return response.json();
}).then(function (results) {
return results && results.length;
})["catch"](function () {
return false;
});
}
},
min: {
key: 'validate.min',
hasLabel: true,
message: function message(component, setting) {
return component.t(component.errorMessage('min'), {
field: component.errorLabel,
min: parseFloat(setting),
data: component.data
});
},
check: function check(component, setting, value) {
var min = parseFloat(setting);
var parsedValue = parseFloat(value);
if (Number.isNaN(min) || Number.isNaN(parsedValue)) {
return true;
}
return parsedValue >= min;
}
},
max: {
key: 'validate.max',
hasLabel: true,
message: function message(component, setting) {
return component.t(component.errorMessage('max'), {
field: component.errorLabel,
max: parseFloat(setting),
data: component.data
});
},
check: function check(component, setting, value) {
var max = parseFloat(setting);
var parsedValue = parseFloat(value);
if (Number.isNaN(max) || Number.isNaN(parsedValue)) {
return true;
}
return parsedValue <= max;
}
},
minSelectedCount: {
key: 'validate.minSelectedCount',
message: function message(component, setting) {
return component.component.minSelectedCountMessage ? component.component.minSelectedCountMessage : component.t(component.errorMessage('minSelectedCount'), {
minCount: parseFloat(setting),
data: component.data
});
},
check: function check(component, setting, value) {
var min = parseFloat(setting);
if (!min) {
return true;
}
var count = Object.keys(value).reduce(function (total, key) {
if (value[key]) {
total++;
}
return total;
}, 0);
// Should not be triggered if there is no options selected at all
return !count || count >= min;
}
},
maxSelectedCount: {
key: 'validate.maxSelectedCount',
message: function message(component, setting) {
return component.component.maxSelectedCountMessage ? component.component.maxSelectedCountMessage : component.t(component.errorMessage('maxSelectedCount'), {
minCount: parseFloat(setting),
data: component.data
});
},
check: function check(component, setting, value) {
var max = parseFloat(setting);
if (!max) {
return true;
}
var count = Object.keys(value).reduce(function (total, key) {
if (value[key]) {
total++;
}
return total;
}, 0);
return count <= max;
}
},
minLength: {
key: 'validate.minLength',
hasLabel: true,
message: function message(component, setting) {
return component.t(component.errorMessage('minLength'), {
field: component.errorLabel,
length: setting,
data: component.data
});
},
check: function check(component, setting, value) {
var minLength = parseInt(setting, 10);
if (!value || !minLength || typeof value !== 'string' || component.isEmpty(value)) {
return true;
}
return value.length >= minLength;
}
},
maxLength: {
key: 'validate.maxLength',
hasLabel: true,
message: function message(component, setting) {
return component.t(component.errorMessage('maxLength'), {
field: component.errorLabel,
length: setting,
data: component.data
});
},
check: function check(component, setting, value) {
var maxLength = parseInt(setting, 10);
if (!maxLength || typeof value !== 'string') {
return true;
}
return value.length <= maxLength;
}
},
maxWords: {
key: 'validate.maxWords',
hasLabel: true,
message: function message(component, setting) {
return component.t(component.errorMessage('maxWords'), {
field: component.errorLabel,
length: setting,
data: component.data
});
},
check: function check(component, setting, value) {
var maxWords = parseInt(setting, 10);
if (!maxWords || typeof value !== 'string') {
return true;
}
return value.trim().split(/\s+/).length <= maxWords;
}
},
minWords: {
key: 'validate.minWords',
hasLabel: true,
message: function message(component, setting) {
return component.t(component.errorMessage('minWords'), {
field: component.errorLabel,
length: setting,
data: component.data
});
},
check: function check(component, setting, value) {
var minWords = parseInt(setting, 10);
if (!minWords || !value || typeof value !== 'string') {
return true;
}
return value.trim().split(/\s+/).length >= minWords;
}
},
email: {
hasLabel: true,
message: function message(component) {
return component.t(component.errorMessage('invalid_email'), {
field: component.errorLabel,
data: component.data
});
},
check: function check(component, setting, value) {
/* eslint-disable max-len */
// From http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
var re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
/* eslint-enable max-len */
// Allow emails to be valid if the component is pristine and no value is provided.
return !value || re.test(value);
}
},
url: {
hasLabel: true,
message: function message(component) {
return component.t(component.errorMessage('invalid_url'), {
field: component.errorLabel,
data: component.data
});
},
check: function check(component, setting, value) {
/* eslint-disable max-len */
// From https://stackoverflow.com/questions/8667070/javascript-regular-expression-to-validate-url
var re = /^(?:(?:(?:https?|ftp):)?\/\/)?(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i;
// From http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
var emailRe = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
/* eslint-enable max-len */
// Allow urls to be valid if the component is pristine and no value is provided.
return !value || re.test(value) && !emailRe.test(value);
}
},
date: {
hasLabel: true,
message: function message(component) {
return component.t(component.errorMessage('invalid_date'), {
field: component.errorLabel,
data: component.data
});
},
check: function check(component, setting, value) {
if (!value) {
return true;
}
if (value === 'Invalid date' || value === 'Invalid Date') {
return false;
}
if (typeof value === 'string') {
value = new Date(value);
}
return value instanceof Date === true && value.toString() !== 'Invalid Date';
}
},
day: {
hasLabel: true,
message: function message(component) {
return component.t(component.errorMessage('invalid_day'), {
field: component.errorLabel,
data: component.data
});
},
check: function check(component, setting, value) {
if (!value) {
return true;
}
var _ref = component.dayFirst ? [0, 1, 2] : [1, 0, 2],
_ref2 = _slicedToArray(_ref, 3),
DAY = _ref2[0],
MONTH = _ref2[1],
YEAR = _ref2[2];
var values = value.split('/').map(function (x) {
return parseInt(x, 10);
}),
day = values[DAY],
month = values[MONTH],
year = values[YEAR],
maxDay = getDaysInMonthCount(month, year);
if (day < 0 || day > maxDay) {
return false;
}
if (month < 0 || month > 12) {
return false;
}
if (year < 0 || year > 9999) {
return false;
}
return true;
function isLeapYear(year) {
// Year is leap if it is evenly divisible by 400 or evenly divisible by 4 and not evenly divisible by 100.
return !(year % 400) || !!(year % 100) && !(year % 4);
}
function getDaysInMonthCount(month, year) {
switch (month) {
case 1: // January
case 3: // March
case 5: // May
case 7: // July
case 8: // August
case 10: // October
case 12:
// December
return 31;
case 4: // April
case 6: // June
case 9: // September
case 11:
// November
return 30;
case 2:
// February
return isLeapYear(year) ? 29 : 28;
default:
return 31;
}
}
}
},
pattern: {
key: 'validate.pattern',
hasLabel: true,
message: function message(component, setting) {
return component.t(_lodash["default"].get(component, 'component.validate.patternMessage', component.errorMessage('pattern')), {
field: component.errorLabel,
pattern: setting,
data: component.data
});
},
check: function check(component, setting, value) {
if (component.isEmpty(value)) return true;
var pattern = setting;
if (!pattern) {
return true;
}
var regex = new RegExp("^".concat(pattern, "$"));
return regex.test(value);
}
},
json: {
key: 'validate.json',
check: function check(component, setting, value, data, index, row) {
if (!setting) {
return true;
}
var valid = component.evaluate(setting, {
data: data,
row: row,
rowIndex: index,
input: value
});
if (valid === null) {
return true;
}
return valid;
}
},
mask: {
key: 'inputMask',
hasLabel: true,
message: function message(component) {
return component.t(component.errorMessage('mask'), {
field: component.errorLabel,
data: component.data
});
},
check: function check(component, setting, value) {
var inputMask;
if (component.isMultipleMasksField) {
var maskName = value ? value.maskName : undefined;
var formioInputMask = component.getMaskByName(maskName);
if (formioInputMask) {
inputMask = formioInputMask;
}
value = value ? value.value : value;
} else {
inputMask = setting;
}
if (value && inputMask && typeof value === 'string' && component.type === 'textfield') {
return _inputmask["default"].isValid(value, inputMask);
}
inputMask = inputMask ? (0, _utils.getInputMask)(inputMask) : null;
if (value && inputMask && !component.skipMaskValidation) {
// If char which is used inside mask placeholder was used in the mask, replace it with space to prevent errors
inputMask = inputMask.map(function (_char) {
return _char === component.placeholderChar ? ' ' : _char;
});
return (0, _utils.matchInputMask)(value, inputMask);
}
return true;
}
},
custom: {
key: 'validate.custom',
message: function message(component) {
return component.t(component.errorMessage('custom'), {
field: component.errorLabel,
data: component.data
});
},
check: function check(component, setting, value, data, index, row) {
if (!setting) {
return true;
}
var valid = component.evaluate(setting, {
valid: true,
data: data,
rowIndex: index,
row: row,
input: value
}, 'valid', true);
if (valid === null) {
return true;
}
return valid;
}
},
maxDate: {
key: 'maxDate',
hasLabel: true,
message: function message(component, setting) {
var date = (0, _utils.getDateSetting)(setting);
return component.t(component.errorMessage('maxDate'), {
field: component.errorLabel,
maxDate: (0, _moment["default"])(date).format(component.format)
});
},
check: function check(component, setting, value) {
//if any parts of day are missing, skip maxDate validation
if (component.isPartialDay && component.isPartialDay(value)) {
return true;
}
var date = component.getValidationFormat ? (0, _moment["default"])(value, component.getValidationFormat()) : (0, _moment["default"])(value);
var maxDate = (0, _utils.getDateSetting)(setting);
if (_lodash["default"].isNull(maxDate)) {
return true;
} else {
maxDate.setHours(0, 0, 0, 0);
}
return date.isBefore(maxDate) || date.isSame(maxDate);
}
},
minDate: {
key: 'minDate',
hasLabel: true,
message: function message(component, setting) {
var date = (0, _utils.getDateSetting)(setting);
return component.t(component.errorMessage('minDate'), {
field: component.errorLabel,
minDate: (0, _moment["default"])(date).format(component.format)
});
},
check: function check(component, setting, value) {
//if any parts of day are missing, skip minDate validation
if (component.isPartialDay && component.isPartialDay(value)) {
return true;
}
var date = component.getValidationFormat ? (0, _moment["default"])(value, component.getValidationFormat()) : (0, _moment["default"])(value);
var minDate = (0, _utils.getDateSetting)(setting);
if (_lodash["default"].isNull(minDate)) {
return true;
} else {
minDate.setHours(0, 0, 0, 0);
}
return date.isAfter(minDate) || date.isSame(minDate);
}
},
minYear: {
key: 'minYear',
hasLabel: true,
message: function message(component, setting) {
return component.t(component.errorMessage('minYear'), {
field: component.errorLabel,
minYear: setting
});
},
check: function check(component, setting, value) {
var minYear = setting;
var year = /\d{4}$/.exec(value);
year = year ? year[0] : null;
if (!+minYear || !+year) {
return true;
}
return +year >= +minYear;
}
},
maxYear: {
key: 'maxYear',
hasLabel: true,
message: function message(component, setting) {
return component.t(component.errorMessage('maxYear'), {
field: component.errorLabel,
maxYear: setting
});
},
check: function check(component, setting, value) {
var maxYear = setting;
var year = /\d{4}$/.exec(value);
year = year ? year[0] : null;
if (!+maxYear || !+year) {
return true;
}
return +year <= +maxYear;
}
},
calendar: {
key: 'validate.calendar',
messageText: '',
hasLabel: true,
message: function message(component) {
return component.t(component.errorMessage(this.validators.calendar.messageText), {
field: component.errorLabel,
maxDate: (0, _moment["default"])(component.dataValue).format(component.format)
});
},
check: function check(component, setting, value, data, index) {
this.validators.calendar.messageText = '';
var widget = component.getWidget(index);
if (!widget) {
return true;
}
var settings = widget.settings,
enteredDate = widget.enteredDate;
var minDate = settings.minDate,
maxDate = settings.maxDate,
format = settings.format;
var momentFormat = [(0, _utils.convertFormatToMoment)(format)];
if (momentFormat[0].match(/M{3,}/g)) {
momentFormat.push(momentFormat[0].replace(/M{3,}/g, 'MM'));
}
if (!value && enteredDate) {
var _checkInvalidDate = (0, _calendarUtils.checkInvalidDate)(enteredDate, momentFormat, minDate, maxDate),
message = _checkInvalidDate.message,
result = _checkInvalidDate.result;
if (!result) {
this.validators.calendar.messageText = message;
return result;
}
}
if (value && enteredDate) {
if ((0, _moment["default"])(value).format() !== (0, _moment["default"])(enteredDate, momentFormat, true).format() && enteredDate.match(/_/gi)) {
this.validators.calendar.messageText = _calendarUtils.CALENDAR_ERROR_MESSAGES.INCOMPLETE;
return false;
} else {
widget.enteredDate = '';
return true;
}
}
}
},
time: {
key: 'validate.time',
messageText: 'Invalid time',
hasLabel: true,
message: function message(component) {
return component.t(component.errorMessage(this.validators.time.messageText), {
field: component.errorLabel
});
},
check: function check(component, setting, value) {
if (component.isEmpty(value)) return true;
return (0, _moment["default"])(value, component.component.format).isValid();
}
},
availableValueProperty: {
key: 'validate.availableValueProperty',
method: 'validateValueProperty',
messageText: 'Invalid Value Property',
hasLabel: true,
message: function message(component) {
return component.t(component.errorMessage(this.validators.availableValueProperty.messageText), {
field: component.errorLabel
});
},
check: function check(component, setting, value) {
if (component.component.dataSrc === 'url' && (_lodash["default"].isUndefined(value) || _lodash["default"].isObject(value))) {
return false;
}
return true;
}
}
};
}
_createClass(ValidationChecker, [{
key: "checkValidator",
value: function checkValidator(component, validator, setting, value, data, index, row, async) {
var _this2 = this;
var resultOrPromise = null;
// Allow each component to override their own validators by implementing the validator.method
if (validator.method && typeof component[validator.method] === 'function') {
resultOrPromise = component[validator.method](setting, value, data, index, row, async);
} else {
resultOrPromise = validator.check.call(this, component, setting, value, data, index, row, async);
}
var processResult = function processResult(result) {
if (typeof result === 'string') {
return result;
}
if (!result && validator.message) {
return validator.message.call(_this2, component, setting, index, row);
}
return '';
};
if (async) {
return _nativePromiseOnly["default"].resolve(resultOrPromise).then(processResult);
} else {
return processResult(resultOrPromise);
}
}
}, {
key: "validate",
value: function validate(component, validatorName, value, data, index, row, async, conditionallyVisible, validationObj) {
// Skip validation for conditionally hidden components
if (!conditionallyVisible && !_lodash["default"].get(component.component, 'validateWhenHidden', false)) {
return false;
}
var validator = this.validators[validatorName];
var setting = _lodash["default"].get(validationObj || component.component, validator.key, null);
var resultOrPromise = this.checkValidator(component, validator, setting, value, data, index, row, async);
var processResult = function processResult(result) {
if (result) {
var _component$refs$input;
var resultData = {
message: (0, _utils.unescapeHTML)(_lodash["default"].get(result, 'message', result)),
level: _lodash["default"].get(result, 'level') === 'warning' ? 'warning' : 'error',
path: (0, _utils.getArrayFromComponentPath)(component.path || ''),
context: {
validator: validatorName,
hasLabel: validator.hasLabel,
setting: setting,
key: component.key,
label: component.label,
value: value,
index: index,
input: (_component$refs$input = component.refs.input) === null || _component$refs$input === void 0 ? void 0 : _component$refs$input[index]
}
};
if (validatorName === 'unique' && component.conflictId) {
resultData.conflictId = component.conflictId;
}
return resultData;
} else {
return false;
}
};
if (async) {
return _nativePromiseOnly["default"].resolve(resultOrPromise).then(processResult);
} else {
return processResult(resultOrPromise);
}
}
}, {
key: "checkComponent",
value: function checkComponent(component, data, row) {
var _component$addons,
_this3 = this;
var includeWarnings = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
var async = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
var isServerSidePersistent = typeof process !== 'undefined' && _lodash["default"].get(process, 'release.name') === 'node' && !_lodash["default"].defaultTo(component.component.persistent, true);
// If we're server-side and it's not a persistent component, don't run validation at all
if (isServerSidePersistent || component.component.validate === false) {
return async ? _nativePromiseOnly["default"].resolve([]) : [];
}
data = data || component.rootValue;
row = row || component.data;
var values = component.component.multiple && Array.isArray(component.validationValue) ? component.validationValue : [component.validationValue];
var conditionallyVisible = component.conditionallyVisible();
var addonsValidations = [];
if (component !== null && component !== void 0 && (_component$addons = component.addons) !== null && _component$addons !== void 0 && _component$addons.length) {
values.forEach(function (value) {
component.addons.forEach(function (addon) {
if (!addon.checkValidity(value)) {
addonsValidations.push.apply(addonsValidations, _toConsumableArray(addon.errors || []));
}
});
});
}
// If this component has the new validation system enabled, use it instead.
var validations = _lodash["default"].get(component, 'component.validations');
var nextGenResultsOrPromises = [];
if (validations && Array.isArray(validations) && validations.length) {
var validationsGroupedByMode = _lodash["default"].chain(validations).groupBy(function (validation) {
return validation.mode;
}).value();
if (component.calculateCondition) {
includeWarnings = true;
var uiGroupedValidation = _lodash["default"].chain(validationsGroupedByMode.ui).filter('active').groupBy(function (validation) {
return validation.group || null;
}).value();
var commonValidations = uiGroupedValidation["null"] || [];
delete uiGroupedValidation["null"];
commonValidations.forEach(function (_ref3) {
var condition = _ref3.condition,
message = _ref3.message,
severity = _ref3.severity;
if (!component.calculateCondition(condition)) {
nextGenResultsOrPromises.push({
level: severity || 'error',
message: component.t(message),
componentInstance: component
});
}
});
_lodash["default"].forEach(uiGroupedValidation, function (validationGroup) {
_lodash["default"].forEach(validationGroup, function (_ref4) {
var condition = _ref4.condition,
message = _ref4.message,
severity = _ref4.severity;
if (!component.calculateCondition(condition)) {
nextGenResultsOrPromises.push({
level: severity || 'error',
message: component.t(message),
componentInstance: component
});
return false;
}
});
});
} else {
nextGenResultsOrPromises = this.checkValidations(component, validations, data, row, values, async);
}
if (component.validators.includes('custom') && validationsGroupedByMode.js) {
_lodash["default"].each(validationsGroupedByMode.js, function (validation) {
nextGenResultsOrPromises.push(_lodash["default"].map(values, function (value, index) {
return _this3.validate(component, 'custom', value, data, index, row, async, conditionallyVisible, validation);
}));
});
}
if (component.validators.includes('json') && validationsGroupedByMode.json) {
_lodash["default"].each(validationsGroupedByMode.json, function (validation) {
nextGenResultsOrPromises.push(_lodash["default"].map(values, function (value, index) {
return _this3.validate(component, 'json', value, data, index, row, async, conditionallyVisible, validation);
}));
});
}
}
var validateCustom = _lodash["default"].get(component, 'component.validate.custom');
var customErrorMessage = _lodash["default"].get(component, 'component.validate.customMessage');
// Run primary validators
var resultsOrPromises = (0, _lodash["default"])(component.validators).chain().map(function (validatorName) {
if (!_this3.validators.hasOwnProperty(validatorName)) {
return {
message: "Validator for \"".concat(validatorName, "\" is not defined"),
level: 'warning',
context: {
validator: validatorName,
key: component.key,
label: component.label
}
};
}
// Handle the case when there is no values defined and it is required.
if (validatorName === 'required' && !values.length) {
return [_this3.validate(component, validatorName, null, data, 0, row, async, conditionallyVisible)];
}
return _lodash["default"].map(values, function (value, index) {
return _this3.validate(component, validatorName, value, data, index, row, async, conditionallyVisible);
});
}).flatten().value();
// Run the "unique" pseudo-validator
component.component.validate = component.component.validate || {};
component.component.validate.unique = component.component.unique;
resultsOrPromises.push(this.validate(component, 'unique', component.validationValue, data, 0, data, async, conditionallyVisible));
// Run the "multiple" pseudo-validator
component.component.validate.multiple = component.component.multiple;
resultsOrPromises.push(this.validate(component, 'multiple', component.validationValue, data, 0, data, async, conditionallyVisible));
resultsOrPromises.push.apply(resultsOrPromises, addonsValidations);
resultsOrPromises.push.apply(resultsOrPromises, _toConsumableArray(nextGenResultsOrPromises));
// Define how results should be formatted
var formatResults = function formatResults(results) {
// Condense to a single flat array
results = (0, _lodash["default"])(results).chain().flatten().compact().value();
if (customErrorMessage || validateCustom) {
_lodash["default"].each(results, function (result) {
result.message = component.t(customErrorMessage || result.message, {
field: component.errorLabel,
data: data,
row: row,
error: result
});
result.context.hasLabel = false;
});
}
return includeWarnings ? results : _lodash["default"].reject(results, function (result) {
return result.level === 'warning';
});
};
// Wait for results if using async mode, otherwise process and return immediately
if (async) {
return _nativePromiseOnly["default"].all(resultsOrPromises).then(formatResults);
} else {
return formatResults(resultsOrPromises);
}
}
/**
* Use the new validations engine to evaluate any errors.