form-father 0.7.1 → 0.8.2

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/demos/main.js ADDED
@@ -0,0 +1,149 @@
1
+ const api = window.FormFather || {};
2
+ const Form = api.default || api.Form || api;
3
+ const { FORM_ERROR_FIELD, createLengthValidator, registerValidator, sameAsField } = api;
4
+ const output = document.querySelector('#demo-output');
5
+
6
+ function writeOutput(title, payload) {
7
+ output.textContent = `${title}\n\n${JSON.stringify(payload, null, 2)}`;
8
+ }
9
+
10
+ function json(body, status = 200) {
11
+ return new Response(JSON.stringify(body), {
12
+ status,
13
+ headers: { 'Content-Type': 'application/json' },
14
+ });
15
+ }
16
+
17
+ window.fetch = async (url, options = {}) => {
18
+ const requestUrl = String(url);
19
+ const method = options.method || 'GET';
20
+
21
+ if (requestUrl.startsWith('/demo/login')) {
22
+ return json({
23
+ success: true,
24
+ message: 'Logged in',
25
+ method,
26
+ });
27
+ }
28
+
29
+ if (requestUrl.startsWith('/demo/callback')) {
30
+ return json(
31
+ {
32
+ success: false,
33
+ error: true,
34
+ 'error-msg': 'Сервер вернул ошибки по полям.',
35
+ errors: [{ name: 'tel', 'error-msg': 'Этот номер уже есть в заявках' }],
36
+ },
37
+ 422,
38
+ );
39
+ }
40
+
41
+ if (requestUrl.startsWith('/demo/search')) {
42
+ return json({
43
+ success: true,
44
+ query: requestUrl,
45
+ results: ['Validation API', 'Server response format', 'Demo recipes'],
46
+ });
47
+ }
48
+
49
+ if (requestUrl.startsWith('/demo/upload')) {
50
+ return json({
51
+ success: true,
52
+ message: 'FormData accepted',
53
+ contentType: options.headers?.['Content-Type'] || 'browser-managed multipart boundary',
54
+ });
55
+ }
56
+
57
+ if (requestUrl.startsWith('/demo/api')) {
58
+ return json({
59
+ success: true,
60
+ message: 'Programmatic API form accepted',
61
+ method,
62
+ });
63
+ }
64
+
65
+ return json({ success: false, error: true, 'error-msg': `No demo route for ${requestUrl}` }, 404);
66
+ };
67
+
68
+ registerValidator('min-length', createLengthValidator({ min: 6 }), 'Слишком короткое значение', { override: true });
69
+ registerValidator(
70
+ 'available-email',
71
+ value =>
72
+ new Promise(resolve => {
73
+ setTimeout(() => resolve(value.toLowerCase() !== 'taken@example.com'), 350);
74
+ }),
75
+ 'Email is already used',
76
+ { override: true },
77
+ );
78
+
79
+ const forms = Form.initAll('form[data-form-father]', {
80
+ inputSelector: '.input',
81
+ inputWrapperSelector: '.field',
82
+ validateOn: ['blur', 'change'],
83
+ revalidateOn: ['input', 'change'],
84
+ validationDebounce: 120,
85
+ focusFirstErroredInput: true,
86
+ loaderColor: 'currentColor',
87
+ errorSummary: {
88
+ title: 'Проверьте поля формы',
89
+ focus: true,
90
+ },
91
+ formValidators: sameAsField('passwordConfirm', 'password', 'Пароли не совпадают'),
92
+ onValidationError(errors) {
93
+ writeOutput('Client validation errors', errors);
94
+ },
95
+ onResponse(responseBody, form) {
96
+ writeOutput(`${form.$el.dataset.demo || 'form'} response`, responseBody);
97
+ },
98
+ onResponseSuccess(responseBody, form) {
99
+ if (form.$el.dataset.demo === 'login' || form.$el.dataset.demo === 'upload') {
100
+ form.clearInputs();
101
+ }
102
+ },
103
+ });
104
+
105
+ const formByDemo = new Map(forms.map(form => [form.$el.dataset.demo, form]));
106
+ const apiForm = formByDemo.get('api');
107
+
108
+ document.querySelectorAll('[data-api-action]').forEach(button => {
109
+ button.addEventListener('click', async () => {
110
+ if (!apiForm) return;
111
+
112
+ switch (button.dataset.apiAction) {
113
+ case 'fill':
114
+ apiForm.setValues({
115
+ name: 'Ada Lovelace',
116
+ email: 'ada@example.com',
117
+ plan: 'team',
118
+ });
119
+ writeOutput('setValues()', apiForm.getValues());
120
+ break;
121
+
122
+ case 'validate-email': {
123
+ const valid = await apiForm.validateField('email');
124
+ writeOutput('validateField("email")', {
125
+ valid,
126
+ errors: apiForm.getErrors(),
127
+ });
128
+ break;
129
+ }
130
+
131
+ case 'server-errors':
132
+ apiForm.setErrors({
133
+ email: 'Этот email уже зарегистрирован',
134
+ [FORM_ERROR_FIELD]: 'Сервер попросил проверить форму',
135
+ });
136
+ writeOutput('setErrors()', apiForm.getErrors());
137
+ break;
138
+
139
+ case 'values':
140
+ writeOutput('getValues()', apiForm.getValues());
141
+ break;
142
+
143
+ case 'clear':
144
+ apiForm.clearErrors();
145
+ writeOutput('clearErrors()', apiForm.getErrors());
146
+ break;
147
+ }
148
+ });
149
+ });
@@ -0,0 +1,276 @@
1
+ :root {
2
+ color-scheme: light;
3
+ --bg: #f5f7fb;
4
+ --panel: #ffffff;
5
+ --text: #172033;
6
+ --muted: #637083;
7
+ --line: #d9e0ea;
8
+ --accent: #176b87;
9
+ --accent-strong: #0f5268;
10
+ --danger: #b3261e;
11
+ --success: #176b3a;
12
+ }
13
+
14
+ * {
15
+ box-sizing: border-box;
16
+ }
17
+
18
+ body {
19
+ margin: 0;
20
+ background: var(--bg);
21
+ color: var(--text);
22
+ font: 16px/1.5 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
23
+ }
24
+
25
+ .demo-header {
26
+ display: flex;
27
+ align-items: flex-end;
28
+ justify-content: space-between;
29
+ gap: 24px;
30
+ padding: 40px min(6vw, 72px) 24px;
31
+ border-bottom: 1px solid var(--line);
32
+ background: #ffffff;
33
+ }
34
+
35
+ .demo-header h1,
36
+ .panel-title h2 {
37
+ margin: 0;
38
+ letter-spacing: 0;
39
+ }
40
+
41
+ .demo-header h1 {
42
+ max-width: 760px;
43
+ font-size: clamp(32px, 4vw, 54px);
44
+ line-height: 1.05;
45
+ }
46
+
47
+ .eyebrow {
48
+ margin: 0 0 8px;
49
+ color: var(--accent);
50
+ font-size: 13px;
51
+ font-weight: 700;
52
+ letter-spacing: 0;
53
+ text-transform: uppercase;
54
+ }
55
+
56
+ .github-link,
57
+ button {
58
+ display: inline-flex;
59
+ align-items: center;
60
+ justify-content: center;
61
+ min-height: 42px;
62
+ padding: 0 16px;
63
+ border: 1px solid var(--accent);
64
+ border-radius: 6px;
65
+ background: var(--accent);
66
+ color: #ffffff;
67
+ font: inherit;
68
+ font-weight: 700;
69
+ text-decoration: none;
70
+ cursor: pointer;
71
+ }
72
+
73
+ .github-link {
74
+ background: #ffffff;
75
+ color: var(--accent);
76
+ }
77
+
78
+ button:hover,
79
+ button:focus-visible {
80
+ background: var(--accent-strong);
81
+ border-color: var(--accent-strong);
82
+ }
83
+
84
+ .github-link:hover,
85
+ .github-link:focus-visible {
86
+ border-color: var(--accent-strong);
87
+ color: var(--accent-strong);
88
+ }
89
+
90
+ .demo-grid {
91
+ display: grid;
92
+ grid-template-columns: repeat(2, minmax(0, 1fr));
93
+ gap: 18px;
94
+ padding: 24px min(6vw, 72px);
95
+ }
96
+
97
+ .demo-panel,
98
+ .demo-output {
99
+ border: 1px solid var(--line);
100
+ border-radius: 8px;
101
+ background: var(--panel);
102
+ padding: 22px;
103
+ }
104
+
105
+ .demo-panel--wide {
106
+ grid-column: 1 / -1;
107
+ }
108
+
109
+ .panel-title {
110
+ margin-bottom: 18px;
111
+ }
112
+
113
+ .panel-title h2 {
114
+ font-size: 22px;
115
+ line-height: 1.2;
116
+ }
117
+
118
+ form {
119
+ display: grid;
120
+ gap: 14px;
121
+ }
122
+
123
+ [data-form-father-summary]:not([hidden]) {
124
+ display: grid;
125
+ gap: 8px;
126
+ padding: 12px;
127
+ border: 1px solid rgba(179, 38, 30, 0.28);
128
+ border-radius: 6px;
129
+ background: rgba(179, 38, 30, 0.06);
130
+ color: var(--danger);
131
+ font-size: 14px;
132
+ }
133
+
134
+ [data-form-father-summary-title] {
135
+ margin: 0;
136
+ font-weight: 700;
137
+ }
138
+
139
+ [data-form-father-summary-list] {
140
+ display: grid;
141
+ gap: 4px;
142
+ margin: 0;
143
+ padding-left: 18px;
144
+ }
145
+
146
+ [data-form-father-summary] button {
147
+ min-height: 0;
148
+ padding: 0;
149
+ border: 0;
150
+ background: transparent;
151
+ color: inherit;
152
+ font-weight: 700;
153
+ text-align: left;
154
+ text-decoration: underline;
155
+ }
156
+
157
+ [data-form-father-summary] button:hover,
158
+ [data-form-father-summary] button:focus-visible {
159
+ background: transparent;
160
+ color: var(--accent-strong);
161
+ }
162
+
163
+ .demo-actions {
164
+ display: flex;
165
+ flex-wrap: wrap;
166
+ gap: 8px;
167
+ }
168
+
169
+ .demo-actions button {
170
+ min-height: 38px;
171
+ padding-inline: 12px;
172
+ }
173
+
174
+ .field {
175
+ display: grid;
176
+ gap: 6px;
177
+ }
178
+
179
+ .field span {
180
+ color: var(--muted);
181
+ font-size: 14px;
182
+ font-weight: 700;
183
+ }
184
+
185
+ .input {
186
+ width: 100%;
187
+ min-height: 42px;
188
+ padding: 8px 10px;
189
+ border: 1px solid var(--line);
190
+ border-radius: 6px;
191
+ background: #ffffff;
192
+ color: var(--text);
193
+ font: inherit;
194
+ }
195
+
196
+ .input:focus {
197
+ border-color: var(--accent);
198
+ outline: 3px solid rgba(23, 107, 135, 0.16);
199
+ }
200
+
201
+ .input[aria-invalid="true"] {
202
+ border-color: var(--danger);
203
+ }
204
+
205
+ .input[aria-busy="true"] {
206
+ opacity: 0.72;
207
+ }
208
+
209
+ .input[data-form-father-state="valid"] {
210
+ border-color: var(--success);
211
+ }
212
+
213
+ .input[data-form-father-state="invalid"] {
214
+ border-color: var(--danger);
215
+ }
216
+
217
+ [data-form-father-error],
218
+ .field-error {
219
+ color: var(--danger);
220
+ font-size: 13px;
221
+ }
222
+
223
+ .input__wrapper--error .input {
224
+ border-color: var(--danger);
225
+ }
226
+
227
+ .button--loading {
228
+ opacity: 0.74;
229
+ pointer-events: none;
230
+ }
231
+
232
+ .button__loader {
233
+ display: inline-flex;
234
+ width: 18px;
235
+ height: 18px;
236
+ margin-right: 8px;
237
+ }
238
+
239
+ .button__loader svg {
240
+ width: 18px;
241
+ height: 18px;
242
+ }
243
+
244
+ .error-block-under-input__wrapper {
245
+ color: var(--danger);
246
+ font-size: 14px;
247
+ }
248
+
249
+ .demo-output {
250
+ margin: 0 min(6vw, 72px) 40px;
251
+ }
252
+
253
+ pre {
254
+ min-height: 130px;
255
+ margin: 0;
256
+ overflow: auto;
257
+ padding: 16px;
258
+ border: 1px solid var(--line);
259
+ border-radius: 8px;
260
+ background: #101820;
261
+ color: #d8f3dc;
262
+ font-size: 13px;
263
+ white-space: pre-wrap;
264
+ }
265
+
266
+ @media (max-width: 760px) {
267
+ .demo-header {
268
+ display: grid;
269
+ align-items: start;
270
+ padding-top: 28px;
271
+ }
272
+
273
+ .demo-grid {
274
+ grid-template-columns: 1fr;
275
+ }
276
+ }
@@ -0,0 +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}({});
2
+ //# sourceMappingURL=FormFather.min.js.map