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/CHANGELOG.md +223 -0
- package/RESPONSE_API.md +233 -0
- package/demos/index.html +198 -0
- package/demos/main.js +149 -0
- package/demos/styles.css +276 -0
- package/dist/FormFather.min.js +2 -0
- package/dist/FormFather.min.js.map +1 -0
- package/{index.d.ts → dist/index.d.ts} +2 -1
- package/dist/index.js +1791 -0
- package/dist/index.js.map +1 -0
- package/docs/api/README.md +153 -0
- package/docs/demo/README.md +37 -0
- package/docs/en/README.md +270 -0
- package/docs/recipes/README.md +150 -0
- package/package.json +47 -33
- package/FormFather.min.js +0 -2
- package/FormFather.min.js.map +0 -1
- package/index.js +0 -2076
- package/index.js.map +0 -1
- /package/{types → dist/types}/adapters.d.ts +0 -0
- /package/{types → dist/types}/helpers.d.ts +0 -0
- /package/{types → dist/types}/index.d.ts +0 -0
- /package/{types → dist/types}/validators.d.ts +0 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# Form Father
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/form-father)
|
|
4
|
+
[](https://www.npmjs.com/package/form-father)
|
|
5
|
+
|
|
6
|
+
[🇷🇺 Документация на русском](https://github.com/Poliklot/form-father/blob/master/README.md)
|
|
7
|
+
|
|
8
|
+
**Form Father** is a library for handling forms in pure JavaScript, providing convenient validation and form submission
|
|
9
|
+
with TypeScript support.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Install the library via npm:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install form-father
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
import Form, { isUrlValid, serializeToFormData } from 'form-father';
|
|
23
|
+
|
|
24
|
+
Form.setDefaultParams({
|
|
25
|
+
showLoaderButton: false,
|
|
26
|
+
scrollToFirstErroredInput: false,
|
|
27
|
+
logging: true,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const formElement = document.querySelector('#myForm');
|
|
31
|
+
const options = {
|
|
32
|
+
onSubmit: formInstance => {
|
|
33
|
+
// Actions on form submission
|
|
34
|
+
},
|
|
35
|
+
onResponse: (responseBody, formInstance) => {
|
|
36
|
+
// Actions on receiving server response
|
|
37
|
+
},
|
|
38
|
+
// Other options...
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const form = new Form(formElement, options);
|
|
42
|
+
const simpleForm = new Form(document.querySelector('#simpleForm')); // options are optional
|
|
43
|
+
|
|
44
|
+
const forms = Form.initAll('form[data-form-father]', {
|
|
45
|
+
inputWrapperSelector: '.field',
|
|
46
|
+
validateOn: ['blur', 'change'],
|
|
47
|
+
revalidateOn: ['input', 'change'],
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import Form, {
|
|
53
|
+
FORM_ERROR_FIELD,
|
|
54
|
+
type FormOptions,
|
|
55
|
+
type ValidationSchema,
|
|
56
|
+
type ResponseBody,
|
|
57
|
+
type ValidationError,
|
|
58
|
+
type SubmitResult,
|
|
59
|
+
type FormResetOptions,
|
|
60
|
+
type FormValidator,
|
|
61
|
+
type ValidationIssue,
|
|
62
|
+
type FormValidatorPredicate,
|
|
63
|
+
type ErrorSummaryOptions,
|
|
64
|
+
} from 'form-father';
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Options
|
|
68
|
+
|
|
69
|
+
- **onSubmit**: Callback function executed on form submission.
|
|
70
|
+
- **onBeforeSubmit**: Runs before sending an already valid form; returning `false` cancels the request.
|
|
71
|
+
- **onSubmitError**: Runs when form submission throws.
|
|
72
|
+
- **onResponse**: Callback function executed when a server response is received.
|
|
73
|
+
- **onResponseSuccess**: Callback function executed for a successful HTTP response with `success: true`.
|
|
74
|
+
- **onResponseUnsuccess**: Callback function executed for unsuccessful HTTP responses, `success !== true`, or invalid JSON.
|
|
75
|
+
- **onValidationError**: Runs when client-side validation fails.
|
|
76
|
+
- **showLoaderButton**: Whether to display a loader in the submit button. Defaults to `true`.
|
|
77
|
+
- **scrollToFirstErroredInput**: Whether to scroll to the first input with an error. Defaults to `true`.
|
|
78
|
+
- **focusFirstErroredInput**: Whether to focus the first invalid field. Defaults to `false`.
|
|
79
|
+
- **customTypeError**: Custom error type.
|
|
80
|
+
- **loaderColor**: Color of the loader in the submit button.
|
|
81
|
+
- **logging**: Specifies whether to log data to the console. Defaults to false.
|
|
82
|
+
- **validateOn**: Field live-validation events: `submit`, `input`, `blur`, `change`.
|
|
83
|
+
- **revalidateOn**: Events used to re-check already invalid fields. Defaults to `input` and `change`.
|
|
84
|
+
- **validationDebounce**: Live-validation delay in milliseconds.
|
|
85
|
+
- **errorContainerAttribute**: Attribute containing the CSS selector for a custom error container. Defaults to
|
|
86
|
+
`data-error-container`.
|
|
87
|
+
- **validationStateAttribute**: Field state attribute: `validating`, `valid`, or `invalid`. Defaults to
|
|
88
|
+
`data-form-father-state`.
|
|
89
|
+
- **ariaDescribeErrors**: Links inline error text to the field with `aria-describedby`. Defaults to `true`.
|
|
90
|
+
- **errorIdPrefix**: Prefix for automatically generated error element ids. Defaults to `form-father-error`.
|
|
91
|
+
- **errorSummary**: Form error summary: `true` for the default renderer or `{ selector, title, focus, render }`.
|
|
92
|
+
- **observeMutations**: Watches dynamically added fields and submit buttons.
|
|
93
|
+
- **formValidators**: A form validator or array of validators for cross-field rules.
|
|
94
|
+
|
|
95
|
+
## Form Submission
|
|
96
|
+
|
|
97
|
+
- Submit elements are `button[type="submit"]`, `button` without `type`, `input[type="submit"]`, and
|
|
98
|
+
`input[type="image"]`.
|
|
99
|
+
- `submit()` is public and returns `Promise<SubmitResult | undefined>`.
|
|
100
|
+
- For `method="GET"` and `method="HEAD"`, data is appended to the `action` query string and no request body is sent.
|
|
101
|
+
- `wrapData` applies to every supported `enctype`: `application/x-www-form-urlencoded`, `multipart/form-data`,
|
|
102
|
+
`text/plain`, and `application/json`.
|
|
103
|
+
- `onResponseSuccess` runs only for a successful HTTP response with `success: true`.
|
|
104
|
+
- `onResponseUnsuccess` runs for unsuccessful HTTP responses, `success !== true`, or invalid JSON responses.
|
|
105
|
+
|
|
106
|
+
## Methods
|
|
107
|
+
|
|
108
|
+
- **Form.initAll(selector, options)**: Initializes all forms matching a selector and reuses existing instances.
|
|
109
|
+
- **updateOptions(options)**: Updates one form instance and rebinds live validation.
|
|
110
|
+
- **validate()**: Validates the whole form.
|
|
111
|
+
- **validateField(field)**: Validates one field by name or DOM element.
|
|
112
|
+
- **showFieldError(field, message, source)**: Shows a field error manually.
|
|
113
|
+
- **setErrors(errors, source)**: Applies backend/form-level errors from an object, array, or `ErrorResponse[]`.
|
|
114
|
+
- **getErrors()**: Returns current errors as `{ field, rule, message, source }`.
|
|
115
|
+
- **getValues()**: Returns form values as a plain object.
|
|
116
|
+
- **setValues(values)**: Fills fields by `name` and dispatches `input`/`change`.
|
|
117
|
+
- **clearErrors()**: Clears errors without changing field values.
|
|
118
|
+
- **reset(options)**: Calls native `form.reset()` and clears errors by default.
|
|
119
|
+
- **clearInputs()**: Clears all input fields in the form.
|
|
120
|
+
- **setDefaultParams(params):** The setDefaultParams method is used to set default values for all instances of the form. These parameters can be overridden when initializing a specific form.
|
|
121
|
+
- **destroy()**: Removes event listeners and runtime artifacts.
|
|
122
|
+
|
|
123
|
+
## Validation
|
|
124
|
+
|
|
125
|
+
```html
|
|
126
|
+
<input
|
|
127
|
+
name="email"
|
|
128
|
+
type="email"
|
|
129
|
+
data-validate="required|email"
|
|
130
|
+
data-error-required="Email is required"
|
|
131
|
+
data-error-email="Enter a valid email"
|
|
132
|
+
data-error-container="#email-error"
|
|
133
|
+
/>
|
|
134
|
+
<small id="email-error" hidden></small>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- `data-validate` accepts rules separated by `|`, spaces, or commas.
|
|
138
|
+
- `data-error-rule-name` overrides a message for one rule.
|
|
139
|
+
- `data-error-container` points to a custom error container without requiring wrapper markup.
|
|
140
|
+
- During async validation, fields receive `aria-busy="true"` and a `validating` state attribute; stale validator
|
|
141
|
+
responses are ignored.
|
|
142
|
+
- Inline errors receive `role="alert"` and are linked to fields with `aria-describedby`.
|
|
143
|
+
- `data-custom-validate` is still supported for backward compatibility.
|
|
144
|
+
|
|
145
|
+
### Error summary and a11y
|
|
146
|
+
|
|
147
|
+
```html
|
|
148
|
+
<form data-form-father novalidate>
|
|
149
|
+
<div data-form-father-summary hidden></div>
|
|
150
|
+
<label>
|
|
151
|
+
Email
|
|
152
|
+
<input class="input" name="email" data-validate="required|email" />
|
|
153
|
+
</label>
|
|
154
|
+
<button type="submit">Submit</button>
|
|
155
|
+
</form>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
new Form($form, {
|
|
160
|
+
inputWrapperSelector: 'label',
|
|
161
|
+
errorSummary: {
|
|
162
|
+
title: 'Please check the form',
|
|
163
|
+
focus: true,
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
- If the form already contains `[data-form-father-summary]`, Form Father uses it; otherwise `errorSummary: true` creates
|
|
169
|
+
a summary at the start of the form.
|
|
170
|
+
- `selector` sends the summary into your own container.
|
|
171
|
+
- `render(errors, form)` lets you fully replace the summary markup.
|
|
172
|
+
- `ariaDescribeErrors: false` disables automatic error id insertion into `aria-describedby`.
|
|
173
|
+
|
|
174
|
+
Final rule order:
|
|
175
|
+
|
|
176
|
+
```text
|
|
177
|
+
required → schema.rules → data-validate → data-custom-validate
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Cross-field and form-level validation
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
import { dateOrder, requiredIf, sameAsField } from 'form-father';
|
|
184
|
+
|
|
185
|
+
const form = new Form($form, {
|
|
186
|
+
formValidators: [
|
|
187
|
+
sameAsField('passwordConfirm', 'password', 'Passwords do not match'),
|
|
188
|
+
requiredIf('email', ({ values }) => Boolean(values.subscribe), 'Email is required for subscription'),
|
|
189
|
+
dateOrder('start', 'end', 'End date must be after start'),
|
|
190
|
+
],
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Return a string or an issue without `field` to show a form-level error. In `getErrors()`, it uses
|
|
195
|
+
`FORM_ERROR_FIELD` (`"_form"`).
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
form.setErrors({
|
|
199
|
+
email: 'Email is already used',
|
|
200
|
+
[FORM_ERROR_FIELD]: 'Please check the form',
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Rule DX helpers
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
import { createLengthValidator, createPatternValidator, registerFieldValidator } from 'form-father';
|
|
208
|
+
|
|
209
|
+
registerFieldValidator('starts-with-a', value => value.startsWith('A'), 'Must start with A');
|
|
210
|
+
registerValidator('slug', createPatternValidator(/^[a-z0-9-]+$/), 'Slug characters only');
|
|
211
|
+
registerValidator('short-name', createLengthValidator({ min: 2, max: 24 }), 'Use 2-24 characters');
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Schema Adapters
|
|
215
|
+
|
|
216
|
+
Form Father has no schema-library runtime dependency, but can wrap Zod/Valibot/Yup-like schemas with `safeParse()` or
|
|
217
|
+
`parse()`.
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
import { registerSchemaValidator } from 'form-father';
|
|
221
|
+
|
|
222
|
+
registerSchemaValidator('company-email', z.string().email(), 'Use a company email');
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
For simple checks, use `createFieldValidator(predicate, message)`.
|
|
226
|
+
|
|
227
|
+
## Demos and Recipes
|
|
228
|
+
|
|
229
|
+
- `demos/index.html` is a static login/callback/search/upload/public API demo after `npm run build`.
|
|
230
|
+
- `npm run demos` builds the package and starts the Vite demo server.
|
|
231
|
+
- `docs/api/README.md` is a compact reference for public API, options, methods, and exports.
|
|
232
|
+
- `docs/demo/README.md` explains manual demo checks and demo form coverage.
|
|
233
|
+
- `docs/recipes/README.md` contains short API, data-attribute, adapter, and server-error recipes.
|
|
234
|
+
- `CHANGELOG.md` tracks releases and coverage baselines.
|
|
235
|
+
|
|
236
|
+
## Helpers
|
|
237
|
+
|
|
238
|
+
The library provides a number of utility functions:
|
|
239
|
+
|
|
240
|
+
- **serializeToFormData($element)**: Serializes form data into a `FormData` object.
|
|
241
|
+
- **isEmailValid(value)**: Checks if a string is a valid email address.
|
|
242
|
+
- **isUrlValid(value)**: Checks an `http(s)` URL, domain, IP, or `localhost`; the scheme may be omitted.
|
|
243
|
+
- **isPhoneValid(value)**: Checks if a string is a valid phone number.
|
|
244
|
+
- **closest($el, selector)**: Finds the closest parent element matching the given selector.
|
|
245
|
+
- **blockScrollBody()**: Blocks page scrolling.
|
|
246
|
+
- **unblockScrollBody()**: Unblocks page scrolling.
|
|
247
|
+
- **parseCommonResponseProperties(responseBody)**: Processes common properties from the server response.
|
|
248
|
+
- **serializeFormToJSON(form)**: Serializes form data into a plain object.
|
|
249
|
+
|
|
250
|
+
## Build and Development
|
|
251
|
+
|
|
252
|
+
**Rollup** is used for building the project. Main commands:
|
|
253
|
+
|
|
254
|
+
- **npm run build**: Builds the project.
|
|
255
|
+
- **npm run watch**: Builds the project in watch mode.
|
|
256
|
+
- **npm run docs:check**: Checks docs links, version references, demo files, and package README sync.
|
|
257
|
+
- **npm run demos**: Runs a demo version using Vite.
|
|
258
|
+
|
|
259
|
+
## Contributing
|
|
260
|
+
|
|
261
|
+
We welcome your suggestions and improvements! Please create [issues](https://github.com/poliklot/form-father/issues) and
|
|
262
|
+
submit [pull requests](https://github.com/poliklot/form-father/pulls).
|
|
263
|
+
|
|
264
|
+
## License
|
|
265
|
+
|
|
266
|
+
[MIT](LICENSE)
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
© 2024 Poliklot
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Form Father Recipes
|
|
2
|
+
|
|
3
|
+
## Init several forms
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import Form from 'form-father';
|
|
7
|
+
|
|
8
|
+
const forms = Form.initAll('form[data-form-father]', {
|
|
9
|
+
inputWrapperSelector: '.field',
|
|
10
|
+
validateOn: ['blur', 'change'],
|
|
11
|
+
revalidateOn: ['input', 'change'],
|
|
12
|
+
});
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## HTML-only rules
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<input
|
|
19
|
+
name="email"
|
|
20
|
+
type="email"
|
|
21
|
+
data-validate="required|email"
|
|
22
|
+
data-error-required="Email is required"
|
|
23
|
+
data-error-email="Enter a valid email"
|
|
24
|
+
data-error-container="#email-error"
|
|
25
|
+
/>
|
|
26
|
+
<small id="email-error" hidden></small>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Programmatic field API
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
const form = new Form(document.querySelector('form')!);
|
|
33
|
+
|
|
34
|
+
await form.validateField('email');
|
|
35
|
+
form.showFieldError('email', 'This email is already used', 'server');
|
|
36
|
+
console.log(form.getErrors());
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## State helpers
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
form.setValues({
|
|
43
|
+
email: 'user@example.com',
|
|
44
|
+
interests: ['docs', 'api'],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
console.log(form.getValues());
|
|
48
|
+
form.clearErrors();
|
|
49
|
+
form.reset();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Cross-field validation
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { dateOrder, requiredIf, sameAsField } from 'form-father';
|
|
56
|
+
|
|
57
|
+
const form = new Form(document.querySelector('form')!, {
|
|
58
|
+
formValidators: [
|
|
59
|
+
sameAsField('passwordConfirm', 'password', 'Passwords do not match'),
|
|
60
|
+
requiredIf('email', ({ values }) => Boolean(values.subscribe), 'Email is required'),
|
|
61
|
+
dateOrder('start', 'end', 'End date must be after start'),
|
|
62
|
+
],
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Field rule helpers
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { createLengthValidator, createPatternValidator, registerFieldValidator, registerValidator } from 'form-father';
|
|
70
|
+
|
|
71
|
+
registerFieldValidator('starts-with-a', value => value.startsWith('A'), 'Must start with A');
|
|
72
|
+
registerValidator('slug', createPatternValidator(/^[a-z0-9-]+$/), 'Slug characters only');
|
|
73
|
+
registerValidator('short-name', createLengthValidator({ min: 2, max: 24 }), 'Use 2-24 characters');
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Applying backend errors
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { FORM_ERROR_FIELD } from 'form-father';
|
|
80
|
+
|
|
81
|
+
form.setErrors({
|
|
82
|
+
email: 'Email is already registered',
|
|
83
|
+
[FORM_ERROR_FIELD]: 'Please check the form',
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Async validation state
|
|
88
|
+
|
|
89
|
+
```css
|
|
90
|
+
[data-form-father-state="validating"] {
|
|
91
|
+
opacity: 0.7;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
[data-form-father-state="invalid"] {
|
|
95
|
+
border-color: #b3261e;
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Async validators are race-safe: if an older validation finishes after a newer one started, Form Father ignores the stale
|
|
100
|
+
result and keeps the latest field state.
|
|
101
|
+
|
|
102
|
+
## Zod-like schema adapter
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import { registerSchemaValidator } from 'form-father';
|
|
106
|
+
|
|
107
|
+
registerSchemaValidator('email-domain', z.string().email().endsWith('@company.com'), 'Use a company email');
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The adapter only expects a `safeParse(value)` or `parse(value)` method, so it can be used with Zod-like libraries without
|
|
111
|
+
adding runtime dependencies to Form Father.
|
|
112
|
+
|
|
113
|
+
## Server response errors
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"success": false,
|
|
118
|
+
"error": true,
|
|
119
|
+
"error-msg": "Please check the form",
|
|
120
|
+
"errors": [{ "name": "email", "error-msg": "Email is already registered" }]
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Field errors are shown through the same rendering path as client-side validation errors.
|
|
125
|
+
|
|
126
|
+
## Accessible error summary
|
|
127
|
+
|
|
128
|
+
```html
|
|
129
|
+
<form data-form-father novalidate>
|
|
130
|
+
<div data-form-father-summary hidden></div>
|
|
131
|
+
<label>
|
|
132
|
+
Email
|
|
133
|
+
<input class="input" name="email" data-validate="required|email" />
|
|
134
|
+
</label>
|
|
135
|
+
<button type="submit">Submit</button>
|
|
136
|
+
</form>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
const form = new Form(document.querySelector('form')!, {
|
|
141
|
+
inputWrapperSelector: 'label',
|
|
142
|
+
errorSummary: {
|
|
143
|
+
title: 'Please check the form',
|
|
144
|
+
focus: true,
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Inline errors are linked to fields with `aria-describedby` by default. Use `ariaDescribeErrors: false` if your design
|
|
150
|
+
system owns that relationship itself.
|
package/package.json
CHANGED
|
@@ -1,40 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "form-father",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Form Father: Библиотека для обработки форм",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "index.js",
|
|
7
|
-
"module": "index.js",
|
|
8
|
-
"types": "index.d.ts",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
|
-
"types": "./index.d.ts",
|
|
12
|
-
"import": "./index.js",
|
|
13
|
-
"default": "./index.js"
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
14
|
},
|
|
15
|
-
"./FormFather.min.js": "./FormFather.min.js"
|
|
15
|
+
"./FormFather.min.js": "./dist/FormFather.min.js"
|
|
16
16
|
},
|
|
17
|
-
"unpkg": "./FormFather.min.js",
|
|
18
|
-
"jsdelivr": "./FormFather.min.js",
|
|
17
|
+
"unpkg": "./dist/FormFather.min.js",
|
|
18
|
+
"jsdelivr": "./dist/FormFather.min.js",
|
|
19
19
|
"files": [
|
|
20
|
-
"
|
|
21
|
-
"index.d.ts",
|
|
22
|
-
"FormFather.min.js",
|
|
23
|
-
"*.map",
|
|
24
|
-
"types",
|
|
20
|
+
"dist",
|
|
25
21
|
"README.md",
|
|
26
22
|
"LICENSE",
|
|
27
|
-
"RESPONSE_API.md"
|
|
23
|
+
"RESPONSE_API.md",
|
|
24
|
+
"CHANGELOG.md",
|
|
25
|
+
"docs",
|
|
26
|
+
"demos"
|
|
28
27
|
],
|
|
29
28
|
"scripts": {
|
|
30
|
-
"build": "
|
|
29
|
+
"build": "npm run build:js && npm run build:types",
|
|
30
|
+
"build:js": "rollup -c",
|
|
31
|
+
"build:types": "tsc -p tsconfig.json --emitDeclarationOnly && rollup -c rollup.types.config.js",
|
|
31
32
|
"watch": "rollup -c -w",
|
|
32
|
-
"
|
|
33
|
+
"check": "npm run release:check",
|
|
34
|
+
"release:check": "npm test -- --runInBand --watchman=false && npm run docs:check && npm run build && npm run smoke:package && npm run pack:dry-run",
|
|
35
|
+
"docs:check": "node scripts/check-docs.mjs",
|
|
36
|
+
"test": "jest --coverage",
|
|
37
|
+
"prepublishOnly": "npm run release:check",
|
|
38
|
+
"pack:dry-run": "npm_config_cache=/tmp/form-father-npm-cache npm pack --dry-run",
|
|
39
|
+
"smoke:package": "node scripts/smoke-package.mjs",
|
|
33
40
|
"demos": "BUILD_TARGET=demos npm run build && concurrently --kill-others \"vite\" \"npm run watch\""
|
|
34
41
|
},
|
|
35
42
|
"repository": {
|
|
36
43
|
"type": "git",
|
|
37
|
-
"url": "
|
|
44
|
+
"url": "https://github.com/Poliklot/form-father"
|
|
38
45
|
},
|
|
39
46
|
"keywords": [
|
|
40
47
|
"form",
|
|
@@ -47,30 +54,37 @@
|
|
|
47
54
|
"author": "Poliklot",
|
|
48
55
|
"license": "MIT",
|
|
49
56
|
"bugs": {
|
|
50
|
-
"url": "https://github.com/
|
|
57
|
+
"url": "https://github.com/Poliklot/form-father/issues"
|
|
51
58
|
},
|
|
52
59
|
"sideEffects": false,
|
|
53
60
|
"devDependencies": {
|
|
54
61
|
"@babel/preset-env": "^7.26.0",
|
|
55
|
-
"@rollup/plugin-babel": "^
|
|
56
|
-
"@rollup/plugin-commonjs": "^
|
|
62
|
+
"@rollup/plugin-babel": "^7.1.0",
|
|
63
|
+
"@rollup/plugin-commonjs": "^29.0.3",
|
|
57
64
|
"@rollup/plugin-json": "^6.1.0",
|
|
58
|
-
"@rollup/plugin-node-resolve": "^
|
|
59
|
-
"@rollup/plugin-terser": "^0.
|
|
60
|
-
"
|
|
65
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
66
|
+
"@rollup/plugin-terser": "^1.0.0",
|
|
67
|
+
"@types/jest": "^30.0.0",
|
|
68
|
+
"@types/jsdom": "^21.1.7",
|
|
61
69
|
"concurrently": "^9.0.1",
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
70
|
+
"esbuild": "^0.28.1",
|
|
71
|
+
"jest": "^30.0.5",
|
|
72
|
+
"jest-environment-jsdom": "^30.0.5",
|
|
73
|
+
"jsdom": "^26.1.0",
|
|
74
|
+
"postcss-preset-env": "^11.3.1",
|
|
66
75
|
"prettier-plugin-jsdoc": "^1.3.0",
|
|
67
76
|
"rollup": "^4.24.3",
|
|
68
77
|
"rollup-plugin-copy": "^3.5.0",
|
|
69
78
|
"rollup-plugin-delete": "^2.1.0",
|
|
70
79
|
"rollup-plugin-dts": "^6.1.1",
|
|
71
|
-
"rollup-plugin-
|
|
80
|
+
"rollup-plugin-esbuild": "^6.2.1",
|
|
72
81
|
"sass-embedded": "^1.80.5",
|
|
73
|
-
"
|
|
74
|
-
"
|
|
82
|
+
"ts-jest": "^29.4.0",
|
|
83
|
+
"ts-node": "^10.9.2",
|
|
84
|
+
"typescript": "^6.0.3",
|
|
85
|
+
"vite": "^8.0.16"
|
|
86
|
+
},
|
|
87
|
+
"overrides": {
|
|
88
|
+
"js-yaml": "4.2.0"
|
|
75
89
|
}
|
|
76
|
-
}
|
|
90
|
+
}
|
package/FormFather.min.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
var FormFather=function(t){"use strict";function e(t,e,r,i){return new(r||(r=Promise))((function(n,o){function s(t){try{l(i.next(t))}catch(t){o(t)}}function a(t){try{l(i.throw(t))}catch(t){o(t)}}function l(t){var e;t.done?n(t.value):(e=t.value,e instanceof r?e:new r((function(t){t(e)}))).then(s,a)}l((i=i.apply(t,e||[])).next())}))}function r(t,e){if(null==t)return null;let r=t;for(;r;){if(r.matches(e))return r;r=r.parentElement}return null}"function"==typeof SuppressedError&&SuppressedError;const i=["http:","https:"];function n(t){return t.length>0&&t.length<=63&&/^[a-z\d-]+$/i.test(t)&&!t.startsWith("-")&&!t.endsWith("-")}function o(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(n)}(t))}function s(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 i.includes(t.protocol)&&o(t.hostname)}catch(t){return!1}}function a(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 n;if("select-one"===i||"select-multiple"===i){const o=t;if("select-multiple"===i){Array.from(o.selectedOptions).map((t=>t.value)).forEach(((t,i)=>{r.append(`${e}[${i}]`,t)}))}else n=o.value,n&&r.append(e,n)}else if("tel"===i)n=t.value.split(/[\s()-]/).join("").replace(/^8/,"+7");else if("checkbox"===i||"radio"===i){if(!t.checked)return;n=t.value}else if("date"===i){const e=t.getAttribute("data-date-format");n=e?function(t,e){const r=new Date(t),i=t=>t.toString().padStart(2,"0"),n=i(r.getDate()),o=i(r.getMonth()+1),s=r.getFullYear().toString();return e.replace("dd",n).replace("mm",o).replace("yyyy",s)}(t.value,e):t.value}else n=t.value;n&&"select-one"!==i&&"select-multiple"!==i&&r.append(e,n)}})),r}function l(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 d(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"),h=null!==(c=globalThis[u])&&void 0!==c?c:globalThis[u]=new Map;function m(t,e,r,{override:i=!1}={}){!h.has(t)||i?h.set(t,{fn:e,defaultMessage:r}):console.warn(`[FormFather] Validator "${t}" already exists; pass { override: true } to replace`)}const f=t=>h.get(t);function p(t,e,r){h.has(t)||h.set(t,{fn:e,defaultMessage:r})}function g(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?void 0:e.message;if(t)return t}return"string"==typeof t.message?t.message:void 0}(t)}function y(t,r={}){return(i,n,o,s)=>e(this,void 0,void 0,(function*(){const e=r.mapValue?r.mapValue(i,n,o,s):i;try{if("function"==typeof t)return a=yield t(e),l=g(void 0,r.message),"object"==typeof a&&null!==a?a:void 0===a||!!a||{valid:!1,message:l};if("function"==typeof t.safeParse){const i=yield t.safeParse(e);return!!i.success||{valid:!1,message:g(i.error||i,r.message)}}if("function"==typeof t.parse)return yield t.parse(e),!0}catch(t){return{valid:!1,message:g(t,r.message)}}var a,l;return{valid:!1,message:g(void 0,r.message)}}))}function b(t,r){return(i,n,o,s)=>e(this,void 0,void 0,(function*(){return!!(yield t(i,n,o,s))||{valid:!1,message:r}}))}var v;p("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}),"Пустое значение"),p("email",(t=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)),"Неверный формат"),p("tel",(t=>/^\+7\d{10}$/.test(t)),"Неверный формат"),p("url",(t=>s(t)),"Неверный формат"),p("not-numbers",(t=>!/[0-9]/.test(t)),"Неверный формат");const E=Symbol.for("formfather.shared"),S=null!==(v=globalThis[E])&&void 0!==v?v:globalThis[E]={defaultParams:{},defaultValidationSchema:void 0},w="_form";function A(t,e){return t[e]}function F(t){return Array.isArray(t)?0===t.length:null==t||0===String(t).trim().length}function V(t,r){return i=>e(this,void 0,void 0,(function*(){return!!(yield t(i))||function(t,r,i,n){return e(this,void 0,void 0,(function*(){const e="function"==typeof r?yield r(t):r;return"string"==typeof e?{field:i,rule:n,message:e}:Object.assign({field:i,rule:n},e)}))}(i,r)}))}let $=0,T=0;const O=new WeakMap,_="data-form-father-describedby-id",x="data-form-father-added-describedby-id",k="data-form-father-summary",L={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 C{static get defaultParams(){return S.defaultParams}static set defaultParams(t){S.defaultParams=t}static get defaultValidationSchema(){var t;return null!==(t=S.defaultValidationSchema)&&void 0!==t?t:S.defaultValidationSchema=L}static set defaultValidationSchema(t){S.defaultValidationSchema=t}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},C.defaultParams,e),this.$el?(this.$submits=this.findSubmitElements(),this.isCorrectArguments()&&(this.bindSubmitHandlers(),this.initialization(),this.bindFieldValidationEvents(),this.setupMutationObserver(),O.set(this.$el,this))):console.warn("Empty $el")}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=O.get(t);return r?(r.updateOptions(e),r):new C(t,e)}))}updateOptions(t){return this.options=Object.assign(Object.assign({},this.options),t),this.config=Object.assign(Object.assign({},this.config),t),this.refreshControls(),this.bindSubmitHandlers(),this.bindFieldValidationEvents(),this.setupMutationObserver(),this}getErrors(){return this.errors.map((t=>Object.assign({},t)))}getValues(){return d(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?[Object.assign(Object.assign({},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,n=t.field||w;if(n===w)return this.setErrorRecord(w,i,t.message,r),this.showErrorForm(t.message),null;const o=this.findFieldByName(n);return o?(this.showFieldError(o,t.message,r,i),o):(this.setErrorRecord(n,i,t.message,r),console.warn(`Не найдено поле с именем: ${n}, для вывода ошибки: ${t.message}`),null)}setErrors(t,e="server"){return this.normalizeValidationIssues(t,e).forEach((t=>{this.applyValidationIssue(t,e)})),this.syncErrorSummary(),this}validateFormValidators(){return e(this,void 0,void 0,(function*(){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=yield i({form:this,formElement:this.$el,values:this.getValues(),errors:this.getErrors()}),n=this.normalizeValidationIssues(t,"client");0!==n.length&&(r=!1,n.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=Object.assign(Object.assign({},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=t=>e(this,void 0,void 0,(function*(){yield 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=t=>e(this,void 0,void 0,(function*(){yield this.handleSubmitEvent(t)})),this.$el.addEventListener("submit",this._onSubmitHandler)}handleSubmitEvent(t){return e(this,void 0,void 0,(function*(){var e,r,i,n,o,s;t.preventDefault(),null===(r=(e=this.config).onBeforeValidate)||void 0===r||r.call(e,this);const a=yield this.validate();return null===(n=(i=this.config).onAfterValidate)||void 0===n||n.call(i,a,this),a?this.submit():(null===(s=(o=this.config).onValidationError)||void 0===s||s.call(o,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 n=setTimeout(i,r);this.liveValidationTimers.set(t,n)}setupMutationObserver(){var t;null===(t=this.mutationObserver)||void 0===t||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))}-${++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,e){t.setAttribute("aria-invalid","true");const i=this.getErrorContainer(t);if(i)return i.textContent=e,this.addErrorDescription(t,i),void i.removeAttribute("hidden");const n=r(t,this.config.inputWrapperSelector);if(!n)return void console.warn(`Не найдена обёртка для поля с именем: ${t.name||t.type}`);const o=n.showError;if("function"==typeof o){o.call(n,e);const r=n.querySelector("[data-form-father-error]");return void(r&&this.addErrorDescription(t,r))}n.classList.add("input__wrapper--error");let s=n.querySelector("[data-form-father-error]");s||(s=document.createElement("div"),s.setAttribute("data-form-father-error",""),s.setAttribute("role","alert"),t.insertAdjacentElement("afterend",s)),s.textContent=e,this.addErrorDescription(t,s)}showLoader(){this.$submits.forEach((t=>{if(t.classList.add("button--loading"),t.querySelector(".button__loader"))return;const e="form-father-loader-"+$++,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 e;const i=r(t,this.config.inputWrapperSelector)||t.parentElement,n=this.getErrorContainer(t);t.removeAttribute("aria-invalid"),this.removeErrorDescription(t),n&&(n.textContent="",n.setAttribute("hidden","")),null===(e=null==i?void 0:i.querySelector("[data-form-father-error]"))||void 0===e||e.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 n=document.createElement("ul");n.setAttribute("data-form-father-summary-list",""),e.forEach((t=>{const e=document.createElement("li"),r=t.field===w?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)}n.appendChild(e)})),t.append(i,n)}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 n=null===(e=r.render)||void 0===e?void 0:e.call(r,t.map((t=>Object.assign({},t))),this);n instanceof HTMLElement?i.replaceChildren(n):"string"==typeof n?i.innerHTML=n: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 n=e.findIndex((e=>e===t));n<i&&(i=n,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)||void 0===e||e.call(i,{preventScroll:!0})}getValidationSchema(){return Object.assign(Object.assign({},C.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 n=this.parseValidationAttribute(t.getAttribute("data-validate")).filter((t=>{const e=this.getRuleName(t);return f(e)||!r.has(e)}));i.push(...n),i.push(...this.parseValidationAttribute(t.getAttribute("data-custom-validate")));const o=[],s=new Set;for(const t of i){const e=this.getRuleName(t);if(!f(e)){console.warn(`[FormFather] Unknown validation rule "${e}"`);continue}const r=this.getRuleIdentity(t);s.has(r)||(s.add(r),o.push(t))}return o}getRuleMessage(t,e,r,i,n){const o=t.getAttribute(`data-error-${e}`);return n||o||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[n,o]of Object.entries(e))this.fieldMatchesSchemaDefinition(t,n,o)&&(r.push(...o.rules),Object.assign(i,o.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[n,o]of Object.entries(e))if(o.selector)try{i(t.querySelectorAll(o.selector))}catch(t){console.warn(`[FormFather] Invalid validation selector "${o.selector}"`)}else this.getAllFields(t).filter((t=>this.parseValidationAttribute(t.getAttribute("data-validate")).some((t=>this.getRuleName(t)===n)))).forEach((t=>r.add(t)));return i(t.querySelectorAll("[required], [data-validate], [data-custom-validate]")),Array.from(r)}validateInput(t,r){return e(this,arguments,void 0,(function*(t,e,r={},i=this.$el,n){if(t.hasAttribute("data-no-validate"))return!0;const o=t instanceof HTMLInputElement&&("checkbox"===t.type||"radio"===t.type)?!t.checked:0===t.value.trim().length,s=e.some((t=>"required"===this.getRuleName(t)));if(o&&!s)return this.isLatestFieldValidation(t,n)?(this.clearFieldErrorRecord(t),this.hideError(t),!0):!this.hasFieldError(t);for(const o of e){const{rule:e,params:s}="string"==typeof o?{rule:o,params:void 0}:{rule:o.rule,params:o.params},a=f(e);if(!a)continue;const l=yield a.fn(t.value,t,i,s),d="object"==typeof l&&null!==l,c=d?!0===l.valid:!!l;if(!this.isLatestFieldValidation(t,n))return!this.hasFieldError(t);if(d&&l.effect&&(yield l.effect({value:t.value,$input:t,$form:i,params:s})),!this.isLatestFieldValidation(t,n))return!this.hasFieldError(t);if(!c){const i=l,n=this.getRuleMessage(t,e,r,a.defaultMessage,d&&"string"==typeof i.message?i.message:void 0);return this.setValidationError(t,e,n,"client"),this.showError(t,n),!1}if(d&&l.stopOthers)return!0}return this.isLatestFieldValidation(t,n)?(this.clearFieldErrorRecord(t),this.hideError(t),!0):!this.hasFieldError(t)}))}runFieldValidation(t,r){return e(this,arguments,void 0,(function*(t,e,r={},i=this.$el){const n=this.startFieldValidation(t);try{const o=yield this.validateInput(t,e,r,i,n);return this.finishFieldValidation(t,n,o),o}catch(e){throw this.isLatestFieldValidation(t,n)&&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 n=this.resolveField(t);return n?(this.setValidationError(n,i,e,r),this.showError(n,e),n.removeAttribute("aria-busy"),this.setFieldValidationState(n,"invalid"),this.syncErrorSummary(),!0):(console.warn(`[FormFather] Field "${String(t)}" not found`),!1)}validateField(t){return e(this,void 0,void 0,(function*(){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),n=yield this.runFieldValidation(e,r,i);return this.syncErrorSummary(),n}))}validate(){return e(this,arguments,void 0,(function*(t=this.$el){const e=this.getValidationSchema(),r=[],i=new WeakSet;let n=!0;this.errors=[];for(const o of this.getValidationFields(t,e)){if(i.has(o))continue;const s=this.getRadioGroup(o,t);s.length>0?s.forEach((t=>i.add(t))):i.add(o);const{rules:a,messages:l}=this.getFieldValidationConfig(o,e);(yield this.runFieldValidation(o,a,l,t))||(n=!1,r.push(o))}const o=yield this.validateFormValidators();o.ok||(n=!1,r.push(...o.erroredInputs));const s=r.filter((t=>!t.hasAttribute("data-no-error-scroll")));return!n&&this.config.scrollToFirstErroredInput&&this.scrollToFirstErroredInput(s),!n&&this.config.focusFirstErroredInput&&this.focusFirstErroredInput(s),n?this.hideErrorSummary():this.renderErrorSummary(),n}))}disableSubmit(){this.$submits.forEach((t=>{t.setAttribute("disabled","")}))}enableSubmit(){this.$submits.forEach((t=>{t.removeAttribute("disabled")}))}clearInputs(){var t;null===(t=this.inputs)||void 0===t||t.forEach((t=>{var e;const r=t;"radio"===r.type||"checkbox"===r.type?r.checked=!1:(r.value="",null===(e=r.parentElement)||void 0===e||e.classList.remove("filled"),r.dispatchEvent(new Event("input")))}))}_prefersReducedMotion(){var t;return"undefined"!=typeof window&&(null===(t=window.matchMedia)||void 0===t?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)||void 0===e||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 n=this.$el.querySelector(".error-block-under-input__wrapper");!n||i&&i.nextElementSibling!==n?(n=document.createElement("div"),n.className="error-block-under-input__wrapper",n.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(n.style,{boxSizing:"border-box",overflow:"hidden",height:"0px",paddingTop:"0px",opacity:"0"}),i?i.insertAdjacentElement("afterend",n):this.$el.appendChild(n)):(n.style.boxSizing="border-box",n.style.overflow="hidden"),n.querySelector(".error-block-under-input__main-text").textContent=t;const o=n.querySelector(".error-block-under-input__secondary-text");o&&(o.textContent=""),null===(e=n.querySelector(".error-block-under-input"))||void 0===e||e.classList.remove("error-block-under-input--success");const s=n.style.height,a=n.style.paddingTop;n.style.height="auto",n.style.paddingTop=`${this._anim.paddingTopOpened}px`;const l=n.clientHeight;if(n.style.height=s||"0px",n.style.paddingTop=a||"0px",parseFloat(n.style.height)===l&&1===parseFloat(n.style.opacity||"1"))this._ringIcon(n);else{if(this._cancelAnimations(n),this._canAnimate(n)){n.animate([{height:`${parseFloat(n.style.height)||0}px`,paddingTop:`${parseFloat(n.style.paddingTop)||0}px`,opacity:parseFloat(n.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",(()=>{n.style.height="auto",n.style.paddingTop=`${this._anim.paddingTopOpened}px`,n.style.opacity="1"}))}else n.style.height="auto",n.style.paddingTop=`${this._anim.paddingTopOpened}px`,n.style.opacity="1";this._ringIcon(n)}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)||void 0===t||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,O.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}parseResponseBody(t){return e(this,void 0,void 0,(function*(){try{const e=yield 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(w,"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()}}sendData(){return e(this,void 0,void 0,(function*(){var t;const e=this.$el.getAttribute("action")||"",r=(null===(t=this.$el.getAttribute("method"))||void 0===t?void 0:t.toUpperCase())||"POST",i=this.$el.getAttribute("enctype")||"application/x-www-form-urlencoded";let n=null,o={};const s=a(this.$el);let l=this.formDataToObject(s);if(this.config.logging)for(const t of s.keys())console.log(s.get(t));const d="function"==typeof this.config.wrapData;"function"==typeof this.config.wrapData&&(l=this.config.wrapData(l));const c=d?this.objectToFormData(l):s;if(this.isBodylessMethod(r))return fetch(this.appendQueryString(e,this.formDataToUrlSearchParams(c)),{method:r,headers:o});switch(i){case"application/x-www-form-urlencoded":n=this.formDataToUrlSearchParams(c).toString(),o["Content-Type"]="application/x-www-form-urlencoded";break;case"multipart/form-data":n=c;break;case"text/plain":n=this.formDataToTextBody(c),o["Content-Type"]="text/plain";break;case"application/json":n=JSON.stringify(l),o["Content-Type"]="application/json";break;default:console.warn(`Неизвестный enctype: ${i}. Используется application/x-www-form-urlencoded.`),n=this.formDataToUrlSearchParams(c).toString(),o["Content-Type"]="application/x-www-form-urlencoded"}return fetch(e,{method:r,body:n,headers:o})}))}submit(){return e(this,void 0,void 0,(function*(){var t,e,r,i,n,o,s,a,d,c,u,h;if(!this.waitResponse){let m=!1;const f=()=>{m||(this.waitResponse=!1,!0===this.config.showLoaderButton&&this.hideLoader(),m=!0)};try{if(!1===(yield null===(e=(t=this.config).onBeforeSubmit)||void 0===e?void 0:e.call(t,this)))return{success:!1};!0===this.config.showLoaderButton&&this.showLoader(),null===(i=(r=this.config).onSubmit)||void 0===i||i.call(r,this),this.waitResponse=!0;const u=yield this.sendData(),h=yield this.parseResponseBody(u);return f(),null===(o=(n=this.config).onResponse)||void 0===o||o.call(n,h,this),u.ok&&!0===h.success?null===(a=(s=this.config).onResponseSuccess)||void 0===a||a.call(s,h,this):(null===(c=(d=this.config).onResponseUnsuccess)||void 0===c||c.call(d,h,this),this.handleUnsuccessfulResponse(h)),l(h),{success:u.ok&&!0===h.success,response:u,responseBody:h}}catch(t){throw f(),null===(h=(u=this.config).onSubmitError)||void 0===h||h.call(u,t,this),t}}}))}}return t.FORM_ERROR_FIELD=w,t.blockScrollBody=function(){window.lastBlockScrollPosition=window.pageYOffset,document.body.style.top=-window.lastBlockScrollPosition+"px",document.body.classList.add("body--block-scroll")},t.closest=r,t.createFieldValidator=b,t.createFormValidator=V,t.createLengthValidator=function(t,e){return b((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 b((e=>(t.lastIndex=0,t.test(e))),e)},t.createSchemaValidator=y,t.dateOrder=function(t,e,r="Дата окончания должна быть позже даты начала",i="date-order"){return V((({values:r})=>{const i=A(r,t),n=A(r,e);return!(!F(i)&&!F(n))||Date.parse(String(i))<=Date.parse(String(n))}),{field:e,rule:i,message:r})},t.default=C,t.getAllValidators=function(){return new Map(h)},t.getValidator=f,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=s,t.parseCommonResponseProperties=l,t.registerFieldValidator=function(t,e,r,i={}){m(t,b(e),r,i)},t.registerSchemaValidator=function(t,e,r,i={}){const{override:n}=i;m(t,y(e,function(t,e){var r={};for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&e.indexOf(i)<0&&(r[i]=t[i]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var n=0;for(i=Object.getOwnPropertySymbols(t);n<i.length;n++)e.indexOf(i[n])<0&&Object.prototype.propertyIsEnumerable.call(t,i[n])&&(r[i[n]]=t[i[n]])}return r}(i,["override"])),r,{override:n})},t.registerValidator=m,t.requiredIf=function(t,r,i="Поле обязательно",n="conditional-required"){return V((i=>e(this,void 0,void 0,(function*(){return!(yield r(i))||!F(A(i.values,t))}))),{field:t,rule:n,message:i})},t.sameAsField=function(t,e,r="Значения не совпадают",i="same-as-field"){return V((({values:r})=>A(r,t)===A(r,e)),{field:t,rule:i,message:r})},t.serializeFormToJSON=d,t.serializeToFormData=a,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
|
package/FormFather.min.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FormFather.min.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|