'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;