151 lines
6.3 KiB
JavaScript
151 lines
6.3 KiB
JavaScript
(function (window, document) {
|
|
"use strict";
|
|
|
|
const FORMATS = Object.freeze({
|
|
Email: /^\S*\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,8}\b\S*$/,
|
|
Numeric: /^\d*$/,
|
|
AlphaNumeric: /^[a-zA-Z0-9_*\/\-]*$/,
|
|
AlphaSpace: /^[a-zA-Z0-9()\/ &]*$/,
|
|
LoanAmount: /^[a-zA-Z0-9. ]*$/,
|
|
SplInstruction: /^[a-zA-Z0-9():,#@.\-\/ ]*$/,
|
|
Remarks: /^[a-zA-Z0-9,(),:+&#@'.\-\/ ]*$/,
|
|
Pincode: /^\d{6}$/,
|
|
Phone: /^((\d{6,8}|\d{10}|\d{3}-\d{6,8})([\/](\d{6,8}|\d{10}|\d{3}-\d{6,8})){0,2})$/,
|
|
Extension: /^\d{3,4}$/
|
|
});
|
|
const DATE_PATTERN = /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/;
|
|
|
|
function sanitize(value, options) {
|
|
const multiline = Boolean(options && options.multiline);
|
|
let sanitized = String(value == null ? "" : value).replace(/\t/g, "");
|
|
if (multiline) {
|
|
sanitized = sanitized.replace(/\r\n?/g, "\n").replace(/[^\x20-\x7E\n]/g, "");
|
|
} else {
|
|
sanitized = sanitized.replace(/[\r\n]/g, "").replace(/[^\x20-\x7E]/g, "");
|
|
}
|
|
return sanitized.trim();
|
|
}
|
|
|
|
function sanitizeElement(element) {
|
|
if (!element || typeof element.value !== "string") return "";
|
|
const tagName = element.tagName ? element.tagName.toLowerCase() : "";
|
|
const inputType = (element.type || "text").toLowerCase();
|
|
const sanitizableInput = tagName === "input" &&
|
|
["text", "search", "tel", "email", "url", "password"].includes(inputType);
|
|
if (tagName !== "textarea" && !sanitizableInput) return element.value;
|
|
const sanitized = sanitize(element.value, { multiline: tagName === "textarea" });
|
|
if (element.value !== sanitized) element.value = sanitized;
|
|
return sanitized;
|
|
}
|
|
|
|
function valueOf(element) {
|
|
return element && typeof element.value === "string" ? element.value.trim() : "";
|
|
}
|
|
|
|
function isEmpty(element) {
|
|
const value = valueOf(element);
|
|
return value === "" || (element.type === "select-one" && value === "-1");
|
|
}
|
|
|
|
function isDate(value) {
|
|
const parts = DATE_PATTERN.exec(value);
|
|
if (!parts) return false;
|
|
const day = Number(parts[1]);
|
|
const month = Number(parts[2]);
|
|
const year = Number(parts[3]);
|
|
if (year < 1900 || year > 2100 || month < 1 || month > 12) return false;
|
|
const date = new Date(year, month - 1, day);
|
|
return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day;
|
|
}
|
|
|
|
function matchesFormat(value, format) {
|
|
if (!value || !format || format === "AllowWrong") return true;
|
|
const normalized = Object.keys(FORMATS).find((name) => name.toLowerCase() === format.toLowerCase());
|
|
if (format.toLowerCase() === "date") return isDate(value);
|
|
return normalized ? FORMATS[normalized].test(value) : true;
|
|
}
|
|
|
|
function errorHost(element) {
|
|
let current = element.parentNode;
|
|
let host = current;
|
|
while (current && current !== document.body) {
|
|
if ((" " + (current.className || "") + " ").indexOf(" inputcontainer ") !== -1) return current;
|
|
current = current.parentNode;
|
|
}
|
|
return host;
|
|
}
|
|
|
|
function clearError(element) {
|
|
if (!element) return;
|
|
const icon = document.getElementById("err" + element.id);
|
|
if (icon && icon.parentNode) icon.parentNode.removeChild(icon);
|
|
element.classList.remove("errtxt");
|
|
if (element.parentNode) element.parentNode.classList.remove("errdiv");
|
|
element.removeAttribute("aria-invalid");
|
|
element.removeAttribute("aria-describedby");
|
|
if (typeof element.setCustomValidity === "function") element.setCustomValidity("");
|
|
}
|
|
|
|
function showError(element, message) {
|
|
if (!element) return;
|
|
clearError(element);
|
|
const host = errorHost(element);
|
|
const icon = document.createElement("img");
|
|
icon.src = "/matrix/images/exclamation.png";
|
|
icon.id = "err" + element.id;
|
|
icon.alt = message;
|
|
icon.title = message;
|
|
icon.className = "matrix-validation-error-icon" +
|
|
(element.type === "select-one" ? " matrix-validation-error-icon--select" : "");
|
|
icon.style.cssText = "position:absolute;right:" + (element.type === "select-one" ? "24px" : "5px") +
|
|
";top:50%;width:14px;height:14px;transform:translateY(-50%);z-index:3;pointer-events:none";
|
|
host.style.position = "relative";
|
|
host.appendChild(icon);
|
|
if (element.type === "select-one") element.parentNode.classList.add("errdiv");
|
|
else element.classList.add("errtxt");
|
|
element.setAttribute("aria-invalid", "true");
|
|
element.setAttribute("aria-describedby", icon.id);
|
|
if (typeof element.setCustomValidity === "function") element.setCustomValidity(message);
|
|
}
|
|
|
|
function validateElement(element, rule) {
|
|
if (!element) return true;
|
|
sanitizeElement(element);
|
|
if (typeof rule.when === "function" && !rule.when()) {
|
|
clearError(element);
|
|
return true;
|
|
}
|
|
const required = typeof rule.required === "function" ? rule.required() : Boolean(rule.required);
|
|
let message = "";
|
|
if (required && isEmpty(element)) message = rule.requiredMessage || "This field is required.";
|
|
else if (!isEmpty(element) && !matchesFormat(valueOf(element), rule.format)) {
|
|
message = rule.formatMessage || "Enter a valid value.";
|
|
}
|
|
if (message) showError(element, message);
|
|
else clearError(element);
|
|
return message === "";
|
|
}
|
|
|
|
function validateLegacy(element, requiredFlag, format) {
|
|
return validateElement(element, { required: requiredFlag === "t", format: format });
|
|
}
|
|
|
|
function validateRules(rules) {
|
|
const invalid = [];
|
|
Object.keys(rules).forEach(function (id) {
|
|
const element = document.getElementById(id);
|
|
if (!validateElement(element, rules[id])) invalid.push(element);
|
|
});
|
|
return { valid: invalid.length === 0, invalid: invalid };
|
|
}
|
|
|
|
window.CygnusFormValidation = Object.freeze({
|
|
clearError: clearError,
|
|
sanitize: sanitize,
|
|
sanitizeElement: sanitizeElement,
|
|
validateElement: validateElement,
|
|
validateLegacy: validateLegacy,
|
|
validateRules: validateRules
|
|
});
|
|
}(window, document));
|