form-father 0.8.2 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/FormFather.min.js +1 -1
- package/dist/FormFather.min.js.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.8.4](https://github.com/Poliklot/form-father/compare/form-father-v0.8.3...form-father-v0.8.4) (2026-06-29)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **deps-dev:** update development dependencies ([0c29f98](https://github.com/Poliklot/form-father/commit/0c29f98d3376523d1d3c94099a39d355f5f5cebb))
|
|
11
|
+
|
|
12
|
+
## [0.8.3](https://github.com/Poliklot/form-father/compare/form-father-v0.8.2...form-father-v0.8.3) (2026-06-22)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* avoid unsafe error id regex trimming ([0c9dca8](https://github.com/Poliklot/form-father/commit/0c9dca873c5c7677baffc7381912fcef0fdd9f71))
|
|
18
|
+
|
|
5
19
|
## [0.8.2](https://github.com/Poliklot/form-father/compare/form-father-v0.8.1...form-father-v0.8.2) (2026-06-18)
|
|
6
20
|
|
|
7
21
|
|
package/dist/FormFather.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var FormFather=function(t){"use strict";function e(t,e){if(null==t)return null;let r=t;for(;r;){if(r.matches(e))return r;r=r.parentElement}return null}const r=["http:","https:"];function i(t){return t.length>0&&t.length<=63&&/^[a-z\d-]+$/i.test(t)&&!t.startsWith("-")&&!t.endsWith("-")}function s(t){return!!t&&("localhost"===t||function(t){const e=t.split(".");return 4===e.length&&e.every(t=>{if(!/^\d{1,3}$/.test(t))return!1;const e=Number(t);return e>=0&&e<=255})}(t)||function(t){if(!t.startsWith("[")||!t.endsWith("]"))return!1;const e=t.slice(1,-1);return e.includes(":")&&/^[\da-f:.]+$/i.test(e)}(t)||function(t){const e=t.split(".");return e.length>=2&&e.every(i)}(t))}function n(t){const e=function(t){const e=t.trim();return 0===e.length?"":e.startsWith("//")?`https:${e}`:/^[a-z][a-z\d+.-]*:\/\//i.test(e)?e:`https://${e}`}(t);if(0===e.length)return!1;try{const t=new URL(e);return r.includes(t.protocol)&&s(t.hostname)}catch(t){return!1}}function o(t){const e="FORM"===t.tagName?Array.from(t.elements):Array.from(t.querySelectorAll("input, select, textarea, button")),r=new FormData;return e.forEach(t=>{if(!(t instanceof HTMLInputElement||t instanceof HTMLSelectElement||t instanceof HTMLTextAreaElement||t instanceof HTMLButtonElement))return;if(!t.name||t.disabled||t.hasAttribute("data-no-serialize"))return;if(t instanceof HTMLButtonElement)return;const{name:e,type:i}=t;if("file"===i)if(t.multiple){const i=t.files;if(i)for(let t=0;t<i.length;t++)r.append(`${e}[${t}]`,i[t])}else{const i=t.files;i&&i[0]&&r.append(e,i[0])}else{let s;if("select-one"===i||"select-multiple"===i){const n=t;if("select-multiple"===i){Array.from(n.selectedOptions).map(t=>t.value).forEach((t,i)=>{r.append(`${e}[${i}]`,t)})}else s=n.value,s&&r.append(e,s)}else if("tel"===i)s=t.value.split(/[\s()-]/).join("").replace(/^8/,"+7");else if("checkbox"===i||"radio"===i){if(!t.checked)return;s=t.value}else if("date"===i){const e=t.getAttribute("data-date-format");s=e?function(t,e){const r=new Date(t),i=t=>t.toString().padStart(2,"0"),s=i(r.getDate()),n=i(r.getMonth()+1),o=r.getFullYear().toString();return e.replace("dd",s).replace("mm",n).replace("yyyy",o)}(t.value,e):t.value}else s=t.value;s&&"select-one"!==i&&"select-multiple"!==i&&r.append(e,s)}}),r}function a(t){if(Object.prototype.hasOwnProperty.call(t,"redirect-url"))if(t["redirect-url-delay"]){const e=Number(t["redirect-url-delay"]);setTimeout(()=>{window.location.href=t["redirect-url"]},e)}else window.location.href=t["redirect-url"];else if(Object.prototype.hasOwnProperty.call(t,"reload")){if(!0===t.reload)if(t["reload-delay"]){const e=Number(t["reload-delay"]);setTimeout(()=>{window.location.reload()},e)}else window.location.reload()}else if(Object.prototype.hasOwnProperty.call(t,"toast")){const e=t.toast;(Array.isArray(e)?e:[e]).forEach(t=>{window.showToast(t)})}else Object.prototype.hasOwnProperty.call(t,"error-toast")&&window.showToast(t["error-toast"])}function l(t){const e={};return new FormData(t).forEach((t,r)=>{e.hasOwnProperty(r)?(Array.isArray(e[r])||(e[r]=[e[r]]),e[r].push(t)):e[r]=t}),e}var c;const u=Symbol.for("FormFather.validators"),d=null!=(c=globalThis[u])?c:globalThis[u]=new Map;function h(t,e,r,{override:i=!1}={}){!d.has(t)||i?d.set(t,{fn:e,defaultMessage:r}):console.warn(`[FormFather] Validator "${t}" already exists; pass { override: true } to replace`)}const m=t=>d.get(t);function f(t,e,r){d.has(t)||d.set(t,{fn:e,defaultMessage:r})}function p(t,e){return"function"==typeof e?e(t):e||function(t){var e;if(!t||"object"!=typeof t)return;const r=t.issues||t.errors;if(Array.isArray(r)){const t=null==(e=r.find(t=>"string"==typeof(null==t?void 0:t.message)))?void 0:e.message;if(t)return t}return"string"==typeof t.message?t.message:void 0}(t)}function g(t,e={}){return async(r,i,s,n)=>{const o=e.mapValue?e.mapValue(r,i,s,n):r;try{if("function"==typeof t)return a=await t(o),l=p(void 0,e.message),"object"==typeof a&&null!==a?a:void 0===a||!!a||{valid:!1,message:l};if("function"==typeof t.safeParse){const r=await t.safeParse(o);return!!r.success||{valid:!1,message:p(r.error||r,e.message)}}if("function"==typeof t.parse)return await t.parse(o),!0}catch(t){return{valid:!1,message:p(t,e.message)}}var a,l;return{valid:!1,message:p(void 0,e.message)}}}function y(t,e){return async(r,i,s,n)=>!!await t(r,i,s,n)||{valid:!1,message:e}}var b;f("required",(t,e,r)=>{if(e&&e.type){if("checkbox"===e.type)return e.checked;if("radio"===e.type){const t=r.querySelectorAll(`input[type="radio"][name="${e.name}"]`);return Array.from(t).some(t=>t.checked)}}return t.trim().length>0},"Пустое значение"),f("email",t=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t),"Неверный формат"),f("tel",t=>/^\+7\d{10}$/.test(t),"Неверный формат"),f("url",t=>n(t),"Неверный формат"),f("not-numbers",t=>!/[0-9]/.test(t),"Неверный формат");const v=Symbol.for("formfather.shared"),E=null!=(b=globalThis[v])?b:globalThis[v]={defaultParams:{},defaultValidationSchema:void 0},S="_form";function w(t,e){return t[e]}function A(t){return Array.isArray(t)?0===t.length:null==t||0===String(t).trim().length}function F(t,e){return async r=>!!await t(r)||async function(t,e,r,i){const s="function"==typeof e?await e(t):e;return"string"==typeof s?{field:r,rule:i,message:s}:{field:r,rule:i,...s}}(r,e)}let V=0,$=0;const T=new WeakMap,_="data-form-father-describedby-id",x="data-form-father-added-describedby-id",k="data-form-father-summary",O={tel:{selector:'input[type="tel"]',rules:["tel"]},email:{selector:'input[type="email"]',rules:["email"]},required:{selector:"[required]",rules:["required"]},url:{selector:'input[type="url"]',rules:["url"]},"not-numbers":{selector:'input[data-validate="not-numbers"]',rules:["not-numbers"]}};class L{constructor(t,e={}){this.$submits=[],this.waitResponse=!1,this.inputs=null,this.fieldEventHandlers=[],this.liveValidationTimers=new WeakMap,this.fieldValidationTokens=new WeakMap,this.fieldValidationTokenCounter=0,this.errors=[],this._anim={duration:220,easing:"ease",paddingTopOpened:16,iconDuration:700},this.$el=t,this.options=e;this.config=Object.assign({},{onSubmit:()=>{},onResponse:()=>{},onResponseSuccess:()=>{},onResponseUnsuccess:()=>{},customTypeError:null,loaderColor:"#fff",showLoaderButton:!0,scrollToFirstErroredInput:!0,focusFirstErroredInput:!1,logging:!1,inputSelector:".input",inputWrapperSelector:".input-primary",validateOn:"submit",revalidateOn:["input","change"],validationDebounce:0,errorContainerAttribute:"data-error-container",validationStateAttribute:"data-form-father-state",ariaDescribeErrors:!0,errorIdPrefix:"form-father-error",errorSummary:!1,observeMutations:!1},L.defaultParams,e),this.$el?(this.$submits=this.findSubmitElements(),this.isCorrectArguments()&&(this.bindSubmitHandlers(),this.initialization(),this.bindFieldValidationEvents(),this.setupMutationObserver(),T.set(this.$el,this))):console.warn("Empty $el")}static get defaultParams(){return E.defaultParams}static set defaultParams(t){E.defaultParams=t}static get defaultValidationSchema(){var t;return null!=(t=E.defaultValidationSchema)?t:E.defaultValidationSchema=O}static set defaultValidationSchema(t){E.defaultValidationSchema=t}getOptions(){return this.options}static initAll(t="form[data-form-father]",e={}){return"undefined"==typeof document?[]:Array.from(document.querySelectorAll(t)).filter(t=>"FORM"===t.tagName).map(t=>{const r=T.get(t);return r?(r.updateOptions(e),r):new L(t,e)})}updateOptions(t){return this.options={...this.options,...t},this.config={...this.config,...t},this.refreshControls(),this.bindSubmitHandlers(),this.bindFieldValidationEvents(),this.setupMutationObserver(),this}getErrors(){return this.errors.map(t=>({...t}))}getValues(){return l(this.$el)}normalizeValueList(t){return Array.isArray(t)?t.map(String):[String(t)]}setValues(t){return this.getAllFields().forEach(e=>{if(!e.name||!Object.prototype.hasOwnProperty.call(t,e.name))return;const r=t[e.name];if(!(e instanceof HTMLInputElement&&"file"===e.type)){if(e instanceof HTMLInputElement&&"checkbox"===e.type)e.checked="boolean"==typeof r?r:this.normalizeValueList(r).includes(e.value);else if(e instanceof HTMLInputElement&&"radio"===e.type)e.checked=String(r)===e.value;else if(e instanceof HTMLSelectElement&&e.multiple){const t=this.normalizeValueList(r);Array.from(e.options).forEach(e=>{e.selected=t.includes(e.value)})}else e.value=null==r?"":String(r);e.dispatchEvent(new Event("input",{bubbles:!0})),e.dispatchEvent(new Event("change",{bubbles:!0}))}}),this}clearErrors(){return this.errors=[],this.getAllFields().forEach(t=>{this.hideError(t),this.clearFieldValidationState(t)}),this.hideErrorForm(),this.hideErrorSummary(),this}isErrorResponse(t){return!!t&&"object"==typeof t&&"string"==typeof t.name&&"string"==typeof t["error-msg"]}normalizeValidationIssues(t,e){return null==t||!0===t?[]:!1===t?[{message:"Некорректные данные формы",source:e}]:"string"==typeof t?[{message:t,source:e}]:Array.isArray(t)?t.flatMap(t=>this.normalizeValidationIssues(t,e)):this.isErrorResponse(t)?[{field:t.name,rule:e,message:t["error-msg"],source:e}]:"object"==typeof t&&"message"in t&&"string"==typeof t.message?[{...t,source:t.source||e}]:"object"==typeof t?Object.entries(t).map(([t,r])=>({field:t,rule:e,message:r,source:e})):[]}applyValidationIssue(t,e){const r=t.source||e,i=t.rule||r,s=t.field||S;if(s===S)return this.setErrorRecord(S,i,t.message,r),this.showErrorForm(t.message),null;const n=this.findFieldByName(s);return n?(this.showFieldError(n,t.message,r,i),n):(this.setErrorRecord(s,i,t.message,r),console.warn(`Не найдено поле с именем: ${s}, для вывода ошибки: ${t.message}`),null)}setErrors(t,e="server"){return this.normalizeValidationIssues(t,e).forEach(t=>{this.applyValidationIssue(t,e)}),this.syncErrorSummary(),this}async validateFormValidators(){const t=this.config.formValidators?Array.isArray(this.config.formValidators)?this.config.formValidators:[this.config.formValidators]:[],e=[];let r=!0;for(const i of t){const t=await i({form:this,formElement:this.$el,values:this.getValues(),errors:this.getErrors()}),s=this.normalizeValidationIssues(t,"client");0!==s.length&&(r=!1,s.forEach(t=>{const r=this.applyValidationIssue(t,"client");r&&e.push(r)}))}return{ok:r,erroredInputs:e}}reset(t={}){return this.$el.reset(),this.refreshControls(),!1!==t.clearErrors&&this.clearErrors(),this}static setDefaultParams(t){this.defaultParams={...this.defaultParams,...t}}refreshControls(){this.inputs=this.$el.querySelectorAll(this.config.inputSelector)}findSubmitElements(){return Array.from(this.$el.querySelectorAll('input[type="submit"], input[type="image"], button[type="submit"], button:not([type])'))}bindSubmitHandlers(){this._onSubmitClickHandler||(this._onSubmitClickHandler=async t=>{await this.handleSubmitEvent(t)}),this.$submits.forEach(t=>{t.removeEventListener("click",this._onSubmitClickHandler)}),this.$submits=this.findSubmitElements(),this.$submits.forEach(t=>{t.addEventListener("click",this._onSubmitClickHandler)})}initialization(){this.refreshControls(),this._onSubmitHandler=async t=>{await this.handleSubmitEvent(t)},this.$el.addEventListener("submit",this._onSubmitHandler)}async handleSubmitEvent(t){var e,r,i,s,n,o;t.preventDefault(),null==(r=(e=this.config).onBeforeValidate)||r.call(e,this);const a=await this.validate();return null==(s=(i=this.config).onAfterValidate)||s.call(i,a,this),a?this.submit():(null==(o=(n=this.config).onValidationError)||o.call(n,this.getErrors(),this),{success:!1,error:this.getErrors()})}normalizeTriggers(t,e){const r=null==t?e:Array.isArray(t)?t:[t];return Array.from(new Set(r))}clearFieldValidationEvents(){this.fieldEventHandlers.forEach(({$input:t,event:e,handler:r})=>{t.removeEventListener(e,r)}),this.fieldEventHandlers=[]}bindFieldValidationEvents(){this.clearFieldValidationEvents();const t=this.normalizeTriggers(this.config.validateOn,["submit"]).filter(t=>"submit"!==t),e=this.normalizeTriggers(this.config.revalidateOn,["input","change"]).filter(t=>"submit"!==t),r=Array.from(new Set([...t,...e]));if(0===r.length)return;this.getAllFields().forEach(e=>{r.forEach(r=>{const i=()=>{(t.includes(r)||this.hasFieldError(e))&&this.scheduleFieldValidation(e)};e.addEventListener(r,i),this.fieldEventHandlers.push({$input:e,event:r,handler:i})})})}scheduleFieldValidation(t){const e=this.liveValidationTimers.get(t);e&&clearTimeout(e);const r=Math.max(0,Number(this.config.validationDebounce)||0),i=()=>{this.validateField(t)};if(0===r)return void i();const s=setTimeout(i,r);this.liveValidationTimers.set(t,s)}setupMutationObserver(){var t;null==(t=this.mutationObserver)||t.disconnect(),this.mutationObserver=void 0,this.config.observeMutations&&"undefined"!=typeof MutationObserver&&(this.mutationObserver=new MutationObserver(()=>{this.refreshControls(),this.bindSubmitHandlers(),this.bindFieldValidationEvents()}),this.mutationObserver.observe(this.$el,{childList:!0,subtree:!0}))}getErrorContainer(t){const e=this.config.errorContainerAttribute||"data-error-container",r=t.getAttribute(e);if(!r)return null;try{return document.querySelector(r)}catch(t){return console.warn(`[FormFather] Invalid error container selector "${r}"`),null}}toSafeIdPart(t){return t.trim().toLowerCase().replace(/[^a-z0-9_-]+/gi,"-").replace(/^-+|-+$/g,"")||"field"}createErrorId(t){const e=this.config.errorIdPrefix||"form-father-error";return`${this.toSafeIdPart(e)}-${this.toSafeIdPart(this.getFieldKey(t))}-${++$}`}ensureElementId(t,e){return e.id||(e.id=t.getAttribute(_)||this.createErrorId(t)),e.id}addErrorDescription(t,e){e.hasAttribute("role")||e.setAttribute("role","alert");const r=this.ensureElementId(t,e);if(t.setAttribute(_,r),!1===this.config.ariaDescribeErrors)return;const i=(t.getAttribute("aria-describedby")||"").split(/\s+/).filter(Boolean);i.includes(r)||(t.setAttribute("aria-describedby",[...i,r].join(" ")),t.setAttribute(x,r))}removeErrorDescription(t){const e=t.getAttribute(x);if(e){const r=(t.getAttribute("aria-describedby")||"").split(/\s+/).filter(t=>t&&t!==e);r.length>0?t.setAttribute("aria-describedby",r.join(" ")):t.removeAttribute("aria-describedby")}t.removeAttribute(_),t.removeAttribute(x)}showError(t,r){t.setAttribute("aria-invalid","true");const i=this.getErrorContainer(t);if(i)return i.textContent=r,this.addErrorDescription(t,i),void i.removeAttribute("hidden");const s=e(t,this.config.inputWrapperSelector);if(!s)return void console.warn(`Не найдена обёртка для поля с именем: ${t.name||t.type}`);const n=s.showError;if("function"==typeof n){n.call(s,r);const e=s.querySelector("[data-form-father-error]");return void(e&&this.addErrorDescription(t,e))}s.classList.add("input__wrapper--error");let o=s.querySelector("[data-form-father-error]");o||(o=document.createElement("div"),o.setAttribute("data-form-father-error",""),o.setAttribute("role","alert"),t.insertAdjacentElement("afterend",o)),o.textContent=r,this.addErrorDescription(t,o)}showLoader(){this.$submits.forEach(t=>{if(t.classList.add("button--loading"),t.querySelector(".button__loader"))return;const e="form-father-loader-"+V++,r=`\n\t\t\t\t<svg height="38" viewBox="0 0 38 38" width="38" xmlns="http://www.w3.org/2000/svg">\n\t\t\t\t\t<defs>\n\t\t\t\t\t<linearGradient id="${e}" x1="8.042%" x2="65.682%" y1="0%" y2="23.865%">\n\t\t\t\t\t\t<stop offset="0%" stop-color="${this.config.loaderColor}" stop-opacity="0"/>\n\t\t\t\t\t\t<stop offset="63.146%" stop-color="${this.config.loaderColor}" stop-opacity=".631"/>\n\t\t\t\t\t\t<stop offset="100%" stop-color="${this.config.loaderColor}"/>\n\t\t\t\t\t</linearGradient>\n\t\t\t\t\t</defs>\n\t\t\t\t\t<g fill-rule="evenodd" fill="none">\n\t\t\t\t\t<g transform="translate(1 1)">\n\t\t\t\t\t\t<path d="M36 18c0-9.94-8.06-18-18-18" id="Oval-2" stroke-width="5" stroke="url(#${e})"></path>\n\t\t\t\t\t\t<circle cx="36" cy="18" fill="${this.config.loaderColor}" r="1"></circle>\n\t\t\t\t\t</g>\n\t\t\t\t\t</g>\n\t\t\t\t</svg>\n\t\t\t`,i=document.createElement("span");i.innerHTML=r,i.className="button__loader",t.insertAdjacentElement("afterbegin",i),requestAnimationFrame(()=>{i.setAttribute("data-showed","")})})}hideLoader(){this.$submits.forEach(t=>{t.classList.remove("button--loading");const e=t.querySelector(".button__loader");e&&(e.classList.remove("active"),e.removeAttribute("data-showed"),setTimeout(()=>{e.remove()},250))})}hideError(t){var r;const i=e(t,this.config.inputWrapperSelector)||t.parentElement,s=this.getErrorContainer(t);t.removeAttribute("aria-invalid"),this.removeErrorDescription(t),s&&(s.textContent="",s.setAttribute("hidden","")),null==(r=null==i?void 0:i.querySelector("[data-form-father-error]"))||r.remove(),null==i||i.classList.remove("input__wrapper--error"),i&&i.classList.contains("input__wrapper")&&i.getAttribute("data-type-error")}getErrorSummaryOptions(){return this.config.errorSummary?!0===this.config.errorSummary?{}:this.config.errorSummary:null}getErrorSummaryContainer(t,e){if(t.selector)try{return this.$el.querySelector(t.selector)||document.querySelector(t.selector)}catch(e){return console.warn(`[FormFather] Invalid error summary selector "${t.selector}"`),null}const r=this.$el.querySelector(`[${k}]`);if(r||!e)return r;const i=document.createElement("div");return i.setAttribute(k,""),this.$el.insertAdjacentElement("afterbegin",i),i}prepareErrorSummaryContainer(t){t.setAttribute(k,""),t.hasAttribute("role")||t.setAttribute("role","alert"),t.hasAttribute("tabindex")||t.setAttribute("tabindex","-1"),t.removeAttribute("hidden")}focusErrorSummary(t){try{t.focus({preventScroll:!0})}catch(e){t.focus()}}focusFieldFromSummary(t){"function"==typeof t.scrollIntoView&&t.scrollIntoView({behavior:"smooth",block:"center"});try{t.focus({preventScroll:!0})}catch(e){t.focus()}}renderDefaultErrorSummary(t,e,r){t.textContent="";const i=document.createElement("p");i.setAttribute("data-form-father-summary-title",""),i.textContent=r.title||"Проверьте поля формы";const s=document.createElement("ul");s.setAttribute("data-form-father-summary-list",""),e.forEach(t=>{const e=document.createElement("li"),r=t.field===S?null:this.findFieldByName(t.field);if(r){const i=document.createElement("button");i.type="button",i.setAttribute("data-form-father-summary-field",t.field),i.textContent=t.message,i.addEventListener("click",()=>this.focusFieldFromSummary(r)),e.appendChild(i)}else{const r=document.createElement("span");r.textContent=t.message,e.appendChild(r)}s.appendChild(e)}),t.append(i,s)}renderErrorSummary(t=this.getErrors()){var e;const r=this.getErrorSummaryOptions();if(!r)return;if(0===t.length)return void this.hideErrorSummary();const i=this.getErrorSummaryContainer(r,!0);if(!i)return;this.prepareErrorSummaryContainer(i);const s=null==(e=r.render)?void 0:e.call(r,t.map(t=>({...t})),this);s instanceof HTMLElement?i.replaceChildren(s):"string"==typeof s?i.innerHTML=s:r.render||this.renderDefaultErrorSummary(i,t,r),r.focus&&this.focusErrorSummary(i)}hideErrorSummary(){const t=this.getErrorSummaryOptions()||{},e=this.getErrorSummaryContainer(t,!1);e&&(e.replaceChildren(),e.setAttribute("hidden",""))}syncErrorSummary(){0!==this.errors.length?this.renderErrorSummary():this.hideErrorSummary()}scrollToFirstErroredInput(t){if(t.length>0){const e=Array.from(this.$el.querySelectorAll("input, textarea, select"));let r=null,i=1/0;if(t.forEach(t=>{const s=e.findIndex(e=>e===t);s<i&&(i=s,r=t)}),r){const t=r.getBoundingClientRect(),e=window.pageYOffset||document.documentElement.scrollTop,i=t.top+e-window.innerHeight/2;document.documentElement.setAttribute("data-now-scrolling",""),window.scrollTo({top:i,behavior:"smooth"}),setTimeout(()=>{document.documentElement.removeAttribute("data-now-scrolling")},800)}}}focusFirstErroredInput(t){var e;if(0===t.length)return;const r=Array.from(this.$el.querySelectorAll("input, textarea, select")),i=t.slice().sort((t,e)=>r.indexOf(t)-r.indexOf(e))[0];null==(e=null==i?void 0:i.focus)||e.call(i,{preventScroll:!0})}getValidationSchema(){return{...L.defaultValidationSchema||{},...this.config.validationSchema||{}}}getAllFields(t=this.$el){return Array.from(t.querySelectorAll("input, textarea, select"))}getFieldKey(t){return t.name||t.id||t.getAttribute("data-field")||t.type||"field"}hasFieldError(t){const e=this.getFieldKey(t);return"true"===t.getAttribute("aria-invalid")||this.errors.some(t=>t.field===e)}clearFieldErrorRecord(t){const e=this.getFieldKey(t);this.errors=this.errors.filter(t=>t.field!==e)}setErrorRecord(t,e,r,i){this.errors.push({field:t,rule:e,message:r,source:i})}getValidationStateAttribute(){return this.config.validationStateAttribute||"data-form-father-state"}setFieldValidationState(t,e){const r=this.getValidationStateAttribute();e?t.setAttribute(r,e):t.removeAttribute(r)}startFieldValidation(t){const e=++this.fieldValidationTokenCounter;return this.fieldValidationTokens.set(t,e),t.setAttribute("aria-busy","true"),this.setFieldValidationState(t,"validating"),e}isLatestFieldValidation(t,e){return null==e||this.fieldValidationTokens.get(t)===e}finishFieldValidation(t,e,r){this.isLatestFieldValidation(t,e)&&(t.removeAttribute("aria-busy"),this.setFieldValidationState(t,r?"valid":"invalid"))}clearFieldValidationState(t){this.fieldValidationTokens.delete(t),t.removeAttribute("aria-busy"),this.setFieldValidationState(t,null)}setValidationError(t,e,r,i){this.clearFieldErrorRecord(t),this.setErrorRecord(this.getFieldKey(t),e,r,i)}resolveField(t){return"string"!=typeof t?t:this.findFieldByName(t)}parseRuleString(t){const[e,...r]=t.split(":").map(t=>t.trim());return r.length>0?{rule:e,params:1===r.length?r[0]:r}:e}parseValidationAttribute(t){return(null!=t?t:"").split(/[,\s|]+/).map(t=>t.trim()).filter(Boolean).map(t=>this.parseRuleString(t))}getRuleName(t){return"string"==typeof t?t:t.rule}getRuleIdentity(t){return JSON.stringify(t)}collectRules(t,e=[],r=new Set){const i=[];t.hasAttribute("required")&&i.push("required"),i.push(...e);const s=this.parseValidationAttribute(t.getAttribute("data-validate")).filter(t=>{const e=this.getRuleName(t);return m(e)||!r.has(e)});i.push(...s),i.push(...this.parseValidationAttribute(t.getAttribute("data-custom-validate")));const n=[],o=new Set;for(const t of i){const e=this.getRuleName(t);if(!m(e)){console.warn(`[FormFather] Unknown validation rule "${e}"`);continue}const r=this.getRuleIdentity(t);o.has(r)||(o.add(r),n.push(t))}return n}getRuleMessage(t,e,r,i,s){const n=t.getAttribute(`data-error-${e}`);return s||n||r[e]||i}fieldMatchesSchemaDefinition(t,e,r){if(r.selector)try{return t.matches(r.selector)}catch(t){return console.warn(`[FormFather] Invalid validation selector "${r.selector}"`),!1}return this.parseValidationAttribute(t.getAttribute("data-validate")).some(t=>this.getRuleName(t)===e)}getFieldValidationConfig(t,e=this.getValidationSchema()){const r=[],i={};for(const[s,n]of Object.entries(e))this.fieldMatchesSchemaDefinition(t,s,n)&&(r.push(...n.rules),Object.assign(i,n.messages||{}));return{rules:this.collectRules(t,r,new Set(Object.keys(e))),messages:i}}getValidationFields(t,e){const r=new Set,i=t=>{Array.from(t).forEach(t=>{(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement||t instanceof HTMLSelectElement)&&r.add(t)})};for(const[s,n]of Object.entries(e))if(n.selector)try{i(t.querySelectorAll(n.selector))}catch(t){console.warn(`[FormFather] Invalid validation selector "${n.selector}"`)}else this.getAllFields(t).filter(t=>this.parseValidationAttribute(t.getAttribute("data-validate")).some(t=>this.getRuleName(t)===s)).forEach(t=>r.add(t));return i(t.querySelectorAll("[required], [data-validate], [data-custom-validate]")),Array.from(r)}async validateInput(t,e,r={},i=this.$el,s){if(t.hasAttribute("data-no-validate"))return!0;const n=t instanceof HTMLInputElement&&("checkbox"===t.type||"radio"===t.type)?!t.checked:0===t.value.trim().length,o=e.some(t=>"required"===this.getRuleName(t));if(n&&!o)return this.isLatestFieldValidation(t,s)?(this.clearFieldErrorRecord(t),this.hideError(t),!0):!this.hasFieldError(t);for(const n of e){const{rule:e,params:o}="string"==typeof n?{rule:n,params:void 0}:{rule:n.rule,params:n.params},a=m(e);if(!a)continue;const l=await a.fn(t.value,t,i,o),c="object"==typeof l&&null!==l,u=c?!0===l.valid:!!l;if(!this.isLatestFieldValidation(t,s))return!this.hasFieldError(t);if(c&&l.effect&&await l.effect({value:t.value,$input:t,$form:i,params:o}),!this.isLatestFieldValidation(t,s))return!this.hasFieldError(t);if(!u){const i=l,s=this.getRuleMessage(t,e,r,a.defaultMessage,c&&"string"==typeof i.message?i.message:void 0);return this.setValidationError(t,e,s,"client"),this.showError(t,s),!1}if(c&&l.stopOthers)return!0}return this.isLatestFieldValidation(t,s)?(this.clearFieldErrorRecord(t),this.hideError(t),!0):!this.hasFieldError(t)}async runFieldValidation(t,e,r={},i=this.$el){const s=this.startFieldValidation(t);try{const n=await this.validateInput(t,e,r,i,s);return this.finishFieldValidation(t,s,n),n}catch(e){throw this.isLatestFieldValidation(t,s)&&this.clearFieldValidationState(t),e}}getRadioGroup(t,e){return t instanceof HTMLInputElement&&"radio"===t.type&&t.name?Array.from(e.querySelectorAll('input[type="radio"]')).filter(e=>e.name===t.name):[]}showFieldError(t,e,r="manual",i=r){const s=this.resolveField(t);return s?(this.setValidationError(s,i,e,r),this.showError(s,e),s.removeAttribute("aria-busy"),this.setFieldValidationState(s,"invalid"),this.syncErrorSummary(),!0):(console.warn(`[FormFather] Field "${String(t)}" not found`),!1)}async validateField(t){const e=this.resolveField(t);if(!e)return console.warn(`[FormFather] Field "${String(t)}" not found`),!1;const{rules:r,messages:i}=this.getFieldValidationConfig(e),s=await this.runFieldValidation(e,r,i);return this.syncErrorSummary(),s}async validate(t=this.$el){const e=this.getValidationSchema(),r=[],i=new WeakSet;let s=!0;this.errors=[];for(const n of this.getValidationFields(t,e)){if(i.has(n))continue;const o=this.getRadioGroup(n,t);o.length>0?o.forEach(t=>i.add(t)):i.add(n);const{rules:a,messages:l}=this.getFieldValidationConfig(n,e);await this.runFieldValidation(n,a,l,t)||(s=!1,r.push(n))}const n=await this.validateFormValidators();n.ok||(s=!1,r.push(...n.erroredInputs));const o=r.filter(t=>!t.hasAttribute("data-no-error-scroll"));return!s&&this.config.scrollToFirstErroredInput&&this.scrollToFirstErroredInput(o),!s&&this.config.focusFirstErroredInput&&this.focusFirstErroredInput(o),s?this.hideErrorSummary():this.renderErrorSummary(),s}disableSubmit(){this.$submits.forEach(t=>{t.setAttribute("disabled","")})}enableSubmit(){this.$submits.forEach(t=>{t.removeAttribute("disabled")})}clearInputs(){var t;null==(t=this.inputs)||t.forEach(t=>{var e;const r=t;"radio"===r.type||"checkbox"===r.type?r.checked=!1:(r.value="",null==(e=r.parentElement)||e.classList.remove("filled"),r.dispatchEvent(new Event("input")))})}_prefersReducedMotion(){var t;return"undefined"!=typeof window&&(null==(t=window.matchMedia)?void 0:t.call(window,"(prefers-reduced-motion: reduce)").matches)}_canAnimate(t){return!(!t||!("animate"in t)||this._prefersReducedMotion())}_cancelAnimations(t){var e;const r=t;null==(e=null==r?void 0:r.getAnimations)||e.call(r).forEach(t=>t.cancel())}showErrorForm(t){var e;const r=this.$el.querySelectorAll(this.config.inputWrapperSelector),i=r[r.length-1];let s=this.$el.querySelector(".error-block-under-input__wrapper");!s||i&&i.nextElementSibling!==s?(s=document.createElement("div"),s.className="error-block-under-input__wrapper",s.innerHTML='\n <div class="error-block-under-input error-block-under-input--warning" style="display:grid;grid-template-columns:auto 1fr;gap:8px;align-items:start;">\n <div class="error-block-under-input__icon-wrapper" style="position:relative;width:20px;height:20px;">\n <span class="error-block-under-input__icon" style="display:block;width:20px;height:20px;"></span>\n <span class="error-block-under-input__icon error-block-under-input__icon--animated" style="display:block;position:absolute;inset:0;"></span>\n </div>\n <p class="error-block-under-input__text" style="margin:0;">\n <span class="error-block-under-input__main-text"></span>\n <span class="error-block-under-input__secondary-text"></span>\n </p>\n </div>\n ',Object.assign(s.style,{boxSizing:"border-box",overflow:"hidden",height:"0px",paddingTop:"0px",opacity:"0"}),i?i.insertAdjacentElement("afterend",s):this.$el.appendChild(s)):(s.style.boxSizing="border-box",s.style.overflow="hidden"),s.querySelector(".error-block-under-input__main-text").textContent=t;const n=s.querySelector(".error-block-under-input__secondary-text");n&&(n.textContent=""),null==(e=s.querySelector(".error-block-under-input"))||e.classList.remove("error-block-under-input--success");const o=s.style.height,a=s.style.paddingTop;s.style.height="auto",s.style.paddingTop=`${this._anim.paddingTopOpened}px`;const l=s.clientHeight;if(s.style.height=o||"0px",s.style.paddingTop=a||"0px",parseFloat(s.style.height)===l&&1===parseFloat(s.style.opacity||"1"))this._ringIcon(s);else{if(this._cancelAnimations(s),this._canAnimate(s)){s.animate([{height:`${parseFloat(s.style.height)||0}px`,paddingTop:`${parseFloat(s.style.paddingTop)||0}px`,opacity:parseFloat(s.style.opacity||"0")},{height:`${l}px`,paddingTop:`${this._anim.paddingTopOpened}px`,opacity:1}],{duration:this._anim.duration,easing:this._anim.easing,fill:"forwards"}).addEventListener("finish",()=>{s.style.height="auto",s.style.paddingTop=`${this._anim.paddingTopOpened}px`,s.style.opacity="1"})}else s.style.height="auto",s.style.paddingTop=`${this._anim.paddingTopOpened}px`,s.style.opacity="1";this._ringIcon(s)}this.$el.addEventListener("input",()=>this.hideErrorForm(),{passive:!0,once:!0})}hideErrorForm(){const t=this.$el.querySelectorAll(this.config.inputWrapperSelector),e=t[t.length-1],r=(null==e?void 0:e.nextElementSibling)||this.$el.querySelector(".error-block-under-input__wrapper");if(!r||!r.classList.contains("error-block-under-input__wrapper"))return;if((""===r.style.height||"auto"===r.style.height)&&(r.style.height=`${r.clientHeight}px`),r.style.paddingTop=r.style.paddingTop||`${this._anim.paddingTopOpened}px`,r.style.opacity=r.style.opacity||"1",this._cancelAnimations(r),this._canAnimate(r)){r.animate([{height:`${parseFloat(r.style.height)||r.clientHeight}px`,paddingTop:`${parseFloat(r.style.paddingTop)||this._anim.paddingTopOpened}px`,opacity:parseFloat(r.style.opacity||"1")},{height:"0px",paddingTop:"0px",opacity:0}],{duration:this._anim.duration,easing:this._anim.easing,fill:"forwards"}).addEventListener("finish",()=>{r.remove()})}else r.remove()}_ringIcon(t){const e=t.querySelector(".error-block-under-input__icon--animated");e&&(this._cancelAnimations(e),this._canAnimate(e)&&e.animate([{transform:"rotate(0deg) scale(1)",opacity:0},{transform:"rotate(-8deg) scale(1.05)",opacity:1,offset:.15},{transform:"rotate(8deg) scale(1.05)",opacity:1,offset:.3},{transform:"rotate(-6deg) scale(1.03)",opacity:1,offset:.45},{transform:"rotate(6deg) scale(1.03)",opacity:1,offset:.6},{transform:"rotate(0deg) scale(1)",opacity:0}],{duration:this._anim.iconDuration,easing:"ease"}))}destroy(){var t;this._onSubmitHandler&&(this.$el.removeEventListener("submit",this._onSubmitHandler),this._onSubmitHandler=void 0),this._onSubmitClickHandler&&(this.$submits.forEach(t=>{t.removeEventListener("click",this._onSubmitClickHandler)}),this._onSubmitClickHandler=void 0),this.clearFieldValidationEvents(),null==(t=this.mutationObserver)||t.disconnect(),this.mutationObserver=void 0;const e=this.$el.querySelector(".error-block-under-input__wrapper");e&&(this._cancelAnimations(e),e.remove()),this.hideErrorSummary(),this.$submits.forEach(t=>{t.classList.remove("button--loading");const e=t.querySelector(".button__loader");e&&e.remove()});this.$el.querySelectorAll("input, textarea, select").forEach(t=>{this.hideError(t),this.clearFieldValidationState(t)}),document.documentElement.removeAttribute("data-now-scrolling"),this.inputs=null,this.$submits=[],this.errors=[],this.waitResponse=!1,T.delete(this.$el)}isCorrectArguments(){return"FORM"!==this.$el.tagName?(console.warn("$el не является тегом form"),!1):0!==this.$submits.length||(console.warn("В форме нет кнопки с типом submit"),!1)}formDataToObject(t){const e={};return t.forEach((t,r)=>{Object.prototype.hasOwnProperty.call(e,r)?(Array.isArray(e[r])||(e[r]=[e[r]]),e[r].push(t)):e[r]=t}),e}appendFormDataValue(t,e,r){null!=r&&(Array.isArray(r)?r.forEach((r,i)=>{this.appendFormDataValue(t,`${e}[${i}]`,r)}):r instanceof Blob?t.append(e,r):t.append(e,"object"==typeof r?JSON.stringify(r):String(r)))}objectToFormData(t){const e=new FormData;return Object.entries(t).forEach(([t,r])=>{this.appendFormDataValue(e,t,r)}),e}stringifyFormDataValue(t){return"string"==typeof t?t:t.name}formDataToUrlSearchParams(t){const e=new URLSearchParams;return t.forEach((t,r)=>{e.append(r,this.stringifyFormDataValue(t))}),e}formDataToTextBody(t){return Array.from(t).map(([t,e])=>`${encodeURIComponent(t)}=${encodeURIComponent(this.stringifyFormDataValue(e))}`).join("\n")}isBodylessMethod(t){return"GET"===t||"HEAD"===t}appendQueryString(t,e){const r=e.toString();if(!r)return t;const i=t.includes("?")?"&":"?";return`${t}${i}${r}`}findFieldByName(t){return Array.from(this.$el.querySelectorAll("input, textarea, select")).find(e=>e.name===t)||null}async parseResponseBody(t){try{const e=await t.json();return e&&"object"==typeof e?e:{success:!1,error:!0,"error-msg":"Некорректный ответ сервера"}}catch(t){return{success:!1,error:!0,"error-msg":"Некорректный ответ сервера"}}}handleUnsuccessfulResponse(t){if(!0===t.error){if(t["error-msg"]&&(this.setErrorRecord(S,"server",t["error-msg"],"server"),this.showErrorForm(t["error-msg"])),t.errors){const e=[];t.errors.forEach(t=>{const r=this.findFieldByName(t.name);r?(e.push(r),this.setValidationError(r,"server",t["error-msg"],"server"),this.showError(r,t["error-msg"]),r.removeAttribute("aria-busy"),this.setFieldValidationState(r,"invalid")):(this.setErrorRecord(t.name,"server",t["error-msg"],"server"),console.warn(`Не найдено поле с именем: ${t.name}, для вывода ошибки: ${t["error-msg"]}`))}),!0===this.config.scrollToFirstErroredInput&&this.scrollToFirstErroredInput(e),!0===this.config.focusFirstErroredInput&&this.focusFirstErroredInput(e)}this.syncErrorSummary()}}async sendData(){var t;const e=this.$el.getAttribute("action")||"",r=(null==(t=this.$el.getAttribute("method"))?void 0:t.toUpperCase())||"POST",i=this.$el.getAttribute("enctype")||"application/x-www-form-urlencoded";let s=null,n={};const a=o(this.$el);let l=this.formDataToObject(a);if(this.config.logging)for(const t of a.keys())console.log(a.get(t));const c="function"==typeof this.config.wrapData;"function"==typeof this.config.wrapData&&(l=this.config.wrapData(l));const u=c?this.objectToFormData(l):a;if(this.isBodylessMethod(r))return fetch(this.appendQueryString(e,this.formDataToUrlSearchParams(u)),{method:r,headers:n});switch(i){case"application/x-www-form-urlencoded":s=this.formDataToUrlSearchParams(u).toString(),n["Content-Type"]="application/x-www-form-urlencoded";break;case"multipart/form-data":s=u;break;case"text/plain":s=this.formDataToTextBody(u),n["Content-Type"]="text/plain";break;case"application/json":s=JSON.stringify(l),n["Content-Type"]="application/json";break;default:console.warn(`Неизвестный enctype: ${i}. Используется application/x-www-form-urlencoded.`),s=this.formDataToUrlSearchParams(u).toString(),n["Content-Type"]="application/x-www-form-urlencoded"}return fetch(e,{method:r,body:s,headers:n})}async submit(){var t,e,r,i,s,n,o,l,c,u,d,h;if(!this.waitResponse){let m=!1;const f=()=>{m||(this.waitResponse=!1,!0===this.config.showLoaderButton&&this.hideLoader(),m=!0)};try{if(!1===await(null==(e=(t=this.config).onBeforeSubmit)?void 0:e.call(t,this)))return{success:!1};!0===this.config.showLoaderButton&&this.showLoader(),null==(i=(r=this.config).onSubmit)||i.call(r,this),this.waitResponse=!0;const d=await this.sendData(),h=await this.parseResponseBody(d);return f(),null==(n=(s=this.config).onResponse)||n.call(s,h,this),d.ok&&!0===h.success?null==(l=(o=this.config).onResponseSuccess)||l.call(o,h,this):(null==(u=(c=this.config).onResponseUnsuccess)||u.call(c,h,this),this.handleUnsuccessfulResponse(h)),a(h),{success:d.ok&&!0===h.success,response:d,responseBody:h}}catch(t){throw f(),null==(h=(d=this.config).onSubmitError)||h.call(d,t,this),t}}}}return t.FORM_ERROR_FIELD=S,t.blockScrollBody=function(){window.lastBlockScrollPosition=window.pageYOffset,document.body.style.top=-window.lastBlockScrollPosition+"px",document.body.classList.add("body--block-scroll")},t.closest=e,t.createFieldValidator=y,t.createFormValidator=F,t.createLengthValidator=function(t,e){return y(e=>{const r=!1===t.trim?e:e.trim();return 0===r.length&&!0===t.emptyIsValid||!("number"==typeof t.min&&r.length<t.min)&&!("number"==typeof t.max&&r.length>t.max)},e)},t.createPatternValidator=function(t,e){return y(e=>(t.lastIndex=0,t.test(e)),e)},t.createSchemaValidator=g,t.dateOrder=function(t,e,r="Дата окончания должна быть позже даты начала",i="date-order"){return F(({values:r})=>{const i=w(r,t),s=w(r,e);return!(!A(i)&&!A(s))||Date.parse(String(i))<=Date.parse(String(s))},{field:e,rule:i,message:r})},t.default=L,t.getAllValidators=function(){return new Map(d)},t.getValidator=m,t.isEmailValid=function(t){return/^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/iu.test(t)},t.isPhoneValid=function(t){const e=[];return Array.from(t).forEach(t=>{(Number(t)||0===Number(t))&&e.push(t)}),!(e.length<11||e.length<12&&"+"===t[1])},t.isUrlValid=n,t.parseCommonResponseProperties=a,t.registerFieldValidator=function(t,e,r,i={}){h(t,y(e),r,i)},t.registerSchemaValidator=function(t,e,r,i={}){const{override:s,...n}=i;h(t,g(e,n),r,{override:s})},t.registerValidator=h,t.requiredIf=function(t,e,r="Поле обязательно",i="conditional-required"){return F(async r=>!await e(r)||!A(w(r.values,t)),{field:t,rule:i,message:r})},t.sameAsField=function(t,e,r="Значения не совпадают",i="same-as-field"){return F(({values:r})=>w(r,t)===w(r,e),{field:t,rule:i,message:r})},t.serializeFormToJSON=l,t.serializeToFormData=o,t.unblockScrollBody=function(){document.body.classList.remove("body--block-scroll"),document.body.style.top="",window.scrollTo(0,window.lastBlockScrollPosition||0)},Object.defineProperty(t,"__esModule",{value:!0}),t}({});
|
|
1
|
+
var FormFather=function(t){"use strict";function e(t,e){if(null==t)return null;let r=t;for(;r;){if(r.matches(e))return r;r=r.parentElement}return null}const r=["http:","https:"];function i(t){return t.length>0&&t.length<=63&&/^[a-z\d-]+$/i.test(t)&&!t.startsWith("-")&&!t.endsWith("-")}function s(t){return!!t&&("localhost"===t||function(t){const e=t.split(".");return 4===e.length&&e.every(t=>{if(!/^\d{1,3}$/.test(t))return!1;const e=Number(t);return e>=0&&e<=255})}(t)||function(t){if(!t.startsWith("[")||!t.endsWith("]"))return!1;const e=t.slice(1,-1);return e.includes(":")&&/^[\da-f:.]+$/i.test(e)}(t)||function(t){const e=t.split(".");return e.length>=2&&e.every(i)}(t))}function n(t){const e=function(t){const e=t.trim();return 0===e.length?"":e.startsWith("//")?`https:${e}`:/^[a-z][a-z\d+.-]*:\/\//i.test(e)?e:`https://${e}`}(t);if(0===e.length)return!1;try{const t=new URL(e);return r.includes(t.protocol)&&s(t.hostname)}catch(t){return!1}}function o(t){const e="FORM"===t.tagName?Array.from(t.elements):Array.from(t.querySelectorAll("input, select, textarea, button")),r=new FormData;return e.forEach(t=>{if(!(t instanceof HTMLInputElement||t instanceof HTMLSelectElement||t instanceof HTMLTextAreaElement||t instanceof HTMLButtonElement))return;if(!t.name||t.disabled||t.hasAttribute("data-no-serialize"))return;if(t instanceof HTMLButtonElement)return;const{name:e,type:i}=t;if("file"===i)if(t.multiple){const i=t.files;if(i)for(let t=0;t<i.length;t++)r.append(`${e}[${t}]`,i[t])}else{const i=t.files;i&&i[0]&&r.append(e,i[0])}else{let s;if("select-one"===i||"select-multiple"===i){const n=t;if("select-multiple"===i){Array.from(n.selectedOptions).map(t=>t.value).forEach((t,i)=>{r.append(`${e}[${i}]`,t)})}else s=n.value,s&&r.append(e,s)}else if("tel"===i)s=t.value.split(/[\s()-]/).join("").replace(/^8/,"+7");else if("checkbox"===i||"radio"===i){if(!t.checked)return;s=t.value}else if("date"===i){const e=t.getAttribute("data-date-format");s=e?function(t,e){const r=new Date(t),i=t=>t.toString().padStart(2,"0"),s=i(r.getDate()),n=i(r.getMonth()+1),o=r.getFullYear().toString();return e.replace("dd",s).replace("mm",n).replace("yyyy",o)}(t.value,e):t.value}else s=t.value;s&&"select-one"!==i&&"select-multiple"!==i&&r.append(e,s)}}),r}function a(t){if(Object.prototype.hasOwnProperty.call(t,"redirect-url"))if(t["redirect-url-delay"]){const e=Number(t["redirect-url-delay"]);setTimeout(()=>{window.location.href=t["redirect-url"]},e)}else window.location.href=t["redirect-url"];else if(Object.prototype.hasOwnProperty.call(t,"reload")){if(!0===t.reload)if(t["reload-delay"]){const e=Number(t["reload-delay"]);setTimeout(()=>{window.location.reload()},e)}else window.location.reload()}else if(Object.prototype.hasOwnProperty.call(t,"toast")){const e=t.toast;(Array.isArray(e)?e:[e]).forEach(t=>{window.showToast(t)})}else Object.prototype.hasOwnProperty.call(t,"error-toast")&&window.showToast(t["error-toast"])}function l(t){const e={};return new FormData(t).forEach((t,r)=>{e.hasOwnProperty(r)?(Array.isArray(e[r])||(e[r]=[e[r]]),e[r].push(t)):e[r]=t}),e}var c;const u=Symbol.for("FormFather.validators"),d=null!=(c=globalThis[u])?c:globalThis[u]=new Map;function h(t,e,r,{override:i=!1}={}){!d.has(t)||i?d.set(t,{fn:e,defaultMessage:r}):console.warn(`[FormFather] Validator "${t}" already exists; pass { override: true } to replace`)}const m=t=>d.get(t);function f(t,e,r){d.has(t)||d.set(t,{fn:e,defaultMessage:r})}function p(t,e){return"function"==typeof e?e(t):e||function(t){var e;if(!t||"object"!=typeof t)return;const r=t.issues||t.errors;if(Array.isArray(r)){const t=null==(e=r.find(t=>"string"==typeof(null==t?void 0:t.message)))?void 0:e.message;if(t)return t}return"string"==typeof t.message?t.message:void 0}(t)}function g(t,e={}){return async(r,i,s,n)=>{const o=e.mapValue?e.mapValue(r,i,s,n):r;try{if("function"==typeof t)return a=await t(o),l=p(void 0,e.message),"object"==typeof a&&null!==a?a:void 0===a||!!a||{valid:!1,message:l};if("function"==typeof t.safeParse){const r=await t.safeParse(o);return!!r.success||{valid:!1,message:p(r.error||r,e.message)}}if("function"==typeof t.parse)return await t.parse(o),!0}catch(t){return{valid:!1,message:p(t,e.message)}}var a,l;return{valid:!1,message:p(void 0,e.message)}}}function y(t,e){return async(r,i,s,n)=>!!await t(r,i,s,n)||{valid:!1,message:e}}var b;f("required",(t,e,r)=>{if(e&&e.type){if("checkbox"===e.type)return e.checked;if("radio"===e.type){const t=r.querySelectorAll(`input[type="radio"][name="${e.name}"]`);return Array.from(t).some(t=>t.checked)}}return t.trim().length>0},"Пустое значение"),f("email",t=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t),"Неверный формат"),f("tel",t=>/^\+7\d{10}$/.test(t),"Неверный формат"),f("url",t=>n(t),"Неверный формат"),f("not-numbers",t=>!/[0-9]/.test(t),"Неверный формат");const v=Symbol.for("formfather.shared"),E=null!=(b=globalThis[v])?b:globalThis[v]={defaultParams:{},defaultValidationSchema:void 0},S="_form";function w(t,e){return t[e]}function A(t){return Array.isArray(t)?0===t.length:null==t||0===String(t).trim().length}function F(t,e){return async r=>!!await t(r)||async function(t,e,r,i){const s="function"==typeof e?await e(t):e;return"string"==typeof s?{field:r,rule:i,message:s}:{field:r,rule:i,...s}}(r,e)}let V=0,$=0;const T=new WeakMap,_="data-form-father-describedby-id",x="data-form-father-added-describedby-id",k="data-form-father-summary",O={tel:{selector:'input[type="tel"]',rules:["tel"]},email:{selector:'input[type="email"]',rules:["email"]},required:{selector:"[required]",rules:["required"]},url:{selector:'input[type="url"]',rules:["url"]},"not-numbers":{selector:'input[data-validate="not-numbers"]',rules:["not-numbers"]}};class L{constructor(t,e={}){this.$submits=[],this.waitResponse=!1,this.inputs=null,this.fieldEventHandlers=[],this.liveValidationTimers=new WeakMap,this.fieldValidationTokens=new WeakMap,this.fieldValidationTokenCounter=0,this.errors=[],this._anim={duration:220,easing:"ease",paddingTopOpened:16,iconDuration:700},this.$el=t,this.options=e;this.config=Object.assign({},{onSubmit:()=>{},onResponse:()=>{},onResponseSuccess:()=>{},onResponseUnsuccess:()=>{},customTypeError:null,loaderColor:"#fff",showLoaderButton:!0,scrollToFirstErroredInput:!0,focusFirstErroredInput:!1,logging:!1,inputSelector:".input",inputWrapperSelector:".input-primary",validateOn:"submit",revalidateOn:["input","change"],validationDebounce:0,errorContainerAttribute:"data-error-container",validationStateAttribute:"data-form-father-state",ariaDescribeErrors:!0,errorIdPrefix:"form-father-error",errorSummary:!1,observeMutations:!1},L.defaultParams,e),this.$el?(this.$submits=this.findSubmitElements(),this.isCorrectArguments()&&(this.bindSubmitHandlers(),this.initialization(),this.bindFieldValidationEvents(),this.setupMutationObserver(),T.set(this.$el,this))):console.warn("Empty $el")}static get defaultParams(){return E.defaultParams}static set defaultParams(t){E.defaultParams=t}static get defaultValidationSchema(){var t;return null!=(t=E.defaultValidationSchema)?t:E.defaultValidationSchema=O}static set defaultValidationSchema(t){E.defaultValidationSchema=t}getOptions(){return this.options}static initAll(t="form[data-form-father]",e={}){return"undefined"==typeof document?[]:Array.from(document.querySelectorAll(t)).filter(t=>"FORM"===t.tagName).map(t=>{const r=T.get(t);return r?(r.updateOptions(e),r):new L(t,e)})}updateOptions(t){return this.options={...this.options,...t},this.config={...this.config,...t},this.refreshControls(),this.bindSubmitHandlers(),this.bindFieldValidationEvents(),this.setupMutationObserver(),this}getErrors(){return this.errors.map(t=>({...t}))}getValues(){return l(this.$el)}normalizeValueList(t){return Array.isArray(t)?t.map(String):[String(t)]}setValues(t){return this.getAllFields().forEach(e=>{if(!e.name||!Object.prototype.hasOwnProperty.call(t,e.name))return;const r=t[e.name];if(!(e instanceof HTMLInputElement&&"file"===e.type)){if(e instanceof HTMLInputElement&&"checkbox"===e.type)e.checked="boolean"==typeof r?r:this.normalizeValueList(r).includes(e.value);else if(e instanceof HTMLInputElement&&"radio"===e.type)e.checked=String(r)===e.value;else if(e instanceof HTMLSelectElement&&e.multiple){const t=this.normalizeValueList(r);Array.from(e.options).forEach(e=>{e.selected=t.includes(e.value)})}else e.value=null==r?"":String(r);e.dispatchEvent(new Event("input",{bubbles:!0})),e.dispatchEvent(new Event("change",{bubbles:!0}))}}),this}clearErrors(){return this.errors=[],this.getAllFields().forEach(t=>{this.hideError(t),this.clearFieldValidationState(t)}),this.hideErrorForm(),this.hideErrorSummary(),this}isErrorResponse(t){return!!t&&"object"==typeof t&&"string"==typeof t.name&&"string"==typeof t["error-msg"]}normalizeValidationIssues(t,e){return null==t||!0===t?[]:!1===t?[{message:"Некорректные данные формы",source:e}]:"string"==typeof t?[{message:t,source:e}]:Array.isArray(t)?t.flatMap(t=>this.normalizeValidationIssues(t,e)):this.isErrorResponse(t)?[{field:t.name,rule:e,message:t["error-msg"],source:e}]:"object"==typeof t&&"message"in t&&"string"==typeof t.message?[{...t,source:t.source||e}]:"object"==typeof t?Object.entries(t).map(([t,r])=>({field:t,rule:e,message:r,source:e})):[]}applyValidationIssue(t,e){const r=t.source||e,i=t.rule||r,s=t.field||S;if(s===S)return this.setErrorRecord(S,i,t.message,r),this.showErrorForm(t.message),null;const n=this.findFieldByName(s);return n?(this.showFieldError(n,t.message,r,i),n):(this.setErrorRecord(s,i,t.message,r),console.warn(`Не найдено поле с именем: ${s}, для вывода ошибки: ${t.message}`),null)}setErrors(t,e="server"){return this.normalizeValidationIssues(t,e).forEach(t=>{this.applyValidationIssue(t,e)}),this.syncErrorSummary(),this}async validateFormValidators(){const t=this.config.formValidators?Array.isArray(this.config.formValidators)?this.config.formValidators:[this.config.formValidators]:[],e=[];let r=!0;for(const i of t){const t=await i({form:this,formElement:this.$el,values:this.getValues(),errors:this.getErrors()}),s=this.normalizeValidationIssues(t,"client");0!==s.length&&(r=!1,s.forEach(t=>{const r=this.applyValidationIssue(t,"client");r&&e.push(r)}))}return{ok:r,erroredInputs:e}}reset(t={}){return this.$el.reset(),this.refreshControls(),!1!==t.clearErrors&&this.clearErrors(),this}static setDefaultParams(t){this.defaultParams={...this.defaultParams,...t}}refreshControls(){this.inputs=this.$el.querySelectorAll(this.config.inputSelector)}findSubmitElements(){return Array.from(this.$el.querySelectorAll('input[type="submit"], input[type="image"], button[type="submit"], button:not([type])'))}bindSubmitHandlers(){this._onSubmitClickHandler||(this._onSubmitClickHandler=async t=>{await this.handleSubmitEvent(t)}),this.$submits.forEach(t=>{t.removeEventListener("click",this._onSubmitClickHandler)}),this.$submits=this.findSubmitElements(),this.$submits.forEach(t=>{t.addEventListener("click",this._onSubmitClickHandler)})}initialization(){this.refreshControls(),this._onSubmitHandler=async t=>{await this.handleSubmitEvent(t)},this.$el.addEventListener("submit",this._onSubmitHandler)}async handleSubmitEvent(t){var e,r,i,s,n,o;t.preventDefault(),null==(r=(e=this.config).onBeforeValidate)||r.call(e,this);const a=await this.validate();return null==(s=(i=this.config).onAfterValidate)||s.call(i,a,this),a?this.submit():(null==(o=(n=this.config).onValidationError)||o.call(n,this.getErrors(),this),{success:!1,error:this.getErrors()})}normalizeTriggers(t,e){const r=null==t?e:Array.isArray(t)?t:[t];return Array.from(new Set(r))}clearFieldValidationEvents(){this.fieldEventHandlers.forEach(({$input:t,event:e,handler:r})=>{t.removeEventListener(e,r)}),this.fieldEventHandlers=[]}bindFieldValidationEvents(){this.clearFieldValidationEvents();const t=this.normalizeTriggers(this.config.validateOn,["submit"]).filter(t=>"submit"!==t),e=this.normalizeTriggers(this.config.revalidateOn,["input","change"]).filter(t=>"submit"!==t),r=Array.from(new Set([...t,...e]));if(0===r.length)return;this.getAllFields().forEach(e=>{r.forEach(r=>{const i=()=>{(t.includes(r)||this.hasFieldError(e))&&this.scheduleFieldValidation(e)};e.addEventListener(r,i),this.fieldEventHandlers.push({$input:e,event:r,handler:i})})})}scheduleFieldValidation(t){const e=this.liveValidationTimers.get(t);e&&clearTimeout(e);const r=Math.max(0,Number(this.config.validationDebounce)||0),i=()=>{this.validateField(t)};if(0===r)return void i();const s=setTimeout(i,r);this.liveValidationTimers.set(t,s)}setupMutationObserver(){var t;null==(t=this.mutationObserver)||t.disconnect(),this.mutationObserver=void 0,this.config.observeMutations&&"undefined"!=typeof MutationObserver&&(this.mutationObserver=new MutationObserver(()=>{this.refreshControls(),this.bindSubmitHandlers(),this.bindFieldValidationEvents()}),this.mutationObserver.observe(this.$el,{childList:!0,subtree:!0}))}getErrorContainer(t){const e=this.config.errorContainerAttribute||"data-error-container",r=t.getAttribute(e);if(!r)return null;try{return document.querySelector(r)}catch(t){return console.warn(`[FormFather] Invalid error container selector "${r}"`),null}}toSafeIdPart(t){const e=t.trim().toLowerCase().replace(/[^a-z0-9_-]+/gi,"-");let r=0,i=e.length;for(;r<i&&"-"===e[r];)r+=1;for(;i>r&&"-"===e[i-1];)i-=1;return e.slice(r,i)||"field"}createErrorId(t){const e=this.config.errorIdPrefix||"form-father-error";return`${this.toSafeIdPart(e)}-${this.toSafeIdPart(this.getFieldKey(t))}-${++$}`}ensureElementId(t,e){return e.id||(e.id=t.getAttribute(_)||this.createErrorId(t)),e.id}addErrorDescription(t,e){e.hasAttribute("role")||e.setAttribute("role","alert");const r=this.ensureElementId(t,e);if(t.setAttribute(_,r),!1===this.config.ariaDescribeErrors)return;const i=(t.getAttribute("aria-describedby")||"").split(/\s+/).filter(Boolean);i.includes(r)||(t.setAttribute("aria-describedby",[...i,r].join(" ")),t.setAttribute(x,r))}removeErrorDescription(t){const e=t.getAttribute(x);if(e){const r=(t.getAttribute("aria-describedby")||"").split(/\s+/).filter(t=>t&&t!==e);r.length>0?t.setAttribute("aria-describedby",r.join(" ")):t.removeAttribute("aria-describedby")}t.removeAttribute(_),t.removeAttribute(x)}showError(t,r){t.setAttribute("aria-invalid","true");const i=this.getErrorContainer(t);if(i)return i.textContent=r,this.addErrorDescription(t,i),void i.removeAttribute("hidden");const s=e(t,this.config.inputWrapperSelector);if(!s)return void console.warn(`Не найдена обёртка для поля с именем: ${t.name||t.type}`);const n=s.showError;if("function"==typeof n){n.call(s,r);const e=s.querySelector("[data-form-father-error]");return void(e&&this.addErrorDescription(t,e))}s.classList.add("input__wrapper--error");let o=s.querySelector("[data-form-father-error]");o||(o=document.createElement("div"),o.setAttribute("data-form-father-error",""),o.setAttribute("role","alert"),t.insertAdjacentElement("afterend",o)),o.textContent=r,this.addErrorDescription(t,o)}showLoader(){this.$submits.forEach(t=>{if(t.classList.add("button--loading"),t.querySelector(".button__loader"))return;const e="form-father-loader-"+V++,r=`\n\t\t\t\t<svg height="38" viewBox="0 0 38 38" width="38" xmlns="http://www.w3.org/2000/svg">\n\t\t\t\t\t<defs>\n\t\t\t\t\t<linearGradient id="${e}" x1="8.042%" x2="65.682%" y1="0%" y2="23.865%">\n\t\t\t\t\t\t<stop offset="0%" stop-color="${this.config.loaderColor}" stop-opacity="0"/>\n\t\t\t\t\t\t<stop offset="63.146%" stop-color="${this.config.loaderColor}" stop-opacity=".631"/>\n\t\t\t\t\t\t<stop offset="100%" stop-color="${this.config.loaderColor}"/>\n\t\t\t\t\t</linearGradient>\n\t\t\t\t\t</defs>\n\t\t\t\t\t<g fill-rule="evenodd" fill="none">\n\t\t\t\t\t<g transform="translate(1 1)">\n\t\t\t\t\t\t<path d="M36 18c0-9.94-8.06-18-18-18" id="Oval-2" stroke-width="5" stroke="url(#${e})"></path>\n\t\t\t\t\t\t<circle cx="36" cy="18" fill="${this.config.loaderColor}" r="1"></circle>\n\t\t\t\t\t</g>\n\t\t\t\t\t</g>\n\t\t\t\t</svg>\n\t\t\t`,i=document.createElement("span");i.innerHTML=r,i.className="button__loader",t.insertAdjacentElement("afterbegin",i),requestAnimationFrame(()=>{i.setAttribute("data-showed","")})})}hideLoader(){this.$submits.forEach(t=>{t.classList.remove("button--loading");const e=t.querySelector(".button__loader");e&&(e.classList.remove("active"),e.removeAttribute("data-showed"),setTimeout(()=>{e.remove()},250))})}hideError(t){var r;const i=e(t,this.config.inputWrapperSelector)||t.parentElement,s=this.getErrorContainer(t);t.removeAttribute("aria-invalid"),this.removeErrorDescription(t),s&&(s.textContent="",s.setAttribute("hidden","")),null==(r=null==i?void 0:i.querySelector("[data-form-father-error]"))||r.remove(),null==i||i.classList.remove("input__wrapper--error"),i&&i.classList.contains("input__wrapper")&&i.getAttribute("data-type-error")}getErrorSummaryOptions(){return this.config.errorSummary?!0===this.config.errorSummary?{}:this.config.errorSummary:null}getErrorSummaryContainer(t,e){if(t.selector)try{return this.$el.querySelector(t.selector)||document.querySelector(t.selector)}catch(e){return console.warn(`[FormFather] Invalid error summary selector "${t.selector}"`),null}const r=this.$el.querySelector(`[${k}]`);if(r||!e)return r;const i=document.createElement("div");return i.setAttribute(k,""),this.$el.insertAdjacentElement("afterbegin",i),i}prepareErrorSummaryContainer(t){t.setAttribute(k,""),t.hasAttribute("role")||t.setAttribute("role","alert"),t.hasAttribute("tabindex")||t.setAttribute("tabindex","-1"),t.removeAttribute("hidden")}focusErrorSummary(t){try{t.focus({preventScroll:!0})}catch(e){t.focus()}}focusFieldFromSummary(t){"function"==typeof t.scrollIntoView&&t.scrollIntoView({behavior:"smooth",block:"center"});try{t.focus({preventScroll:!0})}catch(e){t.focus()}}renderDefaultErrorSummary(t,e,r){t.textContent="";const i=document.createElement("p");i.setAttribute("data-form-father-summary-title",""),i.textContent=r.title||"Проверьте поля формы";const s=document.createElement("ul");s.setAttribute("data-form-father-summary-list",""),e.forEach(t=>{const e=document.createElement("li"),r=t.field===S?null:this.findFieldByName(t.field);if(r){const i=document.createElement("button");i.type="button",i.setAttribute("data-form-father-summary-field",t.field),i.textContent=t.message,i.addEventListener("click",()=>this.focusFieldFromSummary(r)),e.appendChild(i)}else{const r=document.createElement("span");r.textContent=t.message,e.appendChild(r)}s.appendChild(e)}),t.append(i,s)}renderErrorSummary(t=this.getErrors()){var e;const r=this.getErrorSummaryOptions();if(!r)return;if(0===t.length)return void this.hideErrorSummary();const i=this.getErrorSummaryContainer(r,!0);if(!i)return;this.prepareErrorSummaryContainer(i);const s=null==(e=r.render)?void 0:e.call(r,t.map(t=>({...t})),this);s instanceof HTMLElement?i.replaceChildren(s):"string"==typeof s?i.innerHTML=s:r.render||this.renderDefaultErrorSummary(i,t,r),r.focus&&this.focusErrorSummary(i)}hideErrorSummary(){const t=this.getErrorSummaryOptions()||{},e=this.getErrorSummaryContainer(t,!1);e&&(e.replaceChildren(),e.setAttribute("hidden",""))}syncErrorSummary(){0!==this.errors.length?this.renderErrorSummary():this.hideErrorSummary()}scrollToFirstErroredInput(t){if(t.length>0){const e=Array.from(this.$el.querySelectorAll("input, textarea, select"));let r=null,i=1/0;if(t.forEach(t=>{const s=e.findIndex(e=>e===t);s<i&&(i=s,r=t)}),r){const t=r.getBoundingClientRect(),e=window.pageYOffset||document.documentElement.scrollTop,i=t.top+e-window.innerHeight/2;document.documentElement.setAttribute("data-now-scrolling",""),window.scrollTo({top:i,behavior:"smooth"}),setTimeout(()=>{document.documentElement.removeAttribute("data-now-scrolling")},800)}}}focusFirstErroredInput(t){var e;if(0===t.length)return;const r=Array.from(this.$el.querySelectorAll("input, textarea, select")),i=t.slice().sort((t,e)=>r.indexOf(t)-r.indexOf(e))[0];null==(e=null==i?void 0:i.focus)||e.call(i,{preventScroll:!0})}getValidationSchema(){return{...L.defaultValidationSchema||{},...this.config.validationSchema||{}}}getAllFields(t=this.$el){return Array.from(t.querySelectorAll("input, textarea, select"))}getFieldKey(t){return t.name||t.id||t.getAttribute("data-field")||t.type||"field"}hasFieldError(t){const e=this.getFieldKey(t);return"true"===t.getAttribute("aria-invalid")||this.errors.some(t=>t.field===e)}clearFieldErrorRecord(t){const e=this.getFieldKey(t);this.errors=this.errors.filter(t=>t.field!==e)}setErrorRecord(t,e,r,i){this.errors.push({field:t,rule:e,message:r,source:i})}getValidationStateAttribute(){return this.config.validationStateAttribute||"data-form-father-state"}setFieldValidationState(t,e){const r=this.getValidationStateAttribute();e?t.setAttribute(r,e):t.removeAttribute(r)}startFieldValidation(t){const e=++this.fieldValidationTokenCounter;return this.fieldValidationTokens.set(t,e),t.setAttribute("aria-busy","true"),this.setFieldValidationState(t,"validating"),e}isLatestFieldValidation(t,e){return null==e||this.fieldValidationTokens.get(t)===e}finishFieldValidation(t,e,r){this.isLatestFieldValidation(t,e)&&(t.removeAttribute("aria-busy"),this.setFieldValidationState(t,r?"valid":"invalid"))}clearFieldValidationState(t){this.fieldValidationTokens.delete(t),t.removeAttribute("aria-busy"),this.setFieldValidationState(t,null)}setValidationError(t,e,r,i){this.clearFieldErrorRecord(t),this.setErrorRecord(this.getFieldKey(t),e,r,i)}resolveField(t){return"string"!=typeof t?t:this.findFieldByName(t)}parseRuleString(t){const[e,...r]=t.split(":").map(t=>t.trim());return r.length>0?{rule:e,params:1===r.length?r[0]:r}:e}parseValidationAttribute(t){return(null!=t?t:"").split(/[,\s|]+/).map(t=>t.trim()).filter(Boolean).map(t=>this.parseRuleString(t))}getRuleName(t){return"string"==typeof t?t:t.rule}getRuleIdentity(t){return JSON.stringify(t)}collectRules(t,e=[],r=new Set){const i=[];t.hasAttribute("required")&&i.push("required"),i.push(...e);const s=this.parseValidationAttribute(t.getAttribute("data-validate")).filter(t=>{const e=this.getRuleName(t);return m(e)||!r.has(e)});i.push(...s),i.push(...this.parseValidationAttribute(t.getAttribute("data-custom-validate")));const n=[],o=new Set;for(const t of i){const e=this.getRuleName(t);if(!m(e)){console.warn(`[FormFather] Unknown validation rule "${e}"`);continue}const r=this.getRuleIdentity(t);o.has(r)||(o.add(r),n.push(t))}return n}getRuleMessage(t,e,r,i,s){const n=t.getAttribute(`data-error-${e}`);return s||n||r[e]||i}fieldMatchesSchemaDefinition(t,e,r){if(r.selector)try{return t.matches(r.selector)}catch(t){return console.warn(`[FormFather] Invalid validation selector "${r.selector}"`),!1}return this.parseValidationAttribute(t.getAttribute("data-validate")).some(t=>this.getRuleName(t)===e)}getFieldValidationConfig(t,e=this.getValidationSchema()){const r=[],i={};for(const[s,n]of Object.entries(e))this.fieldMatchesSchemaDefinition(t,s,n)&&(r.push(...n.rules),Object.assign(i,n.messages||{}));return{rules:this.collectRules(t,r,new Set(Object.keys(e))),messages:i}}getValidationFields(t,e){const r=new Set,i=t=>{Array.from(t).forEach(t=>{(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement||t instanceof HTMLSelectElement)&&r.add(t)})};for(const[s,n]of Object.entries(e))if(n.selector)try{i(t.querySelectorAll(n.selector))}catch(t){console.warn(`[FormFather] Invalid validation selector "${n.selector}"`)}else this.getAllFields(t).filter(t=>this.parseValidationAttribute(t.getAttribute("data-validate")).some(t=>this.getRuleName(t)===s)).forEach(t=>r.add(t));return i(t.querySelectorAll("[required], [data-validate], [data-custom-validate]")),Array.from(r)}async validateInput(t,e,r={},i=this.$el,s){if(t.hasAttribute("data-no-validate"))return!0;const n=t instanceof HTMLInputElement&&("checkbox"===t.type||"radio"===t.type)?!t.checked:0===t.value.trim().length,o=e.some(t=>"required"===this.getRuleName(t));if(n&&!o)return this.isLatestFieldValidation(t,s)?(this.clearFieldErrorRecord(t),this.hideError(t),!0):!this.hasFieldError(t);for(const n of e){const{rule:e,params:o}="string"==typeof n?{rule:n,params:void 0}:{rule:n.rule,params:n.params},a=m(e);if(!a)continue;const l=await a.fn(t.value,t,i,o),c="object"==typeof l&&null!==l,u=c?!0===l.valid:!!l;if(!this.isLatestFieldValidation(t,s))return!this.hasFieldError(t);if(c&&l.effect&&await l.effect({value:t.value,$input:t,$form:i,params:o}),!this.isLatestFieldValidation(t,s))return!this.hasFieldError(t);if(!u){const i=l,s=this.getRuleMessage(t,e,r,a.defaultMessage,c&&"string"==typeof i.message?i.message:void 0);return this.setValidationError(t,e,s,"client"),this.showError(t,s),!1}if(c&&l.stopOthers)return!0}return this.isLatestFieldValidation(t,s)?(this.clearFieldErrorRecord(t),this.hideError(t),!0):!this.hasFieldError(t)}async runFieldValidation(t,e,r={},i=this.$el){const s=this.startFieldValidation(t);try{const n=await this.validateInput(t,e,r,i,s);return this.finishFieldValidation(t,s,n),n}catch(e){throw this.isLatestFieldValidation(t,s)&&this.clearFieldValidationState(t),e}}getRadioGroup(t,e){return t instanceof HTMLInputElement&&"radio"===t.type&&t.name?Array.from(e.querySelectorAll('input[type="radio"]')).filter(e=>e.name===t.name):[]}showFieldError(t,e,r="manual",i=r){const s=this.resolveField(t);return s?(this.setValidationError(s,i,e,r),this.showError(s,e),s.removeAttribute("aria-busy"),this.setFieldValidationState(s,"invalid"),this.syncErrorSummary(),!0):(console.warn(`[FormFather] Field "${String(t)}" not found`),!1)}async validateField(t){const e=this.resolveField(t);if(!e)return console.warn(`[FormFather] Field "${String(t)}" not found`),!1;const{rules:r,messages:i}=this.getFieldValidationConfig(e),s=await this.runFieldValidation(e,r,i);return this.syncErrorSummary(),s}async validate(t=this.$el){const e=this.getValidationSchema(),r=[],i=new WeakSet;let s=!0;this.errors=[];for(const n of this.getValidationFields(t,e)){if(i.has(n))continue;const o=this.getRadioGroup(n,t);o.length>0?o.forEach(t=>i.add(t)):i.add(n);const{rules:a,messages:l}=this.getFieldValidationConfig(n,e);await this.runFieldValidation(n,a,l,t)||(s=!1,r.push(n))}const n=await this.validateFormValidators();n.ok||(s=!1,r.push(...n.erroredInputs));const o=r.filter(t=>!t.hasAttribute("data-no-error-scroll"));return!s&&this.config.scrollToFirstErroredInput&&this.scrollToFirstErroredInput(o),!s&&this.config.focusFirstErroredInput&&this.focusFirstErroredInput(o),s?this.hideErrorSummary():this.renderErrorSummary(),s}disableSubmit(){this.$submits.forEach(t=>{t.setAttribute("disabled","")})}enableSubmit(){this.$submits.forEach(t=>{t.removeAttribute("disabled")})}clearInputs(){var t;null==(t=this.inputs)||t.forEach(t=>{var e;const r=t;"radio"===r.type||"checkbox"===r.type?r.checked=!1:(r.value="",null==(e=r.parentElement)||e.classList.remove("filled"),r.dispatchEvent(new Event("input")))})}_prefersReducedMotion(){var t;return"undefined"!=typeof window&&(null==(t=window.matchMedia)?void 0:t.call(window,"(prefers-reduced-motion: reduce)").matches)}_canAnimate(t){return!(!t||!("animate"in t)||this._prefersReducedMotion())}_cancelAnimations(t){var e;const r=t;null==(e=null==r?void 0:r.getAnimations)||e.call(r).forEach(t=>t.cancel())}showErrorForm(t){var e;const r=this.$el.querySelectorAll(this.config.inputWrapperSelector),i=r[r.length-1];let s=this.$el.querySelector(".error-block-under-input__wrapper");!s||i&&i.nextElementSibling!==s?(s=document.createElement("div"),s.className="error-block-under-input__wrapper",s.innerHTML='\n <div class="error-block-under-input error-block-under-input--warning" style="display:grid;grid-template-columns:auto 1fr;gap:8px;align-items:start;">\n <div class="error-block-under-input__icon-wrapper" style="position:relative;width:20px;height:20px;">\n <span class="error-block-under-input__icon" style="display:block;width:20px;height:20px;"></span>\n <span class="error-block-under-input__icon error-block-under-input__icon--animated" style="display:block;position:absolute;inset:0;"></span>\n </div>\n <p class="error-block-under-input__text" style="margin:0;">\n <span class="error-block-under-input__main-text"></span>\n <span class="error-block-under-input__secondary-text"></span>\n </p>\n </div>\n ',Object.assign(s.style,{boxSizing:"border-box",overflow:"hidden",height:"0px",paddingTop:"0px",opacity:"0"}),i?i.insertAdjacentElement("afterend",s):this.$el.appendChild(s)):(s.style.boxSizing="border-box",s.style.overflow="hidden"),s.querySelector(".error-block-under-input__main-text").textContent=t;const n=s.querySelector(".error-block-under-input__secondary-text");n&&(n.textContent=""),null==(e=s.querySelector(".error-block-under-input"))||e.classList.remove("error-block-under-input--success");const o=s.style.height,a=s.style.paddingTop;s.style.height="auto",s.style.paddingTop=`${this._anim.paddingTopOpened}px`;const l=s.clientHeight;if(s.style.height=o||"0px",s.style.paddingTop=a||"0px",parseFloat(s.style.height)===l&&1===parseFloat(s.style.opacity||"1"))this._ringIcon(s);else{if(this._cancelAnimations(s),this._canAnimate(s)){s.animate([{height:`${parseFloat(s.style.height)||0}px`,paddingTop:`${parseFloat(s.style.paddingTop)||0}px`,opacity:parseFloat(s.style.opacity||"0")},{height:`${l}px`,paddingTop:`${this._anim.paddingTopOpened}px`,opacity:1}],{duration:this._anim.duration,easing:this._anim.easing,fill:"forwards"}).addEventListener("finish",()=>{s.style.height="auto",s.style.paddingTop=`${this._anim.paddingTopOpened}px`,s.style.opacity="1"})}else s.style.height="auto",s.style.paddingTop=`${this._anim.paddingTopOpened}px`,s.style.opacity="1";this._ringIcon(s)}this.$el.addEventListener("input",()=>this.hideErrorForm(),{passive:!0,once:!0})}hideErrorForm(){const t=this.$el.querySelectorAll(this.config.inputWrapperSelector),e=t[t.length-1],r=(null==e?void 0:e.nextElementSibling)||this.$el.querySelector(".error-block-under-input__wrapper");if(!r||!r.classList.contains("error-block-under-input__wrapper"))return;if((""===r.style.height||"auto"===r.style.height)&&(r.style.height=`${r.clientHeight}px`),r.style.paddingTop=r.style.paddingTop||`${this._anim.paddingTopOpened}px`,r.style.opacity=r.style.opacity||"1",this._cancelAnimations(r),this._canAnimate(r)){r.animate([{height:`${parseFloat(r.style.height)||r.clientHeight}px`,paddingTop:`${parseFloat(r.style.paddingTop)||this._anim.paddingTopOpened}px`,opacity:parseFloat(r.style.opacity||"1")},{height:"0px",paddingTop:"0px",opacity:0}],{duration:this._anim.duration,easing:this._anim.easing,fill:"forwards"}).addEventListener("finish",()=>{r.remove()})}else r.remove()}_ringIcon(t){const e=t.querySelector(".error-block-under-input__icon--animated");e&&(this._cancelAnimations(e),this._canAnimate(e)&&e.animate([{transform:"rotate(0deg) scale(1)",opacity:0},{transform:"rotate(-8deg) scale(1.05)",opacity:1,offset:.15},{transform:"rotate(8deg) scale(1.05)",opacity:1,offset:.3},{transform:"rotate(-6deg) scale(1.03)",opacity:1,offset:.45},{transform:"rotate(6deg) scale(1.03)",opacity:1,offset:.6},{transform:"rotate(0deg) scale(1)",opacity:0}],{duration:this._anim.iconDuration,easing:"ease"}))}destroy(){var t;this._onSubmitHandler&&(this.$el.removeEventListener("submit",this._onSubmitHandler),this._onSubmitHandler=void 0),this._onSubmitClickHandler&&(this.$submits.forEach(t=>{t.removeEventListener("click",this._onSubmitClickHandler)}),this._onSubmitClickHandler=void 0),this.clearFieldValidationEvents(),null==(t=this.mutationObserver)||t.disconnect(),this.mutationObserver=void 0;const e=this.$el.querySelector(".error-block-under-input__wrapper");e&&(this._cancelAnimations(e),e.remove()),this.hideErrorSummary(),this.$submits.forEach(t=>{t.classList.remove("button--loading");const e=t.querySelector(".button__loader");e&&e.remove()});this.$el.querySelectorAll("input, textarea, select").forEach(t=>{this.hideError(t),this.clearFieldValidationState(t)}),document.documentElement.removeAttribute("data-now-scrolling"),this.inputs=null,this.$submits=[],this.errors=[],this.waitResponse=!1,T.delete(this.$el)}isCorrectArguments(){return"FORM"!==this.$el.tagName?(console.warn("$el не является тегом form"),!1):0!==this.$submits.length||(console.warn("В форме нет кнопки с типом submit"),!1)}formDataToObject(t){const e={};return t.forEach((t,r)=>{Object.prototype.hasOwnProperty.call(e,r)?(Array.isArray(e[r])||(e[r]=[e[r]]),e[r].push(t)):e[r]=t}),e}appendFormDataValue(t,e,r){null!=r&&(Array.isArray(r)?r.forEach((r,i)=>{this.appendFormDataValue(t,`${e}[${i}]`,r)}):r instanceof Blob?t.append(e,r):t.append(e,"object"==typeof r?JSON.stringify(r):String(r)))}objectToFormData(t){const e=new FormData;return Object.entries(t).forEach(([t,r])=>{this.appendFormDataValue(e,t,r)}),e}stringifyFormDataValue(t){return"string"==typeof t?t:t.name}formDataToUrlSearchParams(t){const e=new URLSearchParams;return t.forEach((t,r)=>{e.append(r,this.stringifyFormDataValue(t))}),e}formDataToTextBody(t){return Array.from(t).map(([t,e])=>`${encodeURIComponent(t)}=${encodeURIComponent(this.stringifyFormDataValue(e))}`).join("\n")}isBodylessMethod(t){return"GET"===t||"HEAD"===t}appendQueryString(t,e){const r=e.toString();if(!r)return t;const i=t.includes("?")?"&":"?";return`${t}${i}${r}`}findFieldByName(t){return Array.from(this.$el.querySelectorAll("input, textarea, select")).find(e=>e.name===t)||null}async parseResponseBody(t){try{const e=await t.json();return e&&"object"==typeof e?e:{success:!1,error:!0,"error-msg":"Некорректный ответ сервера"}}catch(t){return{success:!1,error:!0,"error-msg":"Некорректный ответ сервера"}}}handleUnsuccessfulResponse(t){if(!0===t.error){if(t["error-msg"]&&(this.setErrorRecord(S,"server",t["error-msg"],"server"),this.showErrorForm(t["error-msg"])),t.errors){const e=[];t.errors.forEach(t=>{const r=this.findFieldByName(t.name);r?(e.push(r),this.setValidationError(r,"server",t["error-msg"],"server"),this.showError(r,t["error-msg"]),r.removeAttribute("aria-busy"),this.setFieldValidationState(r,"invalid")):(this.setErrorRecord(t.name,"server",t["error-msg"],"server"),console.warn(`Не найдено поле с именем: ${t.name}, для вывода ошибки: ${t["error-msg"]}`))}),!0===this.config.scrollToFirstErroredInput&&this.scrollToFirstErroredInput(e),!0===this.config.focusFirstErroredInput&&this.focusFirstErroredInput(e)}this.syncErrorSummary()}}async sendData(){var t;const e=this.$el.getAttribute("action")||"",r=(null==(t=this.$el.getAttribute("method"))?void 0:t.toUpperCase())||"POST",i=this.$el.getAttribute("enctype")||"application/x-www-form-urlencoded";let s=null,n={};const a=o(this.$el);let l=this.formDataToObject(a);if(this.config.logging)for(const t of a.keys())console.log(a.get(t));const c="function"==typeof this.config.wrapData;"function"==typeof this.config.wrapData&&(l=this.config.wrapData(l));const u=c?this.objectToFormData(l):a;if(this.isBodylessMethod(r))return fetch(this.appendQueryString(e,this.formDataToUrlSearchParams(u)),{method:r,headers:n});switch(i){case"application/x-www-form-urlencoded":s=this.formDataToUrlSearchParams(u).toString(),n["Content-Type"]="application/x-www-form-urlencoded";break;case"multipart/form-data":s=u;break;case"text/plain":s=this.formDataToTextBody(u),n["Content-Type"]="text/plain";break;case"application/json":s=JSON.stringify(l),n["Content-Type"]="application/json";break;default:console.warn(`Неизвестный enctype: ${i}. Используется application/x-www-form-urlencoded.`),s=this.formDataToUrlSearchParams(u).toString(),n["Content-Type"]="application/x-www-form-urlencoded"}return fetch(e,{method:r,body:s,headers:n})}async submit(){var t,e,r,i,s,n,o,l,c,u,d,h;if(!this.waitResponse){let m=!1;const f=()=>{m||(this.waitResponse=!1,!0===this.config.showLoaderButton&&this.hideLoader(),m=!0)};try{if(!1===await(null==(e=(t=this.config).onBeforeSubmit)?void 0:e.call(t,this)))return{success:!1};!0===this.config.showLoaderButton&&this.showLoader(),null==(i=(r=this.config).onSubmit)||i.call(r,this),this.waitResponse=!0;const d=await this.sendData(),h=await this.parseResponseBody(d);return f(),null==(n=(s=this.config).onResponse)||n.call(s,h,this),d.ok&&!0===h.success?null==(l=(o=this.config).onResponseSuccess)||l.call(o,h,this):(null==(u=(c=this.config).onResponseUnsuccess)||u.call(c,h,this),this.handleUnsuccessfulResponse(h)),a(h),{success:d.ok&&!0===h.success,response:d,responseBody:h}}catch(t){throw f(),null==(h=(d=this.config).onSubmitError)||h.call(d,t,this),t}}}}return t.FORM_ERROR_FIELD=S,t.blockScrollBody=function(){window.lastBlockScrollPosition=window.pageYOffset,document.body.style.top=-window.lastBlockScrollPosition+"px",document.body.classList.add("body--block-scroll")},t.closest=e,t.createFieldValidator=y,t.createFormValidator=F,t.createLengthValidator=function(t,e){return y(e=>{const r=!1===t.trim?e:e.trim();return 0===r.length&&!0===t.emptyIsValid||!("number"==typeof t.min&&r.length<t.min)&&!("number"==typeof t.max&&r.length>t.max)},e)},t.createPatternValidator=function(t,e){return y(e=>(t.lastIndex=0,t.test(e)),e)},t.createSchemaValidator=g,t.dateOrder=function(t,e,r="Дата окончания должна быть позже даты начала",i="date-order"){return F(({values:r})=>{const i=w(r,t),s=w(r,e);return!(!A(i)&&!A(s))||Date.parse(String(i))<=Date.parse(String(s))},{field:e,rule:i,message:r})},t.default=L,t.getAllValidators=function(){return new Map(d)},t.getValidator=m,t.isEmailValid=function(t){return/^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/iu.test(t)},t.isPhoneValid=function(t){const e=[];return Array.from(t).forEach(t=>{(Number(t)||0===Number(t))&&e.push(t)}),!(e.length<11||e.length<12&&"+"===t[1])},t.isUrlValid=n,t.parseCommonResponseProperties=a,t.registerFieldValidator=function(t,e,r,i={}){h(t,y(e),r,i)},t.registerSchemaValidator=function(t,e,r,i={}){const{override:s,...n}=i;h(t,g(e,n),r,{override:s})},t.registerValidator=h,t.requiredIf=function(t,e,r="Поле обязательно",i="conditional-required"){return F(async r=>!await e(r)||!A(w(r.values,t)),{field:t,rule:i,message:r})},t.sameAsField=function(t,e,r="Значения не совпадают",i="same-as-field"){return F(({values:r})=>w(r,t)===w(r,e),{field:t,rule:i,message:r})},t.serializeFormToJSON=l,t.serializeToFormData=o,t.unblockScrollBody=function(){document.body.classList.remove("body--block-scroll"),document.body.style.top="",window.scrollTo(0,window.lastBlockScrollPosition||0)},Object.defineProperty(t,"__esModule",{value:!0}),t}({});
|
|
2
2
|
//# sourceMappingURL=FormFather.min.js.map
|