216 lines
5.3 KiB
JavaScript
216 lines
5.3 KiB
JavaScript
"use strict";
|
|
|
|
import $ from "jquery";
|
|
import Events from "../_events";
|
|
|
|
const FormValidateField = (($) => {
|
|
// Constants
|
|
const NAME = "jsFormValidateField";
|
|
const DATA_KEY = NAME;
|
|
const $Body = $("body");
|
|
|
|
const FieldUI = "jsFormFieldUI";
|
|
|
|
class FormValidateField {
|
|
constructor(el) {
|
|
const ui = this;
|
|
const $el = $(el);
|
|
|
|
ui.$el = $el;
|
|
|
|
ui._actions = $el.parents("form").children(".btn-toolbar,.form-actions");
|
|
$el.data(DATA_KEY, this);
|
|
|
|
// prevent browsers checks (will do it using JS)
|
|
$el.attr("novalidate", "novalidate");
|
|
|
|
$el.on("change focusout", (e) => {
|
|
ui.validate(false);
|
|
});
|
|
|
|
$el.addClass(`${NAME}-active`);
|
|
$el.trigger(Events.FORM_INIT_VALIDATE_FIELD);
|
|
}
|
|
|
|
// Public methods
|
|
dispose() {
|
|
const $el = ui.$el;
|
|
|
|
$el.removeClass(`${NAME}-active`);
|
|
$.removeData(ui.$el[0], DATA_KEY);
|
|
ui.$el = null;
|
|
}
|
|
|
|
validate(scrollTo = true) {
|
|
const ui = this;
|
|
const $el = ui.$el;
|
|
|
|
const $field = $el.closest(".field");
|
|
const extraChecks = $el.data(`${NAME}-extra`);
|
|
let valid = true;
|
|
let msg = null;
|
|
|
|
const val = $el.val();
|
|
|
|
// browser checks
|
|
if (!ui.$el[0].checkValidity()) {
|
|
valid = false;
|
|
console.warn(
|
|
`${NAME}: Browser check validity is failed #${$el.attr("id")}`
|
|
);
|
|
}
|
|
|
|
let unmaskedVal = val;
|
|
if (typeof $el.inputmask === "function") {
|
|
unmaskedVal = $el.inputmask("unmaskedvalue");
|
|
}
|
|
|
|
// required
|
|
if (
|
|
$el.hasClass("required") &&
|
|
(!unmaskedVal.length ||
|
|
!unmaskedVal.trim().length ||
|
|
(ui.isHtml(val) && !$(unmaskedVal).text().length))
|
|
) {
|
|
valid = false;
|
|
console.warn(`${NAME}: Required field is missing #${$el.attr("id")}`);
|
|
}
|
|
|
|
// validate URL
|
|
if (
|
|
$el.hasClass("url") &&
|
|
unmaskedVal.length &&
|
|
!ui.valideURL(unmaskedVal)
|
|
) {
|
|
valid = false;
|
|
|
|
msg =
|
|
"Invalid URL: URL must start with http:// or https://. For example: https://your-domain.com/bla-bla/?p1=b&p2=a#tag";
|
|
console.warn(`${NAME}: Wrong URL #${$el.attr("id")}`);
|
|
}
|
|
|
|
// maxlength
|
|
const maxLength = $el.attr("maxlength");
|
|
if (unmaskedVal.length && maxLength && maxLength.length) {
|
|
if (unmaskedVal.length > maxLength) {
|
|
valid = false;
|
|
|
|
msg = `The value is limited to ${maxLength} chars`;
|
|
console.warn(`${NAME}: Too long value #${$el.attr("id")}`);
|
|
}
|
|
}
|
|
|
|
// minlength
|
|
const minLength = $el.attr("minlength");
|
|
if (unmaskedVal.length && minLength && minLength.length) {
|
|
if (unmaskedVal.length < minLength) {
|
|
valid = false;
|
|
|
|
msg = `The value should contain more than ${minLength} chars`;
|
|
console.warn(`${NAME}: Too short value #${$el.attr("id")}`);
|
|
}
|
|
}
|
|
|
|
this.removeError();
|
|
|
|
// extra checks
|
|
if (extraChecks) {
|
|
extraChecks.forEach((check) => {
|
|
const result = check($el);
|
|
valid = valid && result;
|
|
if (!result) {
|
|
console.log(check);
|
|
console.warn(`${NAME}: Extra check is failed #${$el.attr("id")}`);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (valid) {
|
|
return true;
|
|
}
|
|
|
|
this.setError(scrollTo, msg);
|
|
|
|
return false;
|
|
}
|
|
|
|
isHtml(str) {
|
|
const doc = new DOMParser().parseFromString(str, "text/html");
|
|
return Array.from(doc.body.childNodes).some(
|
|
(node) => node.nodeType === 1
|
|
);
|
|
}
|
|
|
|
valideURL(str) {
|
|
let url;
|
|
|
|
try {
|
|
url = new URL(str);
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
|
|
return url.protocol === "http:" || url.protocol === "https:";
|
|
}
|
|
|
|
setError(scrollTo = true, msg = null) {
|
|
const ui = this;
|
|
const fieldUI = ui.$el.data(FieldUI);
|
|
|
|
const $field = ui.$el.closest(".field");
|
|
|
|
const bodyScroll = $Body.scrollTop();
|
|
const pos = $field[0].getBoundingClientRect().top; //$field.offset().top;
|
|
|
|
const rowCorrection = parseInt($field.css("font-size")) * 4;
|
|
ui.removeError();
|
|
|
|
$field.addClass("error");
|
|
if (msg) {
|
|
fieldUI.addMessage(msg, "alert-error alert-danger", scrollTo);
|
|
} else if (pos && scrollTo) {
|
|
$field.focus();
|
|
$Body.scrollTop(bodyScroll + pos - rowCorrection);
|
|
}
|
|
}
|
|
|
|
removeError() {
|
|
const ui = this;
|
|
const fieldUI = ui.$el.data(FieldUI);
|
|
|
|
const $field = ui.$el.closest(".field");
|
|
|
|
$field.removeClass("error");
|
|
|
|
$field.removeClass("holder-error");
|
|
$field.removeClass("holder-validation");
|
|
$field.find(".alert-error").remove();
|
|
}
|
|
|
|
static _jQueryInterface() {
|
|
return this.each(function () {
|
|
// attach functionality to el
|
|
const $el = $(this);
|
|
let data = $el.data(DATA_KEY);
|
|
|
|
if (!data) {
|
|
data = new FormValidateField(this);
|
|
$el.data(DATA_KEY, data);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// jQuery interface
|
|
$.fn[NAME] = FormValidateField._jQueryInterface;
|
|
$.fn[NAME].Constructor = FormValidateField;
|
|
$.fn[NAME].noConflict = function () {
|
|
$.fn[NAME] = JQUERY_NO_CONFLICT;
|
|
return FormValidateField._jQueryInterface;
|
|
};
|
|
|
|
return FormValidateField;
|
|
})($);
|
|
|
|
export default FormValidateField;
|