@verbb/formie-browser 1.0.0
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 +5 -0
- package/LICENSE.md +40 -0
- package/README.md +34 -0
- package/dist/chunks/address-finder-DfMCiW89.js +47 -0
- package/dist/chunks/api-CbqEMQT5.js +181 -0
- package/dist/chunks/api-DE7LfK-R.js +960 -0
- package/dist/chunks/api-DOfDzYC_.js +538 -0
- package/dist/chunks/async-B3DUf1GZ.js +26 -0
- package/dist/chunks/bpoint-Ciy3yY9Q.js +34 -0
- package/dist/chunks/calculations-CkYAqO_-.js +197 -0
- package/dist/chunks/captcha-eu-DnOWhMwr.js +43 -0
- package/dist/chunks/checkbox-radio-0x7Tc0br.js +197 -0
- package/dist/chunks/chunk-K6L4z4UQ.js +24 -0
- package/dist/chunks/conditions-4fXKhEJS.js +609 -0
- package/dist/chunks/date-picker-B6iZkjHS.js +6204 -0
- package/dist/chunks/debug-KnZeKYBI.js +39 -0
- package/dist/chunks/dist-D09GnXMW.js +2663 -0
- package/dist/chunks/event-names-DamGPtXR.js +51 -0
- package/dist/chunks/eway-DEAYcwT0.js +86 -0
- package/dist/chunks/field-references.keys-BpBZ_quS.js +24 -0
- package/dist/chunks/field-references.resolver-Ba6xhiJC.js +183 -0
- package/dist/chunks/file-upload-Bh63PQSE.js +430 -0
- package/dist/chunks/friendly-captcha-v1-CqO4WVre.js +40 -0
- package/dist/chunks/friendly-captcha-v2-CyykcJcM.js +47 -0
- package/dist/chunks/friendly-challenge-Dg8XkStd.js +1101 -0
- package/dist/chunks/go-cardless-CuND59rR.js +26 -0
- package/dist/chunks/google-address--uR8WDSm.js +208 -0
- package/dist/chunks/hcaptcha-CmaFUesv.js +72 -0
- package/dist/chunks/hidden-CYnZYple.js +36 -0
- package/dist/chunks/http-18nn97DZ.js +29 -0
- package/dist/chunks/i18n-vUh-KGiH.js +55 -0
- package/dist/chunks/loqate-BICNJlVK.js +97 -0
- package/dist/chunks/mollie-DwlsgHZ1.js +26 -0
- package/dist/chunks/moneris-B_IFZFTx.js +159 -0
- package/dist/chunks/opayo-U2x_TOII.js +192 -0
- package/dist/chunks/paddle-BqXFrc79.js +75 -0
- package/dist/chunks/paypal-Cn_DYGDb.js +121 -0
- package/dist/chunks/payway-Rnq796eC.js +75 -0
- package/dist/chunks/phone-country-B6Me4lK0.js +3317 -0
- package/dist/chunks/place-kit-ldUl-u9w.js +56 -0
- package/dist/chunks/placekit-autocomplete.esm-D-lGeaZl.js +1771 -0
- package/dist/chunks/recaptcha-enterprise-DPJNyv1X.js +72 -0
- package/dist/chunks/recaptcha-shared-DTI4qWVR.js +22 -0
- package/dist/chunks/recaptcha-v2-checkbox-zFjpvJ5c.js +49 -0
- package/dist/chunks/recaptcha-v2-invisible-CnYtkNvz.js +62 -0
- package/dist/chunks/recaptcha-v3-EAlWhnkX.js +33 -0
- package/dist/chunks/repeater-CXD1eLSn.js +151 -0
- package/dist/chunks/rich-text-DkmZRhGj.js +442 -0
- package/dist/chunks/scripts-BGD_iU_6.js +41 -0
- package/dist/chunks/sdk-B7u9fTlP.js +2103 -0
- package/dist/chunks/shared-DC6_1u8X.js +85 -0
- package/dist/chunks/signature-E9KyYXS1.js +765 -0
- package/dist/chunks/snaptcha-CCDunGeb.js +8 -0
- package/dist/chunks/square-BLqK51rS.js +61 -0
- package/dist/chunks/stripe-B8gHpZNC.js +273 -0
- package/dist/chunks/styles-BIh6g7V_.js +22 -0
- package/dist/chunks/summary-EcNE0cvg.js +191 -0
- package/dist/chunks/table-yxEDL6kA.js +124 -0
- package/dist/chunks/text-limit-D0H_Ca2c.js +179 -0
- package/dist/chunks/theme-classes-vSHpdCUO.js +59 -0
- package/dist/chunks/turnstile-DP0bdR7T.js +68 -0
- package/dist/chunks/utils-ByrEVYrJ.js +49584 -0
- package/dist/css/formie-base.css +78 -0
- package/dist/css/formie-theme.css +19 -0
- package/dist/css/formie.css +2 -0
- package/dist/css/theme/_buttons.css +249 -0
- package/dist/css/theme/_loading.css +37 -0
- package/dist/css/theme/_messages.css +39 -0
- package/dist/css/theme/_progress.css +62 -0
- package/dist/css/theme/_tokens.css +361 -0
- package/dist/css/theme/_typography.css +70 -0
- package/dist/css/theme/fields/_address.css +17 -0
- package/dist/css/theme/fields/_check-radio.css +108 -0
- package/dist/css/theme/fields/_file.css +58 -0
- package/dist/css/theme/fields/_group.css +13 -0
- package/dist/css/theme/fields/_input.css +48 -0
- package/dist/css/theme/fields/_nested.css +19 -0
- package/dist/css/theme/fields/_repeater.css +69 -0
- package/dist/css/theme/fields/_rich-text.css +201 -0
- package/dist/css/theme/fields/_select.css +37 -0
- package/dist/css/theme/fields/_signature.css +39 -0
- package/dist/css/theme/fields/_summary.css +53 -0
- package/dist/css/theme/fields/_table.css +121 -0
- package/dist/css/theme/fields/_text-limit.css +10 -0
- package/dist/css/theme/forms/_field.css +62 -0
- package/dist/css/theme/forms/_form.css +166 -0
- package/dist/css/theme/integrations/_payment-modal.css +32 -0
- package/dist/css/theme/integrations/_paypal.css +10 -0
- package/dist/css/theme/integrations/_payway.css +10 -0
- package/dist/css/theme/integrations/_stripe.css +24 -0
- package/dist/css/theme/utilities/_accessibility.css +13 -0
- package/dist/css/theme-base/_controls.css +41 -0
- package/dist/css/theme-base/_primitives.css +34 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3206 -0
- package/dist/js/compatibility/dom-adapter.d.ts +12 -0
- package/dist/js/compatibility/dom-adapter.d.ts.map +1 -0
- package/dist/js/compatibility/event-map.d.ts +25 -0
- package/dist/js/compatibility/event-map.d.ts.map +1 -0
- package/dist/js/compatibility/validator-adapter.d.ts +17 -0
- package/dist/js/compatibility/validator-adapter.d.ts.map +1 -0
- package/dist/js/contracts/client.d.ts +40 -0
- package/dist/js/contracts/client.d.ts.map +1 -0
- package/dist/js/contracts/common.d.ts +5 -0
- package/dist/js/contracts/common.d.ts.map +1 -0
- package/dist/js/contracts/modules.d.ts +47 -0
- package/dist/js/contracts/modules.d.ts.map +1 -0
- package/dist/js/contracts/schema.d.ts +79 -0
- package/dist/js/contracts/schema.d.ts.map +1 -0
- package/dist/js/contracts/theme.d.ts +2 -0
- package/dist/js/contracts/theme.d.ts.map +1 -0
- package/dist/js/core/create-formie-client.d.ts +3 -0
- package/dist/js/core/create-formie-client.d.ts.map +1 -0
- package/dist/js/core/dom-events.d.ts +2 -0
- package/dist/js/core/dom-events.d.ts.map +1 -0
- package/dist/js/core/formie.d.ts +27 -0
- package/dist/js/core/formie.d.ts.map +1 -0
- package/dist/js/core/hydrate-modules.d.ts +22 -0
- package/dist/js/core/hydrate-modules.d.ts.map +1 -0
- package/dist/js/core/page-client-event.d.ts +9 -0
- package/dist/js/core/page-client-event.d.ts.map +1 -0
- package/dist/js/core/page-tab-errors.d.ts +2 -0
- package/dist/js/core/page-tab-errors.d.ts.map +1 -0
- package/dist/js/core/submit-flow.d.ts +21 -0
- package/dist/js/core/submit-flow.d.ts.map +1 -0
- package/dist/js/core/submit-result-state.d.ts +8 -0
- package/dist/js/core/submit-result-state.d.ts.map +1 -0
- package/dist/js/core/submit-result-ui.d.ts +10 -0
- package/dist/js/core/submit-result-ui.d.ts.map +1 -0
- package/dist/js/events/event-bus.d.ts +21 -0
- package/dist/js/events/event-bus.d.ts.map +1 -0
- package/dist/js/modules/address/address-finder.d.ts +2 -0
- package/dist/js/modules/address/address-finder.d.ts.map +1 -0
- package/dist/js/modules/address/api.d.ts +8 -0
- package/dist/js/modules/address/api.d.ts.map +1 -0
- package/dist/js/modules/address/constants.d.ts +15 -0
- package/dist/js/modules/address/constants.d.ts.map +1 -0
- package/dist/js/modules/address/factories.d.ts +35 -0
- package/dist/js/modules/address/factories.d.ts.map +1 -0
- package/dist/js/modules/address/google-address.d.ts +2 -0
- package/dist/js/modules/address/google-address.d.ts.map +1 -0
- package/dist/js/modules/address/host.d.ts +30 -0
- package/dist/js/modules/address/host.d.ts.map +1 -0
- package/dist/js/modules/address/index.d.ts +3 -0
- package/dist/js/modules/address/index.d.ts.map +1 -0
- package/dist/js/modules/address/loqate.d.ts +2 -0
- package/dist/js/modules/address/loqate.d.ts.map +1 -0
- package/dist/js/modules/address/place-kit.d.ts +2 -0
- package/dist/js/modules/address/place-kit.d.ts.map +1 -0
- package/dist/js/modules/captchas/api.d.ts +9 -0
- package/dist/js/modules/captchas/api.d.ts.map +1 -0
- package/dist/js/modules/captchas/captcha-eu.d.ts +2 -0
- package/dist/js/modules/captchas/captcha-eu.d.ts.map +1 -0
- package/dist/js/modules/captchas/constants.d.ts +5 -0
- package/dist/js/modules/captchas/constants.d.ts.map +1 -0
- package/dist/js/modules/captchas/factories.d.ts +63 -0
- package/dist/js/modules/captchas/factories.d.ts.map +1 -0
- package/dist/js/modules/captchas/friendly-captcha-v1.d.ts +2 -0
- package/dist/js/modules/captchas/friendly-captcha-v1.d.ts.map +1 -0
- package/dist/js/modules/captchas/friendly-captcha-v2.d.ts +2 -0
- package/dist/js/modules/captchas/friendly-captcha-v2.d.ts.map +1 -0
- package/dist/js/modules/captchas/hcaptcha.d.ts +2 -0
- package/dist/js/modules/captchas/hcaptcha.d.ts.map +1 -0
- package/dist/js/modules/captchas/host.d.ts +78 -0
- package/dist/js/modules/captchas/host.d.ts.map +1 -0
- package/dist/js/modules/captchas/index.d.ts +3 -0
- package/dist/js/modules/captchas/index.d.ts.map +1 -0
- package/dist/js/modules/captchas/recaptcha-enterprise.d.ts +2 -0
- package/dist/js/modules/captchas/recaptcha-enterprise.d.ts.map +1 -0
- package/dist/js/modules/captchas/recaptcha-shared.d.ts +26 -0
- package/dist/js/modules/captchas/recaptcha-shared.d.ts.map +1 -0
- package/dist/js/modules/captchas/recaptcha-v2-checkbox.d.ts +2 -0
- package/dist/js/modules/captchas/recaptcha-v2-checkbox.d.ts.map +1 -0
- package/dist/js/modules/captchas/recaptcha-v2-invisible.d.ts +2 -0
- package/dist/js/modules/captchas/recaptcha-v2-invisible.d.ts.map +1 -0
- package/dist/js/modules/captchas/recaptcha-v3.d.ts +2 -0
- package/dist/js/modules/captchas/recaptcha-v3.d.ts.map +1 -0
- package/dist/js/modules/captchas/snaptcha.d.ts +2 -0
- package/dist/js/modules/captchas/snaptcha.d.ts.map +1 -0
- package/dist/js/modules/captchas/turnstile.d.ts +2 -0
- package/dist/js/modules/captchas/turnstile.d.ts.map +1 -0
- package/dist/js/modules/captchas/utils.d.ts +13 -0
- package/dist/js/modules/captchas/utils.d.ts.map +1 -0
- package/dist/js/modules/fields/calculations.d.ts +3 -0
- package/dist/js/modules/fields/calculations.d.ts.map +1 -0
- package/dist/js/modules/fields/checkbox-radio.d.ts +3 -0
- package/dist/js/modules/fields/checkbox-radio.d.ts.map +1 -0
- package/dist/js/modules/fields/conditions/config.d.ts +5 -0
- package/dist/js/modules/fields/conditions/config.d.ts.map +1 -0
- package/dist/js/modules/fields/conditions/effects.d.ts +2 -0
- package/dist/js/modules/fields/conditions/effects.d.ts.map +1 -0
- package/dist/js/modules/fields/conditions/evaluator.d.ts +6 -0
- package/dist/js/modules/fields/conditions/evaluator.d.ts.map +1 -0
- package/dist/js/modules/fields/conditions/references.d.ts +5 -0
- package/dist/js/modules/fields/conditions/references.d.ts.map +1 -0
- package/dist/js/modules/fields/conditions/transforms.d.ts +3 -0
- package/dist/js/modules/fields/conditions/transforms.d.ts.map +1 -0
- package/dist/js/modules/fields/conditions/types.d.ts +30 -0
- package/dist/js/modules/fields/conditions/types.d.ts.map +1 -0
- package/dist/js/modules/fields/conditions/values.d.ts +5 -0
- package/dist/js/modules/fields/conditions/values.d.ts.map +1 -0
- package/dist/js/modules/fields/conditions.d.ts +3 -0
- package/dist/js/modules/fields/conditions.d.ts.map +1 -0
- package/dist/js/modules/fields/date-picker.d.ts +3 -0
- package/dist/js/modules/fields/date-picker.d.ts.map +1 -0
- package/dist/js/modules/fields/file-upload.d.ts +3 -0
- package/dist/js/modules/fields/file-upload.d.ts.map +1 -0
- package/dist/js/modules/fields/hidden.d.ts +3 -0
- package/dist/js/modules/fields/hidden.d.ts.map +1 -0
- package/dist/js/modules/fields/index.d.ts +3 -0
- package/dist/js/modules/fields/index.d.ts.map +1 -0
- package/dist/js/modules/fields/phone-country.d.ts +3 -0
- package/dist/js/modules/fields/phone-country.d.ts.map +1 -0
- package/dist/js/modules/fields/repeater.d.ts +3 -0
- package/dist/js/modules/fields/repeater.d.ts.map +1 -0
- package/dist/js/modules/fields/rich-text.d.ts +3 -0
- package/dist/js/modules/fields/rich-text.d.ts.map +1 -0
- package/dist/js/modules/fields/shared.d.ts +20 -0
- package/dist/js/modules/fields/shared.d.ts.map +1 -0
- package/dist/js/modules/fields/signature.d.ts +3 -0
- package/dist/js/modules/fields/signature.d.ts.map +1 -0
- package/dist/js/modules/fields/summary.d.ts +3 -0
- package/dist/js/modules/fields/summary.d.ts.map +1 -0
- package/dist/js/modules/fields/table.d.ts +3 -0
- package/dist/js/modules/fields/table.d.ts.map +1 -0
- package/dist/js/modules/fields/text-limit.d.ts +3 -0
- package/dist/js/modules/fields/text-limit.d.ts.map +1 -0
- package/dist/js/modules/loader.d.ts +11 -0
- package/dist/js/modules/loader.d.ts.map +1 -0
- package/dist/js/modules/payments/api.d.ts +8 -0
- package/dist/js/modules/payments/api.d.ts.map +1 -0
- package/dist/js/modules/payments/bpoint.d.ts +2 -0
- package/dist/js/modules/payments/bpoint.d.ts.map +1 -0
- package/dist/js/modules/payments/constants.d.ts +5 -0
- package/dist/js/modules/payments/constants.d.ts.map +1 -0
- package/dist/js/modules/payments/eway.d.ts +9 -0
- package/dist/js/modules/payments/eway.d.ts.map +1 -0
- package/dist/js/modules/payments/factories.d.ts +54 -0
- package/dist/js/modules/payments/factories.d.ts.map +1 -0
- package/dist/js/modules/payments/go-cardless.d.ts +2 -0
- package/dist/js/modules/payments/go-cardless.d.ts.map +1 -0
- package/dist/js/modules/payments/host.d.ts +70 -0
- package/dist/js/modules/payments/host.d.ts.map +1 -0
- package/dist/js/modules/payments/index.d.ts +3 -0
- package/dist/js/modules/payments/index.d.ts.map +1 -0
- package/dist/js/modules/payments/mollie.d.ts +2 -0
- package/dist/js/modules/payments/mollie.d.ts.map +1 -0
- package/dist/js/modules/payments/moneris.d.ts +2 -0
- package/dist/js/modules/payments/moneris.d.ts.map +1 -0
- package/dist/js/modules/payments/opayo.d.ts +25 -0
- package/dist/js/modules/payments/opayo.d.ts.map +1 -0
- package/dist/js/modules/payments/paddle.d.ts +2 -0
- package/dist/js/modules/payments/paddle.d.ts.map +1 -0
- package/dist/js/modules/payments/paypal.d.ts +2 -0
- package/dist/js/modules/payments/paypal.d.ts.map +1 -0
- package/dist/js/modules/payments/payway.d.ts +2 -0
- package/dist/js/modules/payments/payway.d.ts.map +1 -0
- package/dist/js/modules/payments/square.d.ts +2 -0
- package/dist/js/modules/payments/square.d.ts.map +1 -0
- package/dist/js/modules/payments/stripe.d.ts +2 -0
- package/dist/js/modules/payments/stripe.d.ts.map +1 -0
- package/dist/js/modules/payments/utils.d.ts +17 -0
- package/dist/js/modules/payments/utils.d.ts.map +1 -0
- package/dist/js/modules/registry.d.ts +9 -0
- package/dist/js/modules/registry.d.ts.map +1 -0
- package/dist/js/modules/styles.d.ts +2 -0
- package/dist/js/modules/styles.d.ts.map +1 -0
- package/dist/js/submit/pipeline.d.ts +18 -0
- package/dist/js/submit/pipeline.d.ts.map +1 -0
- package/dist/js/theme/theme-classes.d.ts +10 -0
- package/dist/js/theme/theme-classes.d.ts.map +1 -0
- package/dist/js/transport/forms-api.d.ts +11 -0
- package/dist/js/transport/forms-api.d.ts.map +1 -0
- package/dist/js/utils/async.d.ts +8 -0
- package/dist/js/utils/async.d.ts.map +1 -0
- package/dist/js/utils/debug.d.ts +10 -0
- package/dist/js/utils/debug.d.ts.map +1 -0
- package/dist/js/utils/event-names.d.ts +13 -0
- package/dist/js/utils/event-names.d.ts.map +1 -0
- package/dist/js/utils/field-references.d.ts +6 -0
- package/dist/js/utils/field-references.d.ts.map +1 -0
- package/dist/js/utils/field-references.keys.d.ts +4 -0
- package/dist/js/utils/field-references.keys.d.ts.map +1 -0
- package/dist/js/utils/field-references.parser.d.ts +3 -0
- package/dist/js/utils/field-references.parser.d.ts.map +1 -0
- package/dist/js/utils/field-references.registry.d.ts +3 -0
- package/dist/js/utils/field-references.registry.d.ts.map +1 -0
- package/dist/js/utils/field-references.resolver.d.ts +4 -0
- package/dist/js/utils/field-references.resolver.d.ts.map +1 -0
- package/dist/js/utils/field-references.types.d.ts +27 -0
- package/dist/js/utils/field-references.types.d.ts.map +1 -0
- package/dist/js/utils/fields.d.ts +5 -0
- package/dist/js/utils/fields.d.ts.map +1 -0
- package/dist/js/utils/http.d.ts +9 -0
- package/dist/js/utils/http.d.ts.map +1 -0
- package/dist/js/utils/i18n.d.ts +7 -0
- package/dist/js/utils/i18n.d.ts.map +1 -0
- package/dist/js/utils/scripts.d.ts +13 -0
- package/dist/js/utils/scripts.d.ts.map +1 -0
- package/dist/js/utils/unload-warning.d.ts +10 -0
- package/dist/js/utils/unload-warning.d.ts.map +1 -0
- package/dist/js/validation/rules/email.d.ts +4 -0
- package/dist/js/validation/rules/email.d.ts.map +1 -0
- package/dist/js/validation/rules/match.d.ts +4 -0
- package/dist/js/validation/rules/match.d.ts.map +1 -0
- package/dist/js/validation/rules/number.d.ts +4 -0
- package/dist/js/validation/rules/number.d.ts.map +1 -0
- package/dist/js/validation/rules/required.d.ts +4 -0
- package/dist/js/validation/rules/required.d.ts.map +1 -0
- package/dist/js/validation/rules/shared.d.ts +7 -0
- package/dist/js/validation/rules/shared.d.ts.map +1 -0
- package/dist/js/validation/rules/url.d.ts +4 -0
- package/dist/js/validation/rules/url.d.ts.map +1 -0
- package/dist/js/validation/rules.d.ts +10 -0
- package/dist/js/validation/rules.d.ts.map +1 -0
- package/dist/js/validation/types.d.ts +44 -0
- package/dist/js/validation/types.d.ts.map +1 -0
- package/dist/js/validation/validator.d.ts +53 -0
- package/dist/js/validation/validator.d.ts.map +1 -0
- package/package.json +78 -0
- package/src/css/formie-base.css +78 -0
- package/src/css/formie-theme.css +19 -0
- package/src/css/formie.css +2 -0
- package/src/css/theme/_buttons.css +249 -0
- package/src/css/theme/_loading.css +37 -0
- package/src/css/theme/_messages.css +39 -0
- package/src/css/theme/_progress.css +62 -0
- package/src/css/theme/_tokens.css +361 -0
- package/src/css/theme/_typography.css +70 -0
- package/src/css/theme/fields/_address.css +17 -0
- package/src/css/theme/fields/_check-radio.css +108 -0
- package/src/css/theme/fields/_file.css +58 -0
- package/src/css/theme/fields/_group.css +13 -0
- package/src/css/theme/fields/_input.css +48 -0
- package/src/css/theme/fields/_nested.css +19 -0
- package/src/css/theme/fields/_repeater.css +69 -0
- package/src/css/theme/fields/_rich-text.css +201 -0
- package/src/css/theme/fields/_select.css +37 -0
- package/src/css/theme/fields/_signature.css +39 -0
- package/src/css/theme/fields/_summary.css +53 -0
- package/src/css/theme/fields/_table.css +121 -0
- package/src/css/theme/fields/_text-limit.css +10 -0
- package/src/css/theme/forms/_field.css +62 -0
- package/src/css/theme/forms/_form.css +166 -0
- package/src/css/theme/integrations/_payment-modal.css +32 -0
- package/src/css/theme/integrations/_paypal.css +10 -0
- package/src/css/theme/integrations/_payway.css +10 -0
- package/src/css/theme/integrations/_stripe.css +24 -0
- package/src/css/theme/utilities/_accessibility.css +13 -0
- package/src/css/theme-base/_controls.css +41 -0
- package/src/css/theme-base/_primitives.css +34 -0
- package/src/icons/rich-text/aligncenter.svg +6 -0
- package/src/icons/rich-text/alignleft.svg +6 -0
- package/src/icons/rich-text/alignright.svg +6 -0
- package/src/icons/rich-text/bold.svg +4 -0
- package/src/icons/rich-text/clear.svg +6 -0
- package/src/icons/rich-text/code.svg +4 -0
- package/src/icons/rich-text/heading1.svg +3 -0
- package/src/icons/rich-text/heading2.svg +3 -0
- package/src/icons/rich-text/image.svg +6 -0
- package/src/icons/rich-text/italic.svg +5 -0
- package/src/icons/rich-text/line.svg +3 -0
- package/src/icons/rich-text/link.svg +4 -0
- package/src/icons/rich-text/olist.svg +8 -0
- package/src/icons/rich-text/paragraph.svg +3 -0
- package/src/icons/rich-text/quote.svg +4 -0
- package/src/icons/rich-text/strikethrough.svg +4 -0
- package/src/icons/rich-text/ulist.svg +8 -0
- package/src/icons/rich-text/underline.svg +4 -0
- package/src/index.ts +125 -0
- package/src/js/compatibility/dom-adapter.ts +129 -0
- package/src/js/compatibility/event-map.ts +72 -0
- package/src/js/compatibility/validator-adapter.ts +105 -0
- package/src/js/contracts/client.ts +43 -0
- package/src/js/contracts/common.ts +14 -0
- package/src/js/contracts/modules.ts +53 -0
- package/src/js/contracts/schema.ts +83 -0
- package/src/js/contracts/theme.ts +1 -0
- package/src/js/core/create-formie-client.ts +1519 -0
- package/src/js/core/dom-events.ts +8 -0
- package/src/js/core/formie.ts +242 -0
- package/src/js/core/hydrate-modules.ts +102 -0
- package/src/js/core/page-client-event.ts +129 -0
- package/src/js/core/page-tab-errors.ts +37 -0
- package/src/js/core/submit-flow.ts +120 -0
- package/src/js/core/submit-result-state.ts +597 -0
- package/src/js/core/submit-result-ui.ts +448 -0
- package/src/js/events/event-bus.ts +109 -0
- package/src/js/modules/address/address-finder.ts +85 -0
- package/src/js/modules/address/api.ts +22 -0
- package/src/js/modules/address/constants.ts +15 -0
- package/src/js/modules/address/factories.ts +203 -0
- package/src/js/modules/address/google-address.ts +345 -0
- package/src/js/modules/address/host.ts +137 -0
- package/src/js/modules/address/index.ts +10 -0
- package/src/js/modules/address/loqate.ts +128 -0
- package/src/js/modules/address/place-kit.ts +94 -0
- package/src/js/modules/captchas/api.ts +25 -0
- package/src/js/modules/captchas/captcha-eu.ts +86 -0
- package/src/js/modules/captchas/constants.ts +4 -0
- package/src/js/modules/captchas/factories.ts +485 -0
- package/src/js/modules/captchas/friendly-captcha-v1.ts +65 -0
- package/src/js/modules/captchas/friendly-captcha-v2.ts +84 -0
- package/src/js/modules/captchas/hcaptcha.ts +153 -0
- package/src/js/modules/captchas/host.ts +448 -0
- package/src/js/modules/captchas/index.ts +16 -0
- package/src/js/modules/captchas/recaptcha-enterprise.ts +138 -0
- package/src/js/modules/captchas/recaptcha-shared.ts +69 -0
- package/src/js/modules/captchas/recaptcha-v2-checkbox.ts +72 -0
- package/src/js/modules/captchas/recaptcha-v2-invisible.ts +108 -0
- package/src/js/modules/captchas/recaptcha-v3.ts +62 -0
- package/src/js/modules/captchas/snaptcha.ts +10 -0
- package/src/js/modules/captchas/turnstile.ts +131 -0
- package/src/js/modules/captchas/utils.ts +85 -0
- package/src/js/modules/fields/calculations.ts +273 -0
- package/src/js/modules/fields/checkbox-radio.ts +295 -0
- package/src/js/modules/fields/conditions/config.ts +79 -0
- package/src/js/modules/fields/conditions/effects.ts +166 -0
- package/src/js/modules/fields/conditions/evaluator.ts +44 -0
- package/src/js/modules/fields/conditions/references.ts +165 -0
- package/src/js/modules/fields/conditions/transforms.ts +206 -0
- package/src/js/modules/fields/conditions/types.ts +33 -0
- package/src/js/modules/fields/conditions/values.ts +115 -0
- package/src/js/modules/fields/conditions.ts +229 -0
- package/src/js/modules/fields/date-picker.ts +272 -0
- package/src/js/modules/fields/file-upload.ts +628 -0
- package/src/js/modules/fields/hidden.ts +58 -0
- package/src/js/modules/fields/index.ts +19 -0
- package/src/js/modules/fields/phone-country.ts +226 -0
- package/src/js/modules/fields/repeater.ts +231 -0
- package/src/js/modules/fields/rich-text.ts +217 -0
- package/src/js/modules/fields/shared.ts +238 -0
- package/src/js/modules/fields/signature.ts +202 -0
- package/src/js/modules/fields/summary.ts +272 -0
- package/src/js/modules/fields/table.ts +197 -0
- package/src/js/modules/fields/text-limit.ts +280 -0
- package/src/js/modules/loader.ts +331 -0
- package/src/js/modules/payments/api.ts +20 -0
- package/src/js/modules/payments/bpoint.ts +48 -0
- package/src/js/modules/payments/constants.ts +17 -0
- package/src/js/modules/payments/eway.ts +132 -0
- package/src/js/modules/payments/factories.ts +332 -0
- package/src/js/modules/payments/go-cardless.ts +37 -0
- package/src/js/modules/payments/host.ts +459 -0
- package/src/js/modules/payments/index.ts +17 -0
- package/src/js/modules/payments/mollie.ts +38 -0
- package/src/js/modules/payments/moneris.ts +216 -0
- package/src/js/modules/payments/opayo.ts +272 -0
- package/src/js/modules/payments/paddle.ts +111 -0
- package/src/js/modules/payments/payment.ts +183 -0
- package/src/js/modules/payments/paypal.ts +214 -0
- package/src/js/modules/payments/payway.ts +114 -0
- package/src/js/modules/payments/square.ts +106 -0
- package/src/js/modules/payments/stripe.ts +426 -0
- package/src/js/modules/payments/stub-payment-module.ts +87 -0
- package/src/js/modules/payments/utils.ts +60 -0
- package/src/js/modules/registry.ts +38 -0
- package/src/js/modules/styles.ts +29 -0
- package/src/js/submit/pipeline.ts +514 -0
- package/src/js/theme/theme-classes.ts +106 -0
- package/src/js/transport/forms-api.ts +345 -0
- package/src/js/utils/async.ts +81 -0
- package/src/js/utils/debug.ts +59 -0
- package/src/js/utils/event-names.ts +60 -0
- package/src/js/utils/field-references.keys.ts +47 -0
- package/src/js/utils/field-references.parser.ts +121 -0
- package/src/js/utils/field-references.registry.ts +50 -0
- package/src/js/utils/field-references.resolver.ts +115 -0
- package/src/js/utils/field-references.ts +11 -0
- package/src/js/utils/field-references.types.ts +31 -0
- package/src/js/utils/fields.ts +58 -0
- package/src/js/utils/http.ts +51 -0
- package/src/js/utils/i18n.ts +98 -0
- package/src/js/utils/scripts.ts +84 -0
- package/src/js/utils/unload-warning.ts +190 -0
- package/src/js/validation/rules/email.ts +18 -0
- package/src/js/validation/rules/match.ts +26 -0
- package/src/js/validation/rules/minmax.ts +47 -0
- package/src/js/validation/rules/number.ts +55 -0
- package/src/js/validation/rules/pattern.ts +29 -0
- package/src/js/validation/rules/required.ts +30 -0
- package/src/js/validation/rules/shared.ts +47 -0
- package/src/js/validation/rules/url.ts +23 -0
- package/src/js/validation/rules.ts +16 -0
- package/src/js/validation/types.ts +50 -0
- package/src/js/validation/validator.ts +643 -0
- package/src/vendor.d.ts +100 -0
- package/src/vite-env.d.ts +22 -0
- package/vite-dev.mjs +22 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,3206 @@
|
|
|
1
|
+
import { c as getScopedModuleLifecycleEventName, d as toDomEventName, l as getValidatorEventName, o as getGlobalModuleLifecycleEventName, r as getFieldModuleEventName, t as FORMIE_HTML_EVENT_NAMES, u as normalizeFormieEventName } from "./chunks/event-names-DamGPtXR.js";
|
|
2
|
+
import { a as setFormieDebugEnabled, i as isFormieDebugEnabled, n as debugLog, r as debugWarn, t as createDebug } from "./chunks/debug-KnZeKYBI.js";
|
|
3
|
+
import { a as setFormHiddenState, c as dispatchPageClientEventForSubmit, i as clearSubmitLoading, n as applyPageState, o as setSubmitLoading, r as applySubmitResultState, s as syncPageTabErrors, t as definePaymentModule } from "./chunks/api-DE7LfK-R.js";
|
|
4
|
+
import { n as registerThemeClassMap, r as removeThemeClasses, t as addThemeClasses } from "./chunks/theme-classes-vSHpdCUO.js";
|
|
5
|
+
import { t as requestJson } from "./chunks/http-18nn97DZ.js";
|
|
6
|
+
import { a as translate, i as t, n as mergeFormieTranslations, r as setFormieTranslations, t as getFormieTranslations } from "./chunks/i18n-vUh-KGiH.js";
|
|
7
|
+
import { n as definePassiveCaptchaModule, t as defineCaptchaModule } from "./chunks/api-DOfDzYC_.js";
|
|
8
|
+
import { n as inputNameToFieldKey, r as normalizeFieldKey, t as fieldKeyToInputName } from "./chunks/field-references.keys-BpBZ_quS.js";
|
|
9
|
+
import { i as parseFieldReference, n as resolveFieldReferenceLive, r as buildFieldValueRegistry, t as resolveFieldReferenceFromFormData } from "./chunks/field-references.resolver-Ba6xhiJC.js";
|
|
10
|
+
import { t as defineAddressModule } from "./chunks/api-CbqEMQT5.js";
|
|
11
|
+
//#region src/js/compatibility/event-map.ts
|
|
12
|
+
var LEGACY_FORMIE_DOM_EVENT_BRIDGES = [
|
|
13
|
+
{
|
|
14
|
+
legacyEvent: "onFormieLoaded",
|
|
15
|
+
canonicalEvent: "formie:mount:after",
|
|
16
|
+
disposition: "approximate",
|
|
17
|
+
target: "document"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
legacyEvent: "onFormieInit",
|
|
21
|
+
canonicalEvent: "formie:mount:after",
|
|
22
|
+
disposition: "approximate",
|
|
23
|
+
target: "document"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
legacyEvent: "onFormieReady",
|
|
27
|
+
canonicalEvent: "formie:mount:after",
|
|
28
|
+
disposition: "safe"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
legacyEvent: "onAfterFormieSubmit",
|
|
32
|
+
canonicalEvent: "formie:submit:result",
|
|
33
|
+
disposition: "safe"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
legacyEvent: "onFormieSubmitError",
|
|
37
|
+
canonicalEvent: "formie:submit:result",
|
|
38
|
+
disposition: "safe"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
legacyEvent: "onFormiePageToggle",
|
|
42
|
+
canonicalEvent: "formie:page:navigate:after",
|
|
43
|
+
disposition: "safe"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
legacyEvent: "onBeforeFormieSubmit",
|
|
47
|
+
canonicalEvent: "formie:submit:before",
|
|
48
|
+
disposition: "approximate"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
legacyEvent: "onFormieValidate",
|
|
52
|
+
canonicalEvent: "formie:stage:validate:before",
|
|
53
|
+
disposition: "approximate"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
legacyEvent: "onAfterFormieValidate",
|
|
57
|
+
canonicalEvent: "formie:stage:validate:after",
|
|
58
|
+
disposition: "approximate"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
legacyEvent: "onFormieSubmit",
|
|
62
|
+
canonicalEvent: "formie:submit:after",
|
|
63
|
+
disposition: "approximate"
|
|
64
|
+
}
|
|
65
|
+
];
|
|
66
|
+
var LEGACY_FORMIE_VALIDATOR_EVENT_BRIDGES = [
|
|
67
|
+
{
|
|
68
|
+
legacyEvent: "formieValidatorInitialized",
|
|
69
|
+
canonicalEvent: "formie:validator:ready",
|
|
70
|
+
disposition: "safe"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
legacyEvent: "formieValidatorDestroyed",
|
|
74
|
+
canonicalEvent: "formie:validator:destroy",
|
|
75
|
+
disposition: "safe"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
legacyEvent: "formieValidatorShowError",
|
|
79
|
+
canonicalEvent: "formie:validator:show-error",
|
|
80
|
+
disposition: "safe"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
legacyEvent: "formieValidatorClearError",
|
|
84
|
+
canonicalEvent: "formie:validator:clear-error",
|
|
85
|
+
disposition: "safe"
|
|
86
|
+
}
|
|
87
|
+
];
|
|
88
|
+
function resolveLegacyCompatibilityOptions(options) {
|
|
89
|
+
if (!options) return {
|
|
90
|
+
enabled: false,
|
|
91
|
+
legacyDomEvents: false,
|
|
92
|
+
legacyValidatorEvents: false
|
|
93
|
+
};
|
|
94
|
+
if (options === true) return {
|
|
95
|
+
enabled: true,
|
|
96
|
+
legacyDomEvents: true,
|
|
97
|
+
legacyValidatorEvents: true
|
|
98
|
+
};
|
|
99
|
+
const legacyDomEvents = options.legacyDomEvents ?? true;
|
|
100
|
+
const legacyValidatorEvents = options.legacyValidatorEvents ?? true;
|
|
101
|
+
return {
|
|
102
|
+
enabled: legacyDomEvents || legacyValidatorEvents,
|
|
103
|
+
legacyDomEvents,
|
|
104
|
+
legacyValidatorEvents
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region src/js/compatibility/dom-adapter.ts
|
|
109
|
+
function dispatchLegacyDomEvent(target, legacyEvent, detail) {
|
|
110
|
+
target.dispatchEvent(new CustomEvent(legacyEvent, {
|
|
111
|
+
bubbles: true,
|
|
112
|
+
detail
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
function shouldDispatchBridge(bridge, detail) {
|
|
116
|
+
if (bridge.canonicalEvent !== "formie:submit:result") return true;
|
|
117
|
+
const result = detail;
|
|
118
|
+
if (bridge.legacyEvent === "onAfterFormieSubmit") return !!result?.ok;
|
|
119
|
+
if (bridge.legacyEvent === "onFormieSubmitError") return result?.ok === false;
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
function createLegacyPageToggleDetail(form, detail) {
|
|
123
|
+
const eventDetail = detail && typeof detail === "object" ? detail : {};
|
|
124
|
+
const nextPageId = typeof eventDetail.pageId === "string" ? eventDetail.pageId : "";
|
|
125
|
+
const pages = Array.from(form.querySelectorAll("[data-formie-page-id]"));
|
|
126
|
+
return { data: {
|
|
127
|
+
nextPageId,
|
|
128
|
+
nextPageIndex: pages.findIndex((page) => {
|
|
129
|
+
return page.getAttribute("data-formie-page-id") === nextPageId;
|
|
130
|
+
}),
|
|
131
|
+
totalPages: pages.length
|
|
132
|
+
} };
|
|
133
|
+
}
|
|
134
|
+
function createLegacyDetail(bridge, detail, target, form, instance) {
|
|
135
|
+
const legacyFormieApi = globalThis.Formie || instance;
|
|
136
|
+
if (bridge.legacyEvent === "onFormieLoaded") return { formie: legacyFormieApi };
|
|
137
|
+
if (bridge.legacyEvent === "onFormieInit") return {
|
|
138
|
+
formie: legacyFormieApi,
|
|
139
|
+
form: instance,
|
|
140
|
+
$form: form,
|
|
141
|
+
formId: instance.id
|
|
142
|
+
};
|
|
143
|
+
if (bridge.legacyEvent === "onFormieReady") return {
|
|
144
|
+
...detail && typeof detail === "object" ? detail : {},
|
|
145
|
+
form,
|
|
146
|
+
target,
|
|
147
|
+
instance
|
|
148
|
+
};
|
|
149
|
+
if (bridge.legacyEvent === "onFormiePageToggle") return createLegacyPageToggleDetail(form, detail);
|
|
150
|
+
return detail;
|
|
151
|
+
}
|
|
152
|
+
function bindLegacyDomEventCompatibility({ target, form, instance, options, unbinds }) {
|
|
153
|
+
if (!options.legacyDomEvents) return;
|
|
154
|
+
LEGACY_FORMIE_DOM_EVENT_BRIDGES.forEach((bridge) => {
|
|
155
|
+
const handler = (event) => {
|
|
156
|
+
if (!(event instanceof CustomEvent) || !shouldDispatchBridge(bridge, event.detail)) return;
|
|
157
|
+
dispatchLegacyDomEvent(bridge.target === "document" ? document : form, bridge.legacyEvent, createLegacyDetail(bridge, event.detail, target, form, instance));
|
|
158
|
+
};
|
|
159
|
+
target.addEventListener(toDomEventName(bridge.canonicalEvent), handler);
|
|
160
|
+
unbinds.push(() => {
|
|
161
|
+
target.removeEventListener(toDomEventName(bridge.canonicalEvent), handler);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/js/compatibility/validator-adapter.ts
|
|
167
|
+
function dispatchLegacyValidatorEvent(target, legacyEvent, detail) {
|
|
168
|
+
target.dispatchEvent(new CustomEvent(legacyEvent, {
|
|
169
|
+
bubbles: true,
|
|
170
|
+
detail
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
function matchesValidator(detail, validator) {
|
|
174
|
+
return !!detail && typeof detail === "object" && detail.validator === validator;
|
|
175
|
+
}
|
|
176
|
+
function bindLegacyValidatorCompatibility({ target, form, validatorDetail, options, unbinds }) {
|
|
177
|
+
if (!options.legacyValidatorEvents || !validatorDetail) return;
|
|
178
|
+
const { validator, addValidator, removeValidator } = validatorDetail;
|
|
179
|
+
const baseDetail = {
|
|
180
|
+
...validatorDetail,
|
|
181
|
+
form,
|
|
182
|
+
target
|
|
183
|
+
};
|
|
184
|
+
dispatchLegacyValidatorEvent(document, "formieValidatorInitialized", baseDetail);
|
|
185
|
+
const destroyHandler = (event) => {
|
|
186
|
+
if (!(event instanceof CustomEvent) || !matchesValidator(event.detail, validator)) return;
|
|
187
|
+
dispatchLegacyValidatorEvent(document, "formieValidatorDestroyed", {
|
|
188
|
+
...baseDetail,
|
|
189
|
+
...event.detail
|
|
190
|
+
});
|
|
191
|
+
};
|
|
192
|
+
const showErrorHandler = (event) => {
|
|
193
|
+
if (!(event instanceof CustomEvent) || !matchesValidator(event.detail, validator) || !(event.target instanceof Element)) return;
|
|
194
|
+
if (!form.contains(event.target)) return;
|
|
195
|
+
dispatchLegacyValidatorEvent(event.target, "formieValidatorShowError", {
|
|
196
|
+
...event.detail,
|
|
197
|
+
addValidator,
|
|
198
|
+
removeValidator,
|
|
199
|
+
form,
|
|
200
|
+
target
|
|
201
|
+
});
|
|
202
|
+
};
|
|
203
|
+
const clearErrorHandler = (event) => {
|
|
204
|
+
if (!(event instanceof CustomEvent) || !matchesValidator(event.detail, validator) || !(event.target instanceof Element)) return;
|
|
205
|
+
if (!form.contains(event.target)) return;
|
|
206
|
+
dispatchLegacyValidatorEvent(event.target, "formieValidatorClearError", {
|
|
207
|
+
...event.detail,
|
|
208
|
+
addValidator,
|
|
209
|
+
removeValidator,
|
|
210
|
+
form,
|
|
211
|
+
target
|
|
212
|
+
});
|
|
213
|
+
};
|
|
214
|
+
document.addEventListener("formie:validator:destroy", destroyHandler);
|
|
215
|
+
document.addEventListener("formie:validator:show-error", showErrorHandler);
|
|
216
|
+
document.addEventListener("formie:validator:clear-error", clearErrorHandler);
|
|
217
|
+
unbinds.push(() => {
|
|
218
|
+
document.removeEventListener("formie:validator:destroy", destroyHandler);
|
|
219
|
+
document.removeEventListener("formie:validator:show-error", showErrorHandler);
|
|
220
|
+
document.removeEventListener("formie:validator:clear-error", clearErrorHandler);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
//#endregion
|
|
224
|
+
//#region src/js/core/dom-events.ts
|
|
225
|
+
function dispatchFormieDomEvent(target, eventName, detail) {
|
|
226
|
+
target.dispatchEvent(new CustomEvent(toDomEventName(eventName), {
|
|
227
|
+
bubbles: true,
|
|
228
|
+
detail
|
|
229
|
+
}));
|
|
230
|
+
}
|
|
231
|
+
//#endregion
|
|
232
|
+
//#region src/js/events/event-bus.ts
|
|
233
|
+
var EventBus = class {
|
|
234
|
+
constructor() {
|
|
235
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
236
|
+
}
|
|
237
|
+
on(eventName, callback) {
|
|
238
|
+
if (!this.listeners.has(eventName)) this.listeners.set(eventName, /* @__PURE__ */ new Set());
|
|
239
|
+
this.listeners.get(eventName)?.add(callback);
|
|
240
|
+
return () => {
|
|
241
|
+
this.listeners.get(eventName)?.delete(callback);
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
async emit(eventName, payload) {
|
|
245
|
+
const callbacks = this.listeners.get(eventName);
|
|
246
|
+
if (!callbacks || callbacks.size === 0) return;
|
|
247
|
+
for (const callback of callbacks) await callback(payload);
|
|
248
|
+
}
|
|
249
|
+
async emitSafe(eventName, payload) {
|
|
250
|
+
const callbacks = this.listeners.get(eventName);
|
|
251
|
+
const report = {
|
|
252
|
+
eventName,
|
|
253
|
+
total: callbacks?.size || 0,
|
|
254
|
+
succeeded: 0,
|
|
255
|
+
failed: []
|
|
256
|
+
};
|
|
257
|
+
if (!callbacks || callbacks.size === 0) return report;
|
|
258
|
+
let index = 0;
|
|
259
|
+
for (const callback of callbacks) {
|
|
260
|
+
try {
|
|
261
|
+
await callback(payload);
|
|
262
|
+
report.succeeded += 1;
|
|
263
|
+
} catch (error) {
|
|
264
|
+
report.failed.push({
|
|
265
|
+
index,
|
|
266
|
+
error
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
index += 1;
|
|
270
|
+
}
|
|
271
|
+
return report;
|
|
272
|
+
}
|
|
273
|
+
async emitParallelSafe(eventName, payload) {
|
|
274
|
+
const callbacks = this.listeners.get(eventName);
|
|
275
|
+
const report = {
|
|
276
|
+
eventName,
|
|
277
|
+
total: callbacks?.size || 0,
|
|
278
|
+
succeeded: 0,
|
|
279
|
+
failed: []
|
|
280
|
+
};
|
|
281
|
+
if (!callbacks || callbacks.size === 0) return report;
|
|
282
|
+
(await Promise.allSettled(Array.from(callbacks).map(async (callback) => {
|
|
283
|
+
return callback(payload);
|
|
284
|
+
}))).forEach((result, index) => {
|
|
285
|
+
if (result.status === "fulfilled") {
|
|
286
|
+
report.succeeded += 1;
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
report.failed.push({
|
|
290
|
+
index,
|
|
291
|
+
error: result.reason
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
return report;
|
|
295
|
+
}
|
|
296
|
+
clear() {
|
|
297
|
+
this.listeners.clear();
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
//#endregion
|
|
301
|
+
//#region src/js/transport/forms-api.ts
|
|
302
|
+
var debug$6 = createDebug("general", "transport");
|
|
303
|
+
function toServerRenderPayloadInput(renderOptions) {
|
|
304
|
+
const input = {};
|
|
305
|
+
[
|
|
306
|
+
"theme",
|
|
307
|
+
"themeConfig",
|
|
308
|
+
"locale",
|
|
309
|
+
"siteId"
|
|
310
|
+
].forEach((key) => {
|
|
311
|
+
if (renderOptions[key] !== void 0) input[key] = renderOptions[key];
|
|
312
|
+
});
|
|
313
|
+
return input;
|
|
314
|
+
}
|
|
315
|
+
function flattenErrors(errors, path = "", output = {}) {
|
|
316
|
+
if (Array.isArray(errors)) {
|
|
317
|
+
const messages = errors.map((value) => {
|
|
318
|
+
return typeof value === "string" ? value : String(value ?? "");
|
|
319
|
+
}).filter((value) => {
|
|
320
|
+
return value.trim() !== "";
|
|
321
|
+
});
|
|
322
|
+
if (path && messages.length) output[path] = (output[path] || []).concat(messages);
|
|
323
|
+
return output;
|
|
324
|
+
}
|
|
325
|
+
if (errors && typeof errors === "object") Object.entries(errors).forEach(([key, value]) => {
|
|
326
|
+
flattenErrors(value, path ? `${path}.${key}` : key, output);
|
|
327
|
+
});
|
|
328
|
+
return output;
|
|
329
|
+
}
|
|
330
|
+
function normalizePayload(payload, fallbackFormError) {
|
|
331
|
+
const success = payload.success === true;
|
|
332
|
+
const keepSubmitLoading = payload.keepSubmitLoading === true;
|
|
333
|
+
const errors = payload.errors;
|
|
334
|
+
const fieldErrorsFlat = flattenErrors(errors || {});
|
|
335
|
+
const formErrors = fieldErrorsFlat.form || [];
|
|
336
|
+
const fieldErrors = {};
|
|
337
|
+
Object.entries(fieldErrorsFlat).forEach(([key, value]) => {
|
|
338
|
+
if (key === "form") return;
|
|
339
|
+
const topKey = key.split(".")[0];
|
|
340
|
+
fieldErrors[topKey] = (fieldErrors[topKey] || []).concat(value);
|
|
341
|
+
});
|
|
342
|
+
const resolvedFormErrors = !success && formErrors.length === 0 && Object.keys(fieldErrors).length > 0 ? [fallbackFormError || "Submission failed."] : formErrors;
|
|
343
|
+
const isTransientPendingResult = !success && keepSubmitLoading && resolvedFormErrors.length === 0 && Object.keys(fieldErrors).length === 0;
|
|
344
|
+
return {
|
|
345
|
+
ok: success,
|
|
346
|
+
action: payload.submitAction === "back" || payload.submitAction === "save" || payload.submitAction === "submit" ? payload.submitAction : void 0,
|
|
347
|
+
message: payload.submitActionMessage || (success ? "Submission completed." : isTransientPendingResult ? "" : resolvedFormErrors[0] || "Submission failed."),
|
|
348
|
+
code: success ? void 0 : String(payload.code || "SUBMIT_ERROR"),
|
|
349
|
+
keepSubmitLoading,
|
|
350
|
+
fieldErrors: Object.keys(fieldErrors).length ? fieldErrors : void 0,
|
|
351
|
+
formErrors: resolvedFormErrors.length ? resolvedFormErrors : void 0,
|
|
352
|
+
nextPage: payload.nextPageId ? { id: String(payload.nextPageId) } : null,
|
|
353
|
+
redirect: payload.redirectUrl ? {
|
|
354
|
+
url: String(payload.redirectUrl),
|
|
355
|
+
target: payload.submitActionTab === "new-tab" ? "new-tab" : "same-tab"
|
|
356
|
+
} : null,
|
|
357
|
+
submitData: Array.isArray(payload.submitData) ? payload.submitData : void 0,
|
|
358
|
+
meta: payload
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
async function requestRender(endpoint, handle, renderOptions = {}) {
|
|
362
|
+
const body = JSON.stringify({
|
|
363
|
+
handle,
|
|
364
|
+
renderOptions
|
|
365
|
+
});
|
|
366
|
+
debug$6.log("requestRender start.", {
|
|
367
|
+
endpoint,
|
|
368
|
+
handle
|
|
369
|
+
});
|
|
370
|
+
const result = await requestJson(endpoint, {
|
|
371
|
+
method: "POST",
|
|
372
|
+
body,
|
|
373
|
+
headers: { "Content-Type": "application/json" }
|
|
374
|
+
});
|
|
375
|
+
debug$6.log("requestRender complete.", { hasHtml: !!result.html });
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
378
|
+
async function requestGraphqlRender(endpoint, handle, renderOptions = {}) {
|
|
379
|
+
const body = JSON.stringify({
|
|
380
|
+
query: `
|
|
381
|
+
query FormieHtmlForm($handle: String!, $input: ServerRenderPayloadInput) {
|
|
382
|
+
formieHtmlForm(handle: $handle, input: $input) {
|
|
383
|
+
html
|
|
384
|
+
}
|
|
385
|
+
}`,
|
|
386
|
+
variables: {
|
|
387
|
+
handle,
|
|
388
|
+
input: toServerRenderPayloadInput(renderOptions)
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
debug$6.log("requestGraphqlRender start.", {
|
|
392
|
+
endpoint,
|
|
393
|
+
handle
|
|
394
|
+
});
|
|
395
|
+
const result = await requestJson(endpoint, {
|
|
396
|
+
method: "POST",
|
|
397
|
+
body,
|
|
398
|
+
headers: { "Content-Type": "application/json" }
|
|
399
|
+
});
|
|
400
|
+
if (Array.isArray(result.errors) && result.errors.length > 0) throw new Error(result.errors.map((error) => error.message || "Unknown GraphQL error").join("; "));
|
|
401
|
+
if (!result.data?.formieHtmlForm) throw new Error(`Form not found for handle "${handle}".`);
|
|
402
|
+
const payload = result.data.formieHtmlForm;
|
|
403
|
+
debug$6.log("requestGraphqlRender complete.", { hasHtml: !!payload.html });
|
|
404
|
+
return payload;
|
|
405
|
+
}
|
|
406
|
+
async function requestRefreshTokens(endpoint, handle, renderId) {
|
|
407
|
+
const url = new URL(endpoint, window.location.origin);
|
|
408
|
+
url.searchParams.set("handle", handle);
|
|
409
|
+
if (renderId) url.searchParams.set("renderId", renderId);
|
|
410
|
+
debug$6.log("requestRefreshTokens start.", {
|
|
411
|
+
endpoint: url.toString(),
|
|
412
|
+
handle,
|
|
413
|
+
hasRenderId: !!renderId
|
|
414
|
+
});
|
|
415
|
+
const response = await requestJson(url.toString());
|
|
416
|
+
debug$6.log("requestRefreshTokens complete.", { hasRefreshTokens: !!response.refreshTokens });
|
|
417
|
+
return response.refreshTokens || response;
|
|
418
|
+
}
|
|
419
|
+
async function requestSetPage(url, form, pageId) {
|
|
420
|
+
const requestUrl = new URL(url, window.location.origin);
|
|
421
|
+
const body = new FormData();
|
|
422
|
+
if (pageId) body.append("pageId", pageId);
|
|
423
|
+
if (form) {
|
|
424
|
+
[
|
|
425
|
+
"handle",
|
|
426
|
+
"renderId",
|
|
427
|
+
"draftContextToken",
|
|
428
|
+
"draftContext",
|
|
429
|
+
"continuationToken"
|
|
430
|
+
].forEach((name) => {
|
|
431
|
+
const value = form.querySelector(`input[name="${name}"]`)?.value?.trim();
|
|
432
|
+
if (value) body.append(name, value);
|
|
433
|
+
});
|
|
434
|
+
const csrfValue = form.querySelector("input[name=\"CRAFT_CSRF_TOKEN\"]")?.value?.trim();
|
|
435
|
+
if (csrfValue) body.append("CRAFT_CSRF_TOKEN", csrfValue);
|
|
436
|
+
}
|
|
437
|
+
debug$6.log("requestSetPage start.", {
|
|
438
|
+
requestUrl: requestUrl.toString(),
|
|
439
|
+
pageId: pageId || null
|
|
440
|
+
});
|
|
441
|
+
const result = await requestJson(requestUrl.toString(), {
|
|
442
|
+
method: "POST",
|
|
443
|
+
body
|
|
444
|
+
});
|
|
445
|
+
debug$6.log("requestSetPage complete.", result);
|
|
446
|
+
return result;
|
|
447
|
+
}
|
|
448
|
+
function clearSubmissionOnUnload(endpoint, form) {
|
|
449
|
+
const requestUrl = new URL(endpoint, window.location.origin);
|
|
450
|
+
const body = new FormData();
|
|
451
|
+
[
|
|
452
|
+
"handle",
|
|
453
|
+
"renderId",
|
|
454
|
+
"draftContextToken",
|
|
455
|
+
"draftContext"
|
|
456
|
+
].forEach((name) => {
|
|
457
|
+
const value = form.querySelector(`input[name="${name}"]`)?.value?.trim();
|
|
458
|
+
if (value) body.append(name, value);
|
|
459
|
+
});
|
|
460
|
+
const csrfValue = form.querySelector("input[name=\"CRAFT_CSRF_TOKEN\"]")?.value?.trim();
|
|
461
|
+
if (csrfValue) body.append("CRAFT_CSRF_TOKEN", csrfValue);
|
|
462
|
+
debug$6.log("clearSubmissionOnUnload start.", { requestUrl: requestUrl.toString() });
|
|
463
|
+
try {
|
|
464
|
+
if (typeof navigator.sendBeacon === "function" && navigator.sendBeacon(requestUrl.toString(), body)) return;
|
|
465
|
+
} catch (_error) {}
|
|
466
|
+
fetch(requestUrl.toString(), {
|
|
467
|
+
method: "POST",
|
|
468
|
+
body,
|
|
469
|
+
credentials: "include",
|
|
470
|
+
keepalive: true,
|
|
471
|
+
headers: { Accept: "application/json" }
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
async function submitForm(form, formData) {
|
|
475
|
+
const method = (form.getAttribute("method") || "POST").toUpperCase();
|
|
476
|
+
const action = form.getAttribute("action") || window.location.href;
|
|
477
|
+
const fallbackFormError = form.dataset.formieErrorMessage?.trim() || "Submission failed.";
|
|
478
|
+
debug$6.log("submitForm start.", {
|
|
479
|
+
method,
|
|
480
|
+
action,
|
|
481
|
+
submitAction: formData.get("submitAction")
|
|
482
|
+
});
|
|
483
|
+
const response = await fetch(action, {
|
|
484
|
+
method,
|
|
485
|
+
body: formData,
|
|
486
|
+
credentials: "include",
|
|
487
|
+
headers: { Accept: "application/json" }
|
|
488
|
+
});
|
|
489
|
+
const contentType = response.headers.get("content-type") || "";
|
|
490
|
+
if (!contentType.includes("application/json")) {
|
|
491
|
+
if (!response.ok) {
|
|
492
|
+
debug$6.warn("submitForm non-JSON HTTP error.", {
|
|
493
|
+
status: response.status,
|
|
494
|
+
contentType
|
|
495
|
+
});
|
|
496
|
+
return {
|
|
497
|
+
ok: false,
|
|
498
|
+
code: "HTTP_ERROR",
|
|
499
|
+
message: `Request failed (${response.status}).`,
|
|
500
|
+
formErrors: [`Request failed (${response.status}).`]
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
debug$6.log("submitForm non-JSON success response.", {
|
|
504
|
+
status: response.status,
|
|
505
|
+
contentType
|
|
506
|
+
});
|
|
507
|
+
return {
|
|
508
|
+
ok: true,
|
|
509
|
+
message: "Submission completed."
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
const normalized = normalizePayload(await response.json(), fallbackFormError);
|
|
513
|
+
debug$6.log("submitForm JSON response normalized.", {
|
|
514
|
+
ok: normalized.ok,
|
|
515
|
+
code: normalized.code,
|
|
516
|
+
hasRedirect: !!normalized.redirect?.url,
|
|
517
|
+
hasSubmitData: Array.isArray(normalized.submitData) && normalized.submitData.length > 0
|
|
518
|
+
});
|
|
519
|
+
return normalized;
|
|
520
|
+
}
|
|
521
|
+
//#endregion
|
|
522
|
+
//#region src/js/submit/pipeline.ts
|
|
523
|
+
var STAGES = [
|
|
524
|
+
"prepare",
|
|
525
|
+
"normalize",
|
|
526
|
+
"validate",
|
|
527
|
+
"screen",
|
|
528
|
+
"authorize",
|
|
529
|
+
"dispatch",
|
|
530
|
+
"finalize"
|
|
531
|
+
];
|
|
532
|
+
var PREFLIGHT_STAGES = [
|
|
533
|
+
"prepare",
|
|
534
|
+
"normalize",
|
|
535
|
+
"validate",
|
|
536
|
+
"screen",
|
|
537
|
+
"authorize"
|
|
538
|
+
];
|
|
539
|
+
var debug$5 = createDebug("general", "pipeline");
|
|
540
|
+
function getAbortedResult(stage, reason) {
|
|
541
|
+
return {
|
|
542
|
+
ok: false,
|
|
543
|
+
stage,
|
|
544
|
+
code: "ABORTED",
|
|
545
|
+
message: reason || "Submission aborted.",
|
|
546
|
+
formErrors: [reason || "Submission aborted."]
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
function getPages(form) {
|
|
550
|
+
return Array.from(form.querySelectorAll("[data-formie-page]"));
|
|
551
|
+
}
|
|
552
|
+
function getValidationScope(form) {
|
|
553
|
+
const pages = getPages(form);
|
|
554
|
+
if (!pages.length) return {
|
|
555
|
+
scope: form,
|
|
556
|
+
final: true
|
|
557
|
+
};
|
|
558
|
+
const currentPage = pages.find((page) => {
|
|
559
|
+
return !page.hasAttribute("data-formie-page-hidden");
|
|
560
|
+
}) || pages[pages.length - 1];
|
|
561
|
+
return {
|
|
562
|
+
scope: currentPage,
|
|
563
|
+
final: currentPage === pages[pages.length - 1]
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
function isSubmittableControl(element) {
|
|
567
|
+
return element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement;
|
|
568
|
+
}
|
|
569
|
+
function shouldIncludeControl(control) {
|
|
570
|
+
if (!control.name || control.disabled) return false;
|
|
571
|
+
if (control instanceof HTMLInputElement) {
|
|
572
|
+
if (control.type === "submit" || control.type === "button" || control.type === "reset" || control.type === "image") return false;
|
|
573
|
+
if ((control.type === "checkbox" || control.type === "radio") && !control.checked) return false;
|
|
574
|
+
if (control.type === "file" && (!control.files || control.files.length === 0)) return false;
|
|
575
|
+
}
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
578
|
+
function appendControlValue(formData, control) {
|
|
579
|
+
if (control instanceof HTMLInputElement) {
|
|
580
|
+
if (control.type === "file") {
|
|
581
|
+
Array.from(control.files || []).forEach((file) => {
|
|
582
|
+
formData.append(control.name, file);
|
|
583
|
+
});
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
formData.append(control.name, control.value);
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
if (control instanceof HTMLSelectElement && control.multiple) {
|
|
590
|
+
Array.from(control.selectedOptions).forEach((option) => {
|
|
591
|
+
formData.append(control.name, option.value);
|
|
592
|
+
});
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
formData.append(control.name, control.value);
|
|
596
|
+
}
|
|
597
|
+
function appendControlsFromRoot(formData, form) {
|
|
598
|
+
form.querySelectorAll("input, select, textarea").forEach((node) => {
|
|
599
|
+
const control = isSubmittableControl(node) ? node : null;
|
|
600
|
+
if (!control || control.closest("[data-formie-page]")) return;
|
|
601
|
+
if (!shouldIncludeControl(control)) return;
|
|
602
|
+
appendControlValue(formData, control);
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
function appendControlsFromPage(formData, page) {
|
|
606
|
+
const fieldNames = /* @__PURE__ */ new Set();
|
|
607
|
+
page.querySelectorAll("input, select, textarea").forEach((node) => {
|
|
608
|
+
const control = isSubmittableControl(node) ? node : null;
|
|
609
|
+
if (!control || !control.name || control.disabled) return;
|
|
610
|
+
if (control instanceof HTMLInputElement && (control.type === "submit" || control.type === "button" || control.type === "reset" || control.type === "image")) return;
|
|
611
|
+
if (control.name.startsWith("fields[")) fieldNames.add(control.name);
|
|
612
|
+
if (!shouldIncludeControl(control)) return;
|
|
613
|
+
appendControlValue(formData, control);
|
|
614
|
+
});
|
|
615
|
+
return fieldNames;
|
|
616
|
+
}
|
|
617
|
+
function appendMissingFieldClears(formData, fieldNames) {
|
|
618
|
+
fieldNames.forEach((name) => {
|
|
619
|
+
if (!formData.has(name)) formData.append(name, "");
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
function buildSubmitFormData(form, action) {
|
|
623
|
+
const pages = getPages(form);
|
|
624
|
+
const currentPage = pages.find((page) => {
|
|
625
|
+
return !page.hasAttribute("data-formie-page-hidden");
|
|
626
|
+
}) || null;
|
|
627
|
+
if (!pages.length || !currentPage) {
|
|
628
|
+
const formData = new FormData(form);
|
|
629
|
+
formData.set("submitAction", action);
|
|
630
|
+
return formData;
|
|
631
|
+
}
|
|
632
|
+
const formData = new FormData();
|
|
633
|
+
appendControlsFromRoot(formData, form);
|
|
634
|
+
appendMissingFieldClears(formData, appendControlsFromPage(formData, currentPage));
|
|
635
|
+
formData.set("submitAction", action);
|
|
636
|
+
return formData;
|
|
637
|
+
}
|
|
638
|
+
function isFinalSubmitAttempt(form, action) {
|
|
639
|
+
if (action !== "submit") return false;
|
|
640
|
+
const pages = getPages(form);
|
|
641
|
+
if (!pages.length) return true;
|
|
642
|
+
return (pages.find((page) => {
|
|
643
|
+
return !page.hasAttribute("data-formie-page-hidden");
|
|
644
|
+
}) || pages[pages.length - 1]) === pages[pages.length - 1];
|
|
645
|
+
}
|
|
646
|
+
async function runSubmitPipeline(form, action, bus, options = {}) {
|
|
647
|
+
debug$5.log("Starting submit pipeline.", {
|
|
648
|
+
action,
|
|
649
|
+
preflightOnly: options.preflightOnly === true
|
|
650
|
+
});
|
|
651
|
+
let aborted = false;
|
|
652
|
+
let abortReason;
|
|
653
|
+
let dispatchResult = null;
|
|
654
|
+
const finalSubmitAttempt = isFinalSubmitAttempt(form, action);
|
|
655
|
+
const context = {
|
|
656
|
+
form,
|
|
657
|
+
action,
|
|
658
|
+
formData: buildSubmitFormData(form, action),
|
|
659
|
+
abort: (reason) => {
|
|
660
|
+
aborted = true;
|
|
661
|
+
abortReason = reason;
|
|
662
|
+
debug$5.warn("Pipeline aborted.", { reason });
|
|
663
|
+
},
|
|
664
|
+
isAborted: () => aborted,
|
|
665
|
+
abortReason: () => abortReason
|
|
666
|
+
};
|
|
667
|
+
const runners = {
|
|
668
|
+
prepare: async (ctx) => {
|
|
669
|
+
const submitAction = ctx.form.querySelector("input[name=\"submitAction\"]");
|
|
670
|
+
if (submitAction) submitAction.value = ctx.action;
|
|
671
|
+
ctx.formData.set("submitAction", ctx.action);
|
|
672
|
+
return null;
|
|
673
|
+
},
|
|
674
|
+
normalize: async () => {
|
|
675
|
+
return null;
|
|
676
|
+
},
|
|
677
|
+
validate: async (ctx) => {
|
|
678
|
+
if (ctx.action !== "submit") return null;
|
|
679
|
+
if (options.validateOnSubmit === false) return null;
|
|
680
|
+
if (options.validator) {
|
|
681
|
+
const { scope, final } = getValidationScope(ctx.form);
|
|
682
|
+
const errors = options.validator.submit(final ? ctx.form : scope, { final });
|
|
683
|
+
if (errors.length > 0) {
|
|
684
|
+
errors[0]?.input.focus();
|
|
685
|
+
return {
|
|
686
|
+
ok: false,
|
|
687
|
+
stage: "validate",
|
|
688
|
+
code: "VALIDATION_FAILED",
|
|
689
|
+
message: options.validator.config.errorMessage || "Validation failed.",
|
|
690
|
+
fieldErrors: options.validator.getFieldErrors(errors),
|
|
691
|
+
formErrors: [options.validator.config.errorMessage || "Validation failed."]
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
696
|
+
if (!ctx.form.checkValidity()) {
|
|
697
|
+
ctx.form.querySelector(":invalid")?.focus();
|
|
698
|
+
return {
|
|
699
|
+
ok: false,
|
|
700
|
+
stage: "validate",
|
|
701
|
+
code: "VALIDATION_FAILED",
|
|
702
|
+
message: "Validation failed.",
|
|
703
|
+
formErrors: ["Validation failed."]
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
return null;
|
|
707
|
+
},
|
|
708
|
+
screen: async () => {
|
|
709
|
+
return null;
|
|
710
|
+
},
|
|
711
|
+
authorize: async () => {
|
|
712
|
+
return null;
|
|
713
|
+
},
|
|
714
|
+
dispatch: async (ctx) => {
|
|
715
|
+
ctx.formData = buildSubmitFormData(ctx.form, ctx.action);
|
|
716
|
+
const result = await submitForm(ctx.form, ctx.formData);
|
|
717
|
+
dispatchResult = result;
|
|
718
|
+
return result;
|
|
719
|
+
},
|
|
720
|
+
finalize: async (resultCtx) => {
|
|
721
|
+
if (!dispatchResult) return null;
|
|
722
|
+
if (dispatchResult.ok && dispatchResult.redirect?.url) if (dispatchResult.redirect.target === "new-tab") window.open(dispatchResult.redirect.url, "_blank");
|
|
723
|
+
else window.location.href = dispatchResult.redirect.url;
|
|
724
|
+
return null;
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
{
|
|
728
|
+
const emitReport = await bus.emitSafe("formie:submit:before", context);
|
|
729
|
+
if (emitReport.failed.length > 0) debug$5.warn("Submit before listeners failed.", {
|
|
730
|
+
eventName: emitReport.eventName,
|
|
731
|
+
failed: emitReport.failed.length
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
if (finalSubmitAttempt) {
|
|
735
|
+
const emitReport = await bus.emitSafe("formie:submit:final:before", context);
|
|
736
|
+
if (emitReport.failed.length > 0) debug$5.warn("Final submit before listeners failed.", {
|
|
737
|
+
eventName: emitReport.eventName,
|
|
738
|
+
failed: emitReport.failed.length
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
const stages = options.preflightOnly ? PREFLIGHT_STAGES : STAGES;
|
|
742
|
+
for (const stage of stages) {
|
|
743
|
+
debug$5.log("Stage start.", {
|
|
744
|
+
stage,
|
|
745
|
+
action
|
|
746
|
+
});
|
|
747
|
+
if (aborted) {
|
|
748
|
+
debug$5.warn("Stage skipped due to abort.", {
|
|
749
|
+
stage,
|
|
750
|
+
reason: abortReason
|
|
751
|
+
});
|
|
752
|
+
return getAbortedResult(stage, abortReason);
|
|
753
|
+
}
|
|
754
|
+
{
|
|
755
|
+
const emitReport = await bus.emitSafe(`formie:stage:${stage}:before`, {
|
|
756
|
+
...context,
|
|
757
|
+
stage
|
|
758
|
+
});
|
|
759
|
+
if (emitReport.failed.length > 0) debug$5.warn("Stage before listeners failed.", {
|
|
760
|
+
stage,
|
|
761
|
+
failed: emitReport.failed.length
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
if (aborted) {
|
|
765
|
+
const abortedResult = getAbortedResult(stage, abortReason);
|
|
766
|
+
{
|
|
767
|
+
const emitReport = await bus.emitSafe("formie:submit:after", abortedResult);
|
|
768
|
+
if (emitReport.failed.length > 0) debug$5.warn("Submit after listeners failed (abort before stage).", {
|
|
769
|
+
stage,
|
|
770
|
+
failed: emitReport.failed.length
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
if (finalSubmitAttempt) {
|
|
774
|
+
const emitReport = await bus.emitSafe("formie:submit:final:after", abortedResult);
|
|
775
|
+
if (emitReport.failed.length > 0) debug$5.warn("Final submit after listeners failed (abort before stage).", {
|
|
776
|
+
stage,
|
|
777
|
+
failed: emitReport.failed.length
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
debug$5.warn("Aborted after stage before-hooks.", {
|
|
781
|
+
stage,
|
|
782
|
+
reason: abortReason
|
|
783
|
+
});
|
|
784
|
+
return abortedResult;
|
|
785
|
+
}
|
|
786
|
+
const stageResult = await runners[stage](context);
|
|
787
|
+
debug$5.log("Stage runner complete.", {
|
|
788
|
+
stage,
|
|
789
|
+
hasResult: !!stageResult,
|
|
790
|
+
ok: stageResult ? stageResult.ok : void 0,
|
|
791
|
+
code: stageResult?.code
|
|
792
|
+
});
|
|
793
|
+
{
|
|
794
|
+
const emitReport = await bus.emitSafe(`formie:stage:${stage}:after`, {
|
|
795
|
+
...context,
|
|
796
|
+
stage,
|
|
797
|
+
result: stageResult
|
|
798
|
+
});
|
|
799
|
+
if (emitReport.failed.length > 0) debug$5.warn("Stage after listeners failed.", {
|
|
800
|
+
stage,
|
|
801
|
+
failed: emitReport.failed.length
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
if (aborted) {
|
|
805
|
+
const abortedResult = getAbortedResult(stage, abortReason);
|
|
806
|
+
{
|
|
807
|
+
const emitReport = await bus.emitSafe("formie:submit:after", abortedResult);
|
|
808
|
+
if (emitReport.failed.length > 0) debug$5.warn("Submit after listeners failed (abort after stage).", {
|
|
809
|
+
stage,
|
|
810
|
+
failed: emitReport.failed.length
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
if (finalSubmitAttempt) {
|
|
814
|
+
const emitReport = await bus.emitSafe("formie:submit:final:after", abortedResult);
|
|
815
|
+
if (emitReport.failed.length > 0) debug$5.warn("Final submit after listeners failed (abort after stage).", {
|
|
816
|
+
stage,
|
|
817
|
+
failed: emitReport.failed.length
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
debug$5.warn("Aborted after stage after-hooks.", {
|
|
821
|
+
stage,
|
|
822
|
+
reason: abortReason
|
|
823
|
+
});
|
|
824
|
+
return abortedResult;
|
|
825
|
+
}
|
|
826
|
+
if (stageResult && !stageResult.ok) {
|
|
827
|
+
{
|
|
828
|
+
const emitReport = await bus.emitSafe("formie:submit:after", stageResult);
|
|
829
|
+
if (emitReport.failed.length > 0) debug$5.warn("Submit after listeners failed (failed stage).", {
|
|
830
|
+
stage,
|
|
831
|
+
failed: emitReport.failed.length
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
if (finalSubmitAttempt) {
|
|
835
|
+
const emitReport = await bus.emitSafe("formie:submit:final:after", stageResult);
|
|
836
|
+
if (emitReport.failed.length > 0) debug$5.warn("Final submit after listeners failed (failed stage).", {
|
|
837
|
+
stage,
|
|
838
|
+
failed: emitReport.failed.length
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
debug$5.warn("Pipeline short-circuited by failed stage.", {
|
|
842
|
+
stage,
|
|
843
|
+
code: stageResult.code,
|
|
844
|
+
message: stageResult.message
|
|
845
|
+
});
|
|
846
|
+
return stageResult;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
const successResult = dispatchResult || {
|
|
850
|
+
ok: true,
|
|
851
|
+
stage: options.preflightOnly ? "authorize" : "finalize",
|
|
852
|
+
message: options.preflightOnly ? "Submission preflight completed." : "Submission completed."
|
|
853
|
+
};
|
|
854
|
+
{
|
|
855
|
+
const emitReport = await bus.emitSafe("formie:submit:after", successResult);
|
|
856
|
+
if (emitReport.failed.length > 0) debug$5.warn("Submit after listeners failed (success).", { failed: emitReport.failed.length });
|
|
857
|
+
}
|
|
858
|
+
if (finalSubmitAttempt) {
|
|
859
|
+
const emitReport = await bus.emitSafe("formie:submit:final:after", successResult);
|
|
860
|
+
if (emitReport.failed.length > 0) debug$5.warn("Final submit after listeners failed (success).", { failed: emitReport.failed.length });
|
|
861
|
+
}
|
|
862
|
+
debug$5.log("Pipeline completed.", {
|
|
863
|
+
ok: successResult.ok,
|
|
864
|
+
stage: successResult.stage,
|
|
865
|
+
code: successResult.code
|
|
866
|
+
});
|
|
867
|
+
return successResult;
|
|
868
|
+
}
|
|
869
|
+
//#endregion
|
|
870
|
+
//#region src/js/validation/rules/email.ts
|
|
871
|
+
var email = {
|
|
872
|
+
rule: ({ input, getRule }) => {
|
|
873
|
+
if (!getRule("email") || !input.value || input.value.length < 1) return true;
|
|
874
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input.value);
|
|
875
|
+
},
|
|
876
|
+
message: ({ input, label, t }) => {
|
|
877
|
+
return input.getAttribute("data-formie-pattern-email-message") ?? input.getAttribute("data-pattern-email-message") ?? t("{attribute} is not a valid email address.", { attribute: label });
|
|
878
|
+
}
|
|
879
|
+
};
|
|
880
|
+
//#endregion
|
|
881
|
+
//#region src/js/validation/rules/shared.ts
|
|
882
|
+
function getLabelText(field) {
|
|
883
|
+
return field?.querySelector("[data-formie-field-label]")?.childNodes[0]?.textContent?.trim() || "";
|
|
884
|
+
}
|
|
885
|
+
function getComparableInput(ctx) {
|
|
886
|
+
const match = ctx.getRule("match");
|
|
887
|
+
if (!match || match === true || typeof match !== "object" || !ctx.field) return null;
|
|
888
|
+
const fieldHandle = typeof match.fieldHandle === "string" ? match.fieldHandle.trim() : "";
|
|
889
|
+
if (!fieldHandle) return null;
|
|
890
|
+
const sourceField = ctx.form.querySelector(`[data-formie-field-handle="${fieldHandle}"]`);
|
|
891
|
+
if (!sourceField) return null;
|
|
892
|
+
return sourceField.querySelector(ctx.config.fieldsSelector);
|
|
893
|
+
}
|
|
894
|
+
//#endregion
|
|
895
|
+
//#region src/js/validation/rules.ts
|
|
896
|
+
var rules_default = {
|
|
897
|
+
required: {
|
|
898
|
+
rule: ({ input, getRule }) => {
|
|
899
|
+
if (!getRule("required") || input.type === "hidden") return true;
|
|
900
|
+
if (input.type === "checkbox" || input.type === "radio") {
|
|
901
|
+
const checkboxInputs = input.form?.querySelectorAll(`[name="${input.name}"]:not([type="hidden"]):not([disabled])`) || [];
|
|
902
|
+
if (checkboxInputs.length) return Array.from(checkboxInputs).some((button) => {
|
|
903
|
+
return button instanceof HTMLInputElement && button.checked;
|
|
904
|
+
});
|
|
905
|
+
return input instanceof HTMLInputElement ? input.checked : true;
|
|
906
|
+
}
|
|
907
|
+
return input.value.trim() !== "";
|
|
908
|
+
},
|
|
909
|
+
message: ({ input, label, t }) => {
|
|
910
|
+
return input.getAttribute("data-formie-required-message") ?? input.getAttribute("data-required-message") ?? t("{attribute} cannot be blank.", { attribute: label });
|
|
911
|
+
}
|
|
912
|
+
},
|
|
913
|
+
email,
|
|
914
|
+
url: {
|
|
915
|
+
rule: ({ input, getRule }) => {
|
|
916
|
+
if (!getRule("url") || !input.value || input.value.length < 1) return true;
|
|
917
|
+
try {
|
|
918
|
+
new URL(input.value);
|
|
919
|
+
return true;
|
|
920
|
+
} catch {
|
|
921
|
+
return false;
|
|
922
|
+
}
|
|
923
|
+
},
|
|
924
|
+
message: ({ input, label, t }) => {
|
|
925
|
+
return input.getAttribute("data-formie-pattern-url-message") ?? input.getAttribute("data-pattern-url-message") ?? t("{attribute} is not a valid URL.", { attribute: label });
|
|
926
|
+
}
|
|
927
|
+
},
|
|
928
|
+
number: {
|
|
929
|
+
rule: ({ input, getRule }) => {
|
|
930
|
+
const rule = getRule("number");
|
|
931
|
+
if (!rule || !input.value || input.value.trim() === "") return true;
|
|
932
|
+
const value = parseFloat(input.value);
|
|
933
|
+
if (Number.isNaN(value)) return false;
|
|
934
|
+
if (rule !== true && typeof rule === "object") {
|
|
935
|
+
const min = typeof rule.min === "number" ? rule.min : null;
|
|
936
|
+
const max = typeof rule.max === "number" ? rule.max : null;
|
|
937
|
+
if (min !== null && value < min) return false;
|
|
938
|
+
if (max !== null && value > max) return false;
|
|
939
|
+
}
|
|
940
|
+
return true;
|
|
941
|
+
},
|
|
942
|
+
message: ({ input, label, getRule, t }) => {
|
|
943
|
+
const rule = getRule("number");
|
|
944
|
+
const min = rule !== true && rule && typeof rule === "object" && typeof rule.min === "number" ? rule.min : null;
|
|
945
|
+
const max = rule !== true && rule && typeof rule === "object" && typeof rule.max === "number" ? rule.max : null;
|
|
946
|
+
if (min !== null && max !== null) return t("{attribute} must be between {min} and {max}.", {
|
|
947
|
+
attribute: label,
|
|
948
|
+
min,
|
|
949
|
+
max
|
|
950
|
+
});
|
|
951
|
+
if (min !== null) return t("{attribute} must be no less than {min}.", {
|
|
952
|
+
attribute: label,
|
|
953
|
+
min
|
|
954
|
+
});
|
|
955
|
+
if (max !== null) return t("{attribute} must be no greater than {max}.", {
|
|
956
|
+
attribute: label,
|
|
957
|
+
max
|
|
958
|
+
});
|
|
959
|
+
return input.getAttribute("data-formie-pattern-number-message") ?? input.getAttribute("data-pattern-number-message") ?? t("{attribute} is not a valid number.", { attribute: label });
|
|
960
|
+
}
|
|
961
|
+
},
|
|
962
|
+
match: {
|
|
963
|
+
rule: (ctx) => {
|
|
964
|
+
const sourceInput = getComparableInput(ctx);
|
|
965
|
+
if (!sourceInput) return true;
|
|
966
|
+
return sourceInput.value === ctx.input.value;
|
|
967
|
+
},
|
|
968
|
+
message: (ctx) => {
|
|
969
|
+
const sourceField = getComparableInput(ctx)?.closest("[data-formie-field-handle]");
|
|
970
|
+
const sourceLabel = getLabelText(sourceField);
|
|
971
|
+
return ctx.t("{name} must match {value}.", {
|
|
972
|
+
name: ctx.label,
|
|
973
|
+
value: sourceLabel
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
};
|
|
978
|
+
//#endregion
|
|
979
|
+
//#region src/js/validation/validator.ts
|
|
980
|
+
var DEFAULT_PATTERNS = {
|
|
981
|
+
email: /^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*(\.\w{2,})+$/,
|
|
982
|
+
url: /^(?:(?:https?|HTTPS?|ftp|FTP):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)(?:\.(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)*(?:\.(?:[a-zA-Z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/,
|
|
983
|
+
number: /^(?:[-+]?[0-9]*[.,]?[0-9]+)$/,
|
|
984
|
+
color: /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/,
|
|
985
|
+
date: /(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))/,
|
|
986
|
+
time: /^(?:(0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9]))$/,
|
|
987
|
+
month: /^(?:(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])))$/
|
|
988
|
+
};
|
|
989
|
+
var debug$4 = createDebug("general", "validator");
|
|
990
|
+
function isValidationInput(node) {
|
|
991
|
+
return !!node && (node instanceof HTMLInputElement || node instanceof HTMLSelectElement || node instanceof HTMLTextAreaElement);
|
|
992
|
+
}
|
|
993
|
+
function removeDescribedBy$1(input, describedById) {
|
|
994
|
+
const current = (input.getAttribute("aria-describedby") || "").trim();
|
|
995
|
+
if (!current) return;
|
|
996
|
+
const filtered = current.split(/\s+/).filter((item) => {
|
|
997
|
+
return item !== describedById;
|
|
998
|
+
});
|
|
999
|
+
if (filtered.length) {
|
|
1000
|
+
input.setAttribute("aria-describedby", filtered.join(" "));
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
input.removeAttribute("aria-describedby");
|
|
1004
|
+
}
|
|
1005
|
+
function appendDescribedBy$1(input, describedById) {
|
|
1006
|
+
const current = (input.getAttribute("aria-describedby") || "").trim();
|
|
1007
|
+
const items = current ? current.split(/\s+/) : [];
|
|
1008
|
+
if (!items.includes(describedById)) items.push(describedById);
|
|
1009
|
+
input.setAttribute("aria-describedby", items.join(" ").trim());
|
|
1010
|
+
}
|
|
1011
|
+
function setErrorMessageReference$1(input, errorMessageId) {
|
|
1012
|
+
input.setAttribute("aria-errormessage", errorMessageId);
|
|
1013
|
+
}
|
|
1014
|
+
function clearErrorMessageReference$1(input, errorMessageId) {
|
|
1015
|
+
if (input.getAttribute("aria-errormessage") === errorMessageId) input.removeAttribute("aria-errormessage");
|
|
1016
|
+
}
|
|
1017
|
+
var FormieValidator = class {
|
|
1018
|
+
constructor(form, config = {}) {
|
|
1019
|
+
this.errors = [];
|
|
1020
|
+
this.validators = {};
|
|
1021
|
+
this.boundListeners = false;
|
|
1022
|
+
this.activated = /* @__PURE__ */ new WeakSet();
|
|
1023
|
+
this.submitted = false;
|
|
1024
|
+
this.initialValues = /* @__PURE__ */ new WeakMap();
|
|
1025
|
+
this.form = form;
|
|
1026
|
+
this.onBlur = this.blurHandler.bind(this);
|
|
1027
|
+
this.onChange = this.changeHandler.bind(this);
|
|
1028
|
+
this.onInput = this.inputHandler.bind(this);
|
|
1029
|
+
this.config = {
|
|
1030
|
+
live: false,
|
|
1031
|
+
errorMessage: "",
|
|
1032
|
+
fieldContainerErrorClass: [],
|
|
1033
|
+
inputErrorClass: [],
|
|
1034
|
+
messagesClass: [],
|
|
1035
|
+
messageClass: [],
|
|
1036
|
+
fieldsSelector: "input:not([type=\"hidden\"]):not([type=\"submit\"]):not([type=\"button\"]):not([disabled]), select:not([disabled]), textarea:not([disabled])",
|
|
1037
|
+
patterns: DEFAULT_PATTERNS,
|
|
1038
|
+
...config
|
|
1039
|
+
};
|
|
1040
|
+
Object.entries(rules_default).forEach(([validatorName, validator]) => {
|
|
1041
|
+
this.addValidator(validatorName, validator.rule, validator.message);
|
|
1042
|
+
});
|
|
1043
|
+
this.init();
|
|
1044
|
+
}
|
|
1045
|
+
init() {
|
|
1046
|
+
debug$4.log("Initializing validator.", {
|
|
1047
|
+
formId: this.form.id || null,
|
|
1048
|
+
live: this.config.live
|
|
1049
|
+
});
|
|
1050
|
+
this.form.setAttribute("novalidate", "true");
|
|
1051
|
+
this.inputs().forEach((input) => {
|
|
1052
|
+
this.initialValues.set(input, this.getInputValue(input));
|
|
1053
|
+
});
|
|
1054
|
+
if (this.config.live) this.addEventListeners();
|
|
1055
|
+
this.emitEvent(document, getValidatorEventName("ready"), { validator: this });
|
|
1056
|
+
}
|
|
1057
|
+
inputs(inputOrSelector = null) {
|
|
1058
|
+
if (isValidationInput(inputOrSelector)) return [inputOrSelector];
|
|
1059
|
+
const root = inputOrSelector || this.form;
|
|
1060
|
+
return Array.from(root.querySelectorAll(this.config.fieldsSelector)).filter((input) => {
|
|
1061
|
+
return isValidationInput(input);
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
getInputValue(input) {
|
|
1065
|
+
if (input instanceof HTMLInputElement && (input.type === "checkbox" || input.type === "radio")) return input.checked;
|
|
1066
|
+
if (input instanceof HTMLInputElement && input.type === "file") return input.files?.length ? Array.from(input.files).map((file) => {
|
|
1067
|
+
return file.name;
|
|
1068
|
+
}).join("|") : "";
|
|
1069
|
+
return input.value ?? "";
|
|
1070
|
+
}
|
|
1071
|
+
isDirty(input) {
|
|
1072
|
+
if (!this.initialValues.has(input)) {
|
|
1073
|
+
this.initialValues.set(input, this.getInputValue(input));
|
|
1074
|
+
return false;
|
|
1075
|
+
}
|
|
1076
|
+
return this.getInputValue(input) !== this.initialValues.get(input);
|
|
1077
|
+
}
|
|
1078
|
+
shouldShowError(input) {
|
|
1079
|
+
return this.submitted || this.activated.has(input);
|
|
1080
|
+
}
|
|
1081
|
+
validate(inputOrSelector = null, options = {}) {
|
|
1082
|
+
this.errors = [];
|
|
1083
|
+
const seenGroups = /* @__PURE__ */ new Set();
|
|
1084
|
+
this.inputs(inputOrSelector).forEach((input) => {
|
|
1085
|
+
let errorShown = false;
|
|
1086
|
+
if (!this.isVisible(input, options)) return;
|
|
1087
|
+
const field = input.closest("[data-formie-field-handle]");
|
|
1088
|
+
const groupKey = input instanceof HTMLInputElement && (input.type === "checkbox" || input.type === "radio") ? `${field?.getAttribute("data-formie-field-handle") || ""}:${input.name}` : null;
|
|
1089
|
+
if (groupKey) {
|
|
1090
|
+
if (seenGroups.has(groupKey)) return;
|
|
1091
|
+
seenGroups.add(groupKey);
|
|
1092
|
+
}
|
|
1093
|
+
if (this.shouldShowError(input)) this.removeError(input);
|
|
1094
|
+
const opts = this.getValidatorCallbackOptions(input);
|
|
1095
|
+
Object.entries(this.validators).forEach(([validatorName, validatorConfig]) => {
|
|
1096
|
+
if (!validatorConfig.validate(opts)) {
|
|
1097
|
+
const errorMessage = this.getErrorMessage(input, validatorName, validatorConfig, opts);
|
|
1098
|
+
if (this.shouldShowError(input) && !errorShown) this.showError(input, validatorName, errorMessage);
|
|
1099
|
+
this.errors.push({
|
|
1100
|
+
input,
|
|
1101
|
+
field: opts.field,
|
|
1102
|
+
validator: validatorName,
|
|
1103
|
+
message: errorMessage,
|
|
1104
|
+
handle: opts.field?.getAttribute("data-formie-field-handle") || null,
|
|
1105
|
+
result: false
|
|
1106
|
+
});
|
|
1107
|
+
errorShown = true;
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
1110
|
+
if (!errorShown && this.shouldShowError(input)) this.removeError(input);
|
|
1111
|
+
});
|
|
1112
|
+
debug$4.log("Validation pass complete.", {
|
|
1113
|
+
errorCount: this.errors.length,
|
|
1114
|
+
includeHiddenPages: options.includeHiddenPages === true
|
|
1115
|
+
});
|
|
1116
|
+
return this.errors;
|
|
1117
|
+
}
|
|
1118
|
+
removeAllErrors() {
|
|
1119
|
+
this.inputs().forEach((input) => {
|
|
1120
|
+
this.removeError(input);
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
removeError(input) {
|
|
1124
|
+
const fieldContainer = input.closest("[data-formie-field-handle]");
|
|
1125
|
+
if (!fieldContainer) {
|
|
1126
|
+
input.removeAttribute("aria-invalid");
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
const errorMessages = fieldContainer.querySelector("[data-formie-field-errors]");
|
|
1130
|
+
const errorContainerId = errorMessages?.id || "";
|
|
1131
|
+
fieldContainer.querySelectorAll("[data-formie-field-error]").forEach((node) => {
|
|
1132
|
+
node.remove();
|
|
1133
|
+
});
|
|
1134
|
+
if (errorMessages) errorMessages.innerHTML = "";
|
|
1135
|
+
fieldContainer.querySelectorAll("input, select, textarea").forEach((fieldInput) => {
|
|
1136
|
+
const element = fieldInput;
|
|
1137
|
+
element.removeAttribute("aria-invalid");
|
|
1138
|
+
if (this.config.inputErrorClass.length) element.classList.remove(...this.config.inputErrorClass);
|
|
1139
|
+
element.removeAttribute("data-formie-input-has-error");
|
|
1140
|
+
if (errorContainerId) removeDescribedBy$1(element, errorContainerId);
|
|
1141
|
+
fieldContainer.querySelectorAll("[data-formie-field-error]").forEach((errorNode) => {
|
|
1142
|
+
const errorMessageId = errorNode.id;
|
|
1143
|
+
if (errorMessageId) clearErrorMessageReference$1(element, errorMessageId);
|
|
1144
|
+
});
|
|
1145
|
+
});
|
|
1146
|
+
for (let element = fieldContainer; element; element = element.parentElement?.closest("[data-formie-field-handle]")) {
|
|
1147
|
+
if (this.config.fieldContainerErrorClass.length) element.classList.remove(...this.config.fieldContainerErrorClass);
|
|
1148
|
+
element.removeAttribute("data-formie-field-has-error");
|
|
1149
|
+
}
|
|
1150
|
+
this.emitEvent(input, getValidatorEventName("clear-error"), { validator: this });
|
|
1151
|
+
syncPageTabErrors(this.form);
|
|
1152
|
+
}
|
|
1153
|
+
showError(input, validatorName, errorMessage) {
|
|
1154
|
+
const fieldContainer = input.closest("[data-formie-field-handle]");
|
|
1155
|
+
if (!fieldContainer) return;
|
|
1156
|
+
let errorMessages = fieldContainer.querySelector("[data-formie-field-errors]");
|
|
1157
|
+
if (!errorMessages) {
|
|
1158
|
+
errorMessages = document.createElement("div");
|
|
1159
|
+
errorMessages.setAttribute("data-formie-field-errors", "true");
|
|
1160
|
+
if (this.config.messagesClass.length) errorMessages.classList.add(...this.config.messagesClass);
|
|
1161
|
+
fieldContainer.appendChild(errorMessages);
|
|
1162
|
+
}
|
|
1163
|
+
if (this.config.messagesClass.length) errorMessages.classList.add(...this.config.messagesClass);
|
|
1164
|
+
errorMessages.innerHTML = "";
|
|
1165
|
+
const handle = fieldContainer.getAttribute("data-formie-field-handle") || "field";
|
|
1166
|
+
const errorId = `${handle}-error`;
|
|
1167
|
+
errorMessages.id = errorMessages.id || `${handle}-errors`;
|
|
1168
|
+
errorMessages.setAttribute("aria-live", "polite");
|
|
1169
|
+
errorMessages.setAttribute("aria-atomic", "true");
|
|
1170
|
+
const errorElement = document.createElement("div");
|
|
1171
|
+
errorElement.setAttribute("data-formie-field-error", "true");
|
|
1172
|
+
errorElement.setAttribute(`data-formie-field-error-${validatorName}`, "true");
|
|
1173
|
+
errorElement.setAttribute("id", errorId);
|
|
1174
|
+
errorElement.setAttribute("role", "alert");
|
|
1175
|
+
if (this.config.messageClass.length) errorElement.classList.add(...this.config.messageClass);
|
|
1176
|
+
errorElement.textContent = errorMessage;
|
|
1177
|
+
errorMessages.appendChild(errorElement);
|
|
1178
|
+
fieldContainer.setAttribute("data-formie-field-has-error", "true");
|
|
1179
|
+
fieldContainer.querySelectorAll("input, select, textarea").forEach((fieldInput) => {
|
|
1180
|
+
const element = fieldInput;
|
|
1181
|
+
element.setAttribute("aria-invalid", "true");
|
|
1182
|
+
if (this.config.inputErrorClass.length) element.classList.add(...this.config.inputErrorClass);
|
|
1183
|
+
element.setAttribute("data-formie-input-has-error", "true");
|
|
1184
|
+
appendDescribedBy$1(element, errorMessages.id);
|
|
1185
|
+
setErrorMessageReference$1(element, errorId);
|
|
1186
|
+
});
|
|
1187
|
+
for (let element = fieldContainer; element; element = element.parentElement?.closest("[data-formie-field-handle]")) {
|
|
1188
|
+
if (this.config.fieldContainerErrorClass.length) element.classList.add(...this.config.fieldContainerErrorClass);
|
|
1189
|
+
element.setAttribute("data-formie-field-has-error", "true");
|
|
1190
|
+
}
|
|
1191
|
+
this.emitEvent(input, getValidatorEventName("show-error"), {
|
|
1192
|
+
validator: this,
|
|
1193
|
+
validatorName,
|
|
1194
|
+
errorMessage
|
|
1195
|
+
});
|
|
1196
|
+
syncPageTabErrors(this.form);
|
|
1197
|
+
}
|
|
1198
|
+
getValidatorCallbackOptions(input) {
|
|
1199
|
+
const fieldContainer = input.closest("[data-formie-field-handle]");
|
|
1200
|
+
const label = fieldContainer?.querySelector("[data-formie-field-label]")?.childNodes[0]?.textContent?.trim() ?? "";
|
|
1201
|
+
const rules = this.parseValidationRules(fieldContainer?.getAttribute("data-formie-validation"));
|
|
1202
|
+
return {
|
|
1203
|
+
t,
|
|
1204
|
+
input,
|
|
1205
|
+
label,
|
|
1206
|
+
field: fieldContainer,
|
|
1207
|
+
form: this.form,
|
|
1208
|
+
config: this.config,
|
|
1209
|
+
rules,
|
|
1210
|
+
getRule: (rule) => {
|
|
1211
|
+
return this.getRule(fieldContainer, rule);
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
getErrorMessage(input, validatorName, validator, opts) {
|
|
1216
|
+
return (typeof validator.errorMessage === "function" ? validator.errorMessage(opts) : validator.errorMessage) ?? t("{attribute} is invalid.", { attribute: opts.label });
|
|
1217
|
+
}
|
|
1218
|
+
getErrors() {
|
|
1219
|
+
return this.errors;
|
|
1220
|
+
}
|
|
1221
|
+
getFieldErrors(errors = this.errors) {
|
|
1222
|
+
const fieldErrors = {};
|
|
1223
|
+
errors.forEach((error) => {
|
|
1224
|
+
if (!error.handle || fieldErrors[error.handle]?.length) return;
|
|
1225
|
+
fieldErrors[error.handle] = [error.message];
|
|
1226
|
+
});
|
|
1227
|
+
return fieldErrors;
|
|
1228
|
+
}
|
|
1229
|
+
getRule(field, rule) {
|
|
1230
|
+
if (!field) return false;
|
|
1231
|
+
const rules = this.parseValidationRules(field.getAttribute("data-formie-validation"));
|
|
1232
|
+
if (Object.prototype.hasOwnProperty.call(rules, rule)) return rules[rule];
|
|
1233
|
+
return false;
|
|
1234
|
+
}
|
|
1235
|
+
parseValidationRules(ruleString) {
|
|
1236
|
+
const rules = {};
|
|
1237
|
+
if (!ruleString) return rules;
|
|
1238
|
+
let parsedRules = null;
|
|
1239
|
+
try {
|
|
1240
|
+
parsedRules = JSON.parse(ruleString);
|
|
1241
|
+
} catch {
|
|
1242
|
+
debug$4.warn("Invalid validation rules payload.", { formId: this.form.id || null });
|
|
1243
|
+
return rules;
|
|
1244
|
+
}
|
|
1245
|
+
if (!Array.isArray(parsedRules)) return rules;
|
|
1246
|
+
parsedRules.forEach((part) => {
|
|
1247
|
+
if (!part || typeof part !== "object" || Array.isArray(part)) return;
|
|
1248
|
+
const candidate = part;
|
|
1249
|
+
const type = typeof candidate.type === "string" ? candidate.type.trim() : "";
|
|
1250
|
+
if (!type) return;
|
|
1251
|
+
rules[type] = candidate;
|
|
1252
|
+
});
|
|
1253
|
+
return rules;
|
|
1254
|
+
}
|
|
1255
|
+
destroy() {
|
|
1256
|
+
debug$4.log("Destroying validator.", { formId: this.form.id || null });
|
|
1257
|
+
this.removeEventListeners();
|
|
1258
|
+
this.form.removeAttribute("novalidate");
|
|
1259
|
+
this.emitEvent(document, getValidatorEventName("destroy"), { validator: this });
|
|
1260
|
+
}
|
|
1261
|
+
isVisible(element, options = {}) {
|
|
1262
|
+
if (element.closest("[data-formie-conditionally-hidden]")) return false;
|
|
1263
|
+
if (element.closest("[data-formie-page-hidden]")) return !!options.includeHiddenPages;
|
|
1264
|
+
return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
|
|
1265
|
+
}
|
|
1266
|
+
blurHandler(event) {
|
|
1267
|
+
if (!(event.target instanceof HTMLElement) || !isValidationInput(event.target) || !event.target.form?.isSameNode(this.form)) return;
|
|
1268
|
+
if (event instanceof CustomEvent) return;
|
|
1269
|
+
if (event.target instanceof HTMLInputElement && event.target.type === "file") return;
|
|
1270
|
+
if (event.target instanceof HTMLInputElement && (event.target.type === "checkbox" || event.target.type === "radio")) return;
|
|
1271
|
+
if (this.isDirty(event.target)) this.activated.add(event.target);
|
|
1272
|
+
if (this.shouldShowError(event.target)) this.validate(event.target);
|
|
1273
|
+
}
|
|
1274
|
+
changeHandler(event) {
|
|
1275
|
+
if (!(event.target instanceof HTMLElement) || !isValidationInput(event.target) || !event.target.form?.isSameNode(this.form)) return;
|
|
1276
|
+
if (event instanceof CustomEvent) return;
|
|
1277
|
+
if (event.target instanceof HTMLSelectElement) {
|
|
1278
|
+
this.activated.add(event.target);
|
|
1279
|
+
this.validate(event.target);
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
if (!(event.target instanceof HTMLInputElement)) return;
|
|
1283
|
+
if (event.target.type !== "file" && event.target.type !== "checkbox" && event.target.type !== "radio") return;
|
|
1284
|
+
this.activated.add(event.target);
|
|
1285
|
+
this.validate(event.target);
|
|
1286
|
+
}
|
|
1287
|
+
inputHandler(event) {
|
|
1288
|
+
if (!(event.target instanceof HTMLElement) || !isValidationInput(event.target) || !event.target.form?.isSameNode(this.form)) return;
|
|
1289
|
+
if (event instanceof CustomEvent) return;
|
|
1290
|
+
if (event.target instanceof HTMLInputElement && (event.target.type === "checkbox" || event.target.type === "radio")) return;
|
|
1291
|
+
if (this.shouldShowError(event.target)) this.validate(event.target);
|
|
1292
|
+
}
|
|
1293
|
+
submit(inputOrSelector = null, { final = false } = {}) {
|
|
1294
|
+
this.submitted = true;
|
|
1295
|
+
debug$4.log("Submit validation requested.", { final });
|
|
1296
|
+
if (!this.boundListeners) this.addEventListeners();
|
|
1297
|
+
this.removeAllErrors();
|
|
1298
|
+
return this.validate(inputOrSelector, { includeHiddenPages: final });
|
|
1299
|
+
}
|
|
1300
|
+
resetLiveState() {
|
|
1301
|
+
this.submitted = false;
|
|
1302
|
+
this.activated = /* @__PURE__ */ new WeakSet();
|
|
1303
|
+
this.errors = [];
|
|
1304
|
+
this.removeAllErrors();
|
|
1305
|
+
}
|
|
1306
|
+
addEventListeners() {
|
|
1307
|
+
if (this.boundListeners) return;
|
|
1308
|
+
this.form.addEventListener("blur", this.onBlur, true);
|
|
1309
|
+
this.form.addEventListener("change", this.onChange, false);
|
|
1310
|
+
this.form.addEventListener("input", this.onInput, false);
|
|
1311
|
+
this.boundListeners = true;
|
|
1312
|
+
debug$4.log("Event listeners attached.");
|
|
1313
|
+
}
|
|
1314
|
+
removeEventListeners() {
|
|
1315
|
+
this.form.removeEventListener("blur", this.onBlur, true);
|
|
1316
|
+
this.form.removeEventListener("change", this.onChange, false);
|
|
1317
|
+
this.form.removeEventListener("input", this.onInput, false);
|
|
1318
|
+
this.boundListeners = false;
|
|
1319
|
+
debug$4.log("Event listeners removed.");
|
|
1320
|
+
}
|
|
1321
|
+
emitEvent(target, type, detail = {}) {
|
|
1322
|
+
target.dispatchEvent(new CustomEvent(type, {
|
|
1323
|
+
bubbles: true,
|
|
1324
|
+
detail
|
|
1325
|
+
}));
|
|
1326
|
+
}
|
|
1327
|
+
addValidator(name, validatorFunction, errorMessage) {
|
|
1328
|
+
this.validators[name] = {
|
|
1329
|
+
validate: validatorFunction,
|
|
1330
|
+
errorMessage
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
removeValidator(name) {
|
|
1334
|
+
delete this.validators[name];
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
//#endregion
|
|
1338
|
+
//#region src/js/core/submit-result-ui.ts
|
|
1339
|
+
var successHideTimers = /* @__PURE__ */ new WeakMap();
|
|
1340
|
+
function getConfiguredSubmitAction(form) {
|
|
1341
|
+
return (form.dataset.formieSubmitAction || "").trim();
|
|
1342
|
+
}
|
|
1343
|
+
function getErrorMessagePosition(form) {
|
|
1344
|
+
return (form.dataset.formieErrorMessagePosition || "top-form").trim() || "top-form";
|
|
1345
|
+
}
|
|
1346
|
+
function getSuccessMessagePosition(form) {
|
|
1347
|
+
return (form.dataset.formieSubmitActionMessagePosition || "").trim();
|
|
1348
|
+
}
|
|
1349
|
+
function getSuccessMessageTimeoutMs(form) {
|
|
1350
|
+
const rawValue = (form.dataset.formieSubmitActionMessageTimeout || "").trim();
|
|
1351
|
+
if (!rawValue) return null;
|
|
1352
|
+
const seconds = Number.parseFloat(rawValue);
|
|
1353
|
+
if (!Number.isFinite(seconds) || seconds < 0) return null;
|
|
1354
|
+
return Math.round(seconds * 1e3);
|
|
1355
|
+
}
|
|
1356
|
+
function shouldHideFormOnSuccess(form) {
|
|
1357
|
+
const rawValue = form.dataset.formieSubmitActionFormHide;
|
|
1358
|
+
if (rawValue === void 0) return false;
|
|
1359
|
+
const normalized = rawValue.trim().toLowerCase();
|
|
1360
|
+
return normalized === "true" || normalized === "1" || normalized === "";
|
|
1361
|
+
}
|
|
1362
|
+
function clearPendingSuccessHide(form) {
|
|
1363
|
+
const timerId = successHideTimers.get(form);
|
|
1364
|
+
if (typeof timerId === "number") {
|
|
1365
|
+
window.clearTimeout(timerId);
|
|
1366
|
+
successHideTimers.delete(form);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
function getTopMessageHost(form) {
|
|
1370
|
+
return form.querySelector("[data-formie-form-messages-top]") || form;
|
|
1371
|
+
}
|
|
1372
|
+
function getBottomMessageHost(form) {
|
|
1373
|
+
return form.querySelector("[data-formie-form-messages-bottom]") || form;
|
|
1374
|
+
}
|
|
1375
|
+
function getErrorMessageHost(form, position) {
|
|
1376
|
+
if (position === "bottom-form") return getBottomMessageHost(form);
|
|
1377
|
+
return getTopMessageHost(form);
|
|
1378
|
+
}
|
|
1379
|
+
function getSuccessMessageHost(form, position) {
|
|
1380
|
+
if (position === "top-form") return getTopMessageHost(form);
|
|
1381
|
+
if (position === "bottom-form" && !shouldHideFormOnSuccess(form)) return getBottomMessageHost(form);
|
|
1382
|
+
return form;
|
|
1383
|
+
}
|
|
1384
|
+
function ensureFormErrorContainer(form) {
|
|
1385
|
+
const position = getErrorMessagePosition(form);
|
|
1386
|
+
const host = getErrorMessageHost(form, position);
|
|
1387
|
+
let container = host.querySelector("[data-formie-error-container], [data-formie-errors]");
|
|
1388
|
+
if (!container) {
|
|
1389
|
+
container = document.createElement("div");
|
|
1390
|
+
container.setAttribute("data-formie-errors", "true");
|
|
1391
|
+
addThemeClasses(container, form, "errors");
|
|
1392
|
+
}
|
|
1393
|
+
container.setAttribute("data-formie-error-container", "true");
|
|
1394
|
+
if (position === "bottom-form") host.append(container);
|
|
1395
|
+
else host.prepend(container);
|
|
1396
|
+
return container;
|
|
1397
|
+
}
|
|
1398
|
+
function ensureFormErrorMessageContainer(form, container) {
|
|
1399
|
+
let messageContainer = container.querySelector("[data-formie-error-message-container], [data-formie-message][data-formie-message-error]");
|
|
1400
|
+
if (!messageContainer) {
|
|
1401
|
+
messageContainer = document.createElement("div");
|
|
1402
|
+
messageContainer.setAttribute("data-formie-error-message-container", "true");
|
|
1403
|
+
container.appendChild(messageContainer);
|
|
1404
|
+
}
|
|
1405
|
+
messageContainer.setAttribute("data-formie-message", "true");
|
|
1406
|
+
messageContainer.setAttribute("data-formie-message-error", "true");
|
|
1407
|
+
addThemeClasses(messageContainer, form, "message", "messageError");
|
|
1408
|
+
messageContainer.setAttribute("role", "alert");
|
|
1409
|
+
messageContainer.setAttribute("aria-live", "polite");
|
|
1410
|
+
messageContainer.setAttribute("aria-atomic", "true");
|
|
1411
|
+
return messageContainer;
|
|
1412
|
+
}
|
|
1413
|
+
function ensureFormSuccessContainer(form, position) {
|
|
1414
|
+
let container = form.querySelector("[data-formie-success-container]");
|
|
1415
|
+
const host = getSuccessMessageHost(form, position);
|
|
1416
|
+
if (!container) {
|
|
1417
|
+
container = document.createElement("div");
|
|
1418
|
+
container.setAttribute("data-formie-success-container", "true");
|
|
1419
|
+
addThemeClasses(container, form, "successes");
|
|
1420
|
+
}
|
|
1421
|
+
if (position === "bottom-form") host.append(container);
|
|
1422
|
+
else if (host === form) host.prepend(container);
|
|
1423
|
+
else host.prepend(container);
|
|
1424
|
+
return container;
|
|
1425
|
+
}
|
|
1426
|
+
function ensureFieldErrorContainer(fieldNode) {
|
|
1427
|
+
let container = fieldNode.querySelector("[data-formie-field-errors]");
|
|
1428
|
+
if (!container) {
|
|
1429
|
+
container = document.createElement("div");
|
|
1430
|
+
container.setAttribute("data-formie-field-errors", "true");
|
|
1431
|
+
addThemeClasses(container, fieldNode, "fieldErrors");
|
|
1432
|
+
fieldNode.appendChild(container);
|
|
1433
|
+
}
|
|
1434
|
+
return container;
|
|
1435
|
+
}
|
|
1436
|
+
function removeDescribedBy(input, describedById) {
|
|
1437
|
+
const current = (input.getAttribute("aria-describedby") || "").trim();
|
|
1438
|
+
if (!current) return;
|
|
1439
|
+
const nextValue = current.split(/\s+/).filter((item) => {
|
|
1440
|
+
return item !== describedById;
|
|
1441
|
+
}).join(" ").trim();
|
|
1442
|
+
if (nextValue) {
|
|
1443
|
+
input.setAttribute("aria-describedby", nextValue);
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
input.removeAttribute("aria-describedby");
|
|
1447
|
+
}
|
|
1448
|
+
function setErrorMessageReference(input, errorMessageId) {
|
|
1449
|
+
input.setAttribute("aria-errormessage", errorMessageId);
|
|
1450
|
+
}
|
|
1451
|
+
function clearErrorMessageReference(input, errorMessageId) {
|
|
1452
|
+
if (input.getAttribute("aria-errormessage") === errorMessageId) input.removeAttribute("aria-errormessage");
|
|
1453
|
+
}
|
|
1454
|
+
function clearFieldErrors(form) {
|
|
1455
|
+
form.querySelectorAll("[data-formie-field-handle]").forEach((fieldNode) => {
|
|
1456
|
+
const fieldElement = fieldNode;
|
|
1457
|
+
const container = fieldElement.querySelector("[data-formie-field-errors]");
|
|
1458
|
+
const containerId = container?.id || "";
|
|
1459
|
+
const errorMessageIds = Array.from(fieldElement.querySelectorAll("[data-formie-field-error]")).map((node) => {
|
|
1460
|
+
return node.id;
|
|
1461
|
+
}).filter(Boolean);
|
|
1462
|
+
removeThemeClasses(fieldElement, form, "fieldLayoutError");
|
|
1463
|
+
fieldElement.removeAttribute("data-formie-field-has-error");
|
|
1464
|
+
fieldElement.querySelectorAll("[data-formie-field-error]").forEach((node) => {
|
|
1465
|
+
node.remove();
|
|
1466
|
+
});
|
|
1467
|
+
if (container && !container.querySelector("[data-formie-field-error]")) container.innerHTML = "";
|
|
1468
|
+
fieldElement.querySelectorAll("input, select, textarea").forEach((input) => {
|
|
1469
|
+
const element = input;
|
|
1470
|
+
element.removeAttribute("aria-invalid");
|
|
1471
|
+
removeThemeClasses(element, form, "fieldControlError");
|
|
1472
|
+
element.removeAttribute("data-formie-input-has-error");
|
|
1473
|
+
if (containerId) removeDescribedBy(element, containerId);
|
|
1474
|
+
errorMessageIds.forEach((errorMessageId) => {
|
|
1475
|
+
clearErrorMessageReference(element, errorMessageId);
|
|
1476
|
+
});
|
|
1477
|
+
});
|
|
1478
|
+
});
|
|
1479
|
+
syncPageTabErrors(form);
|
|
1480
|
+
}
|
|
1481
|
+
function clearFormErrors(form) {
|
|
1482
|
+
form.querySelectorAll("[data-formie-error-container], [data-formie-errors]").forEach((node) => {
|
|
1483
|
+
const container = node;
|
|
1484
|
+
container.querySelectorAll("[data-formie-error]").forEach((errorNode) => {
|
|
1485
|
+
errorNode.remove();
|
|
1486
|
+
});
|
|
1487
|
+
removeThemeClasses(container, form, "message", "messageError");
|
|
1488
|
+
container.removeAttribute("data-formie-message");
|
|
1489
|
+
container.removeAttribute("data-formie-message-error");
|
|
1490
|
+
container.removeAttribute("role");
|
|
1491
|
+
container.removeAttribute("aria-live");
|
|
1492
|
+
container.removeAttribute("aria-atomic");
|
|
1493
|
+
if (!container.querySelector("[data-formie-error]")) container.innerHTML = "";
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
function clearFormSuccess(form) {
|
|
1497
|
+
clearPendingSuccessHide(form);
|
|
1498
|
+
form.querySelectorAll("[data-formie-message-success]:not([data-formie-success-container])").forEach((node) => {
|
|
1499
|
+
node.remove();
|
|
1500
|
+
});
|
|
1501
|
+
form.querySelectorAll("[data-formie-success-container]").forEach((node) => {
|
|
1502
|
+
const container = node;
|
|
1503
|
+
container.querySelectorAll("[data-formie-success]").forEach((successNode) => {
|
|
1504
|
+
successNode.remove();
|
|
1505
|
+
});
|
|
1506
|
+
removeThemeClasses(container, form, "message", "messageSuccess");
|
|
1507
|
+
container.removeAttribute("data-formie-message");
|
|
1508
|
+
container.removeAttribute("data-formie-message-success");
|
|
1509
|
+
container.removeAttribute("role");
|
|
1510
|
+
container.removeAttribute("aria-live");
|
|
1511
|
+
container.removeAttribute("aria-atomic");
|
|
1512
|
+
if (!container.querySelector("[data-formie-success]")) container.innerHTML = "";
|
|
1513
|
+
});
|
|
1514
|
+
if (!(getConfiguredSubmitAction(form) === "message" && shouldHideFormOnSuccess(form))) setFormHiddenState(form, false);
|
|
1515
|
+
}
|
|
1516
|
+
function clearAriaInvalid(form) {
|
|
1517
|
+
form.querySelectorAll("[aria-invalid=\"true\"]").forEach((node) => {
|
|
1518
|
+
node.removeAttribute("aria-invalid");
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
function appendDescribedBy(input, describedById) {
|
|
1522
|
+
const current = (input.getAttribute("aria-describedby") || "").trim();
|
|
1523
|
+
const items = current ? current.split(/\s+/) : [];
|
|
1524
|
+
if (!items.includes(describedById)) items.push(describedById);
|
|
1525
|
+
input.setAttribute("aria-describedby", items.join(" ").trim());
|
|
1526
|
+
}
|
|
1527
|
+
function renderFieldErrors(form, fieldErrors) {
|
|
1528
|
+
Object.entries(fieldErrors).forEach(([handle, messages]) => {
|
|
1529
|
+
const fieldNode = form.querySelector(`[data-formie-field-handle="${handle}"]`);
|
|
1530
|
+
if (!fieldNode) return;
|
|
1531
|
+
const container = ensureFieldErrorContainer(fieldNode);
|
|
1532
|
+
const containerId = container.id && container.id.trim() ? container.id : `${handle}-errors`;
|
|
1533
|
+
container.id = containerId;
|
|
1534
|
+
container.setAttribute("aria-live", "polite");
|
|
1535
|
+
container.setAttribute("aria-atomic", "true");
|
|
1536
|
+
addThemeClasses(fieldNode, form, "fieldLayoutError");
|
|
1537
|
+
fieldNode.setAttribute("data-formie-field-has-error", "true");
|
|
1538
|
+
messages.forEach((message, index) => {
|
|
1539
|
+
const errorNode = document.createElement("div");
|
|
1540
|
+
errorNode.setAttribute("data-formie-field-error", "true");
|
|
1541
|
+
errorNode.setAttribute("role", "alert");
|
|
1542
|
+
errorNode.id = `${containerId}-${index + 1}`;
|
|
1543
|
+
addThemeClasses(errorNode, form, "fieldError");
|
|
1544
|
+
errorNode.textContent = message;
|
|
1545
|
+
container.appendChild(errorNode);
|
|
1546
|
+
});
|
|
1547
|
+
const primaryErrorId = container.querySelector("[data-formie-field-error]")?.id;
|
|
1548
|
+
fieldNode.querySelectorAll("input, select, textarea").forEach((input) => {
|
|
1549
|
+
const element = input;
|
|
1550
|
+
element.setAttribute("aria-invalid", "true");
|
|
1551
|
+
addThemeClasses(element, form, "fieldControlError");
|
|
1552
|
+
element.setAttribute("data-formie-input-has-error", "true");
|
|
1553
|
+
appendDescribedBy(element, containerId);
|
|
1554
|
+
if (primaryErrorId) setErrorMessageReference(element, primaryErrorId);
|
|
1555
|
+
const instructions = fieldNode.querySelector("[data-formie-instructions]");
|
|
1556
|
+
if (instructions?.id) appendDescribedBy(element, instructions.id);
|
|
1557
|
+
});
|
|
1558
|
+
});
|
|
1559
|
+
syncPageTabErrors(form);
|
|
1560
|
+
}
|
|
1561
|
+
function renderFormErrors(form, formErrors) {
|
|
1562
|
+
const container = ensureFormErrorContainer(form);
|
|
1563
|
+
const messageContainer = ensureFormErrorMessageContainer(form, container);
|
|
1564
|
+
addThemeClasses(container, form, "errors");
|
|
1565
|
+
formErrors.forEach((error) => {
|
|
1566
|
+
const errorNode = document.createElement("div");
|
|
1567
|
+
errorNode.setAttribute("data-formie-error", "true");
|
|
1568
|
+
errorNode.setAttribute("role", "alert");
|
|
1569
|
+
addThemeClasses(errorNode, form, "error");
|
|
1570
|
+
errorNode.innerHTML = error;
|
|
1571
|
+
messageContainer.appendChild(errorNode);
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
function shouldRenderSuccessMessage(form, result) {
|
|
1575
|
+
if (!result.message || result.nextPage || result.redirect) return false;
|
|
1576
|
+
if (result.action === "save") return true;
|
|
1577
|
+
return getConfiguredSubmitAction(form) === "message" && getSuccessMessagePosition(form) !== "";
|
|
1578
|
+
}
|
|
1579
|
+
function renderFormSuccess(form, message) {
|
|
1580
|
+
const position = getSuccessMessagePosition(form);
|
|
1581
|
+
if (!position) return;
|
|
1582
|
+
const container = ensureFormSuccessContainer(form, position);
|
|
1583
|
+
addThemeClasses(container, form, "message", "messageSuccess");
|
|
1584
|
+
container.setAttribute("data-formie-message", "true");
|
|
1585
|
+
container.setAttribute("data-formie-message-success", "true");
|
|
1586
|
+
container.setAttribute("role", "status");
|
|
1587
|
+
container.setAttribute("aria-live", "polite");
|
|
1588
|
+
container.setAttribute("aria-atomic", "true");
|
|
1589
|
+
const successNode = document.createElement("div");
|
|
1590
|
+
successNode.setAttribute("data-formie-success", "true");
|
|
1591
|
+
addThemeClasses(successNode, form, "success");
|
|
1592
|
+
successNode.innerHTML = message;
|
|
1593
|
+
container.appendChild(successNode);
|
|
1594
|
+
if (shouldHideFormOnSuccess(form)) setFormHiddenState(form, true);
|
|
1595
|
+
const timeoutMs = getSuccessMessageTimeoutMs(form);
|
|
1596
|
+
if (timeoutMs !== null) {
|
|
1597
|
+
const timerId = window.setTimeout(() => {
|
|
1598
|
+
successHideTimers.delete(form);
|
|
1599
|
+
clearFormSuccess(form);
|
|
1600
|
+
}, timeoutMs);
|
|
1601
|
+
successHideTimers.set(form, timerId);
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
function applySubmitResultUi(form, result) {
|
|
1605
|
+
clearFieldErrors(form);
|
|
1606
|
+
clearFormErrors(form);
|
|
1607
|
+
clearFormSuccess(form);
|
|
1608
|
+
clearAriaInvalid(form);
|
|
1609
|
+
if (result.ok) {
|
|
1610
|
+
if (shouldRenderSuccessMessage(form, result)) renderFormSuccess(form, result.message || "");
|
|
1611
|
+
return;
|
|
1612
|
+
}
|
|
1613
|
+
if (result.fieldErrors) renderFieldErrors(form, result.fieldErrors);
|
|
1614
|
+
if (result.formErrors?.length) {
|
|
1615
|
+
renderFormErrors(form, result.formErrors);
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
if (!result.fieldErrors && result.message) renderFormErrors(form, [result.message]);
|
|
1619
|
+
}
|
|
1620
|
+
//#endregion
|
|
1621
|
+
//#region src/js/core/submit-flow.ts
|
|
1622
|
+
var debug$3 = createDebug("general", "submit-flow");
|
|
1623
|
+
function shouldRefreshTokensAfterSubmit(result) {
|
|
1624
|
+
if (!result.ok && result.stage === "validate") return false;
|
|
1625
|
+
return true;
|
|
1626
|
+
}
|
|
1627
|
+
function shouldKeepSubmitLoading(result) {
|
|
1628
|
+
if (!result) return false;
|
|
1629
|
+
if (result.keepSubmitLoading === true) return true;
|
|
1630
|
+
if (result.ok && result.redirect?.url && result.redirect.target !== "new-tab") return true;
|
|
1631
|
+
return false;
|
|
1632
|
+
}
|
|
1633
|
+
function clearSubmitFeedback(form) {
|
|
1634
|
+
clearFieldErrors(form);
|
|
1635
|
+
clearFormErrors(form);
|
|
1636
|
+
clearFormSuccess(form);
|
|
1637
|
+
clearAriaInvalid(form);
|
|
1638
|
+
}
|
|
1639
|
+
async function executeAjaxSubmitFlow(params) {
|
|
1640
|
+
const { id, target, form, bus, validator, validateOnSubmit, action, submitter, waitForSubmitDelay, onRefreshTokensAfterSubmit, dispatchSubmitResult } = params;
|
|
1641
|
+
clearSubmitFeedback(form);
|
|
1642
|
+
setSubmitLoading(form, submitter || null);
|
|
1643
|
+
let result = {
|
|
1644
|
+
ok: false,
|
|
1645
|
+
code: "SUBMIT_ERROR",
|
|
1646
|
+
message: "Submission failed.",
|
|
1647
|
+
formErrors: ["Submission failed."]
|
|
1648
|
+
};
|
|
1649
|
+
try {
|
|
1650
|
+
await waitForSubmitDelay(form);
|
|
1651
|
+
result = await runSubmitPipeline(form, action, bus, {
|
|
1652
|
+
validator,
|
|
1653
|
+
validateOnSubmit
|
|
1654
|
+
});
|
|
1655
|
+
applySubmitResultUi(form, result);
|
|
1656
|
+
applySubmitResultState(form, result, action);
|
|
1657
|
+
if (shouldRefreshTokensAfterSubmit(result)) await onRefreshTokensAfterSubmit(result);
|
|
1658
|
+
dispatchSubmitResult(result);
|
|
1659
|
+
} catch (error) {
|
|
1660
|
+
result = {
|
|
1661
|
+
ok: false,
|
|
1662
|
+
code: "SUBMIT_ERROR",
|
|
1663
|
+
message: error instanceof Error ? error.message : "Submission failed.",
|
|
1664
|
+
formErrors: [error instanceof Error ? error.message : "Submission failed."]
|
|
1665
|
+
};
|
|
1666
|
+
applySubmitResultUi(form, result);
|
|
1667
|
+
dispatchSubmitResult(result);
|
|
1668
|
+
debug$3.warn("Submit failed with exception.", {
|
|
1669
|
+
id,
|
|
1670
|
+
action,
|
|
1671
|
+
target,
|
|
1672
|
+
error: error instanceof Error ? error.message : error
|
|
1673
|
+
});
|
|
1674
|
+
} finally {
|
|
1675
|
+
if (!shouldKeepSubmitLoading(result)) clearSubmitLoading(form);
|
|
1676
|
+
}
|
|
1677
|
+
return result;
|
|
1678
|
+
}
|
|
1679
|
+
//#endregion
|
|
1680
|
+
//#region src/js/modules/registry.ts
|
|
1681
|
+
var ModuleRegistry = class {
|
|
1682
|
+
constructor() {
|
|
1683
|
+
this.modules = /* @__PURE__ */ new Map();
|
|
1684
|
+
}
|
|
1685
|
+
register(moduleDefinition, options = {}) {
|
|
1686
|
+
const existing = this.modules.get(moduleDefinition.id);
|
|
1687
|
+
if (existing === moduleDefinition) return true;
|
|
1688
|
+
if (existing && !options.replace) {
|
|
1689
|
+
console.warn(`[formie] Module "${moduleDefinition.id}" is already registered. Pass { replace: true } to override the existing definition.`);
|
|
1690
|
+
return false;
|
|
1691
|
+
}
|
|
1692
|
+
this.modules.set(moduleDefinition.id, moduleDefinition);
|
|
1693
|
+
return true;
|
|
1694
|
+
}
|
|
1695
|
+
unregister(moduleId) {
|
|
1696
|
+
this.modules.delete(moduleId);
|
|
1697
|
+
}
|
|
1698
|
+
get(moduleId) {
|
|
1699
|
+
return this.modules.get(moduleId) || null;
|
|
1700
|
+
}
|
|
1701
|
+
getAll() {
|
|
1702
|
+
return Array.from(this.modules.values());
|
|
1703
|
+
}
|
|
1704
|
+
};
|
|
1705
|
+
//#endregion
|
|
1706
|
+
//#region src/js/modules/address/index.ts
|
|
1707
|
+
var builtinAddressModuleLoaders = {
|
|
1708
|
+
"address-finder": () => import("./chunks/address-finder-DfMCiW89.js").then((m) => m.addressFinderModule),
|
|
1709
|
+
"google-address": () => import("./chunks/google-address--uR8WDSm.js").then((m) => m.googleAddressModule),
|
|
1710
|
+
"loqate": () => import("./chunks/loqate-BICNJlVK.js").then((m) => m.loqateModule),
|
|
1711
|
+
"place-kit": () => import("./chunks/place-kit-ldUl-u9w.js").then((m) => m.placeKitModule)
|
|
1712
|
+
};
|
|
1713
|
+
//#endregion
|
|
1714
|
+
//#region src/js/modules/captchas/index.ts
|
|
1715
|
+
var builtinCaptchaModuleLoaders = {
|
|
1716
|
+
"captcha-eu": () => import("./chunks/captcha-eu-DnOWhMwr.js").then((module) => module.captchaEuModule),
|
|
1717
|
+
"friendly-captcha-v1": () => import("./chunks/friendly-captcha-v1-CqO4WVre.js").then((module) => module.friendlyCaptchaV1Module),
|
|
1718
|
+
"friendly-captcha-v2": () => import("./chunks/friendly-captcha-v2-CyykcJcM.js").then((module) => module.friendlyCaptchaV2Module),
|
|
1719
|
+
"hcaptcha": () => import("./chunks/hcaptcha-CmaFUesv.js").then((module) => module.hcaptchaModule),
|
|
1720
|
+
"recaptcha-enterprise": () => import("./chunks/recaptcha-enterprise-DPJNyv1X.js").then((module) => module.recaptchaEnterpriseModule),
|
|
1721
|
+
"recaptcha-v2-checkbox": () => import("./chunks/recaptcha-v2-checkbox-zFjpvJ5c.js").then((module) => module.recaptchaV2CheckboxModule),
|
|
1722
|
+
"recaptcha-v2-invisible": () => import("./chunks/recaptcha-v2-invisible-CnYtkNvz.js").then((module) => module.recaptchaV2InvisibleModule),
|
|
1723
|
+
"recaptcha-v3": () => import("./chunks/recaptcha-v3-EAlWhnkX.js").then((module) => module.recaptchaV3Module),
|
|
1724
|
+
"snaptcha": () => import("./chunks/snaptcha-CCDunGeb.js").then((module) => module.snaptchaModule),
|
|
1725
|
+
"turnstile": () => import("./chunks/turnstile-DP0bdR7T.js").then((module) => module.turnstileModule)
|
|
1726
|
+
};
|
|
1727
|
+
//#endregion
|
|
1728
|
+
//#region src/js/modules/fields/index.ts
|
|
1729
|
+
var builtinFieldModuleLoaders = {
|
|
1730
|
+
"calculations": () => import("./chunks/calculations-CkYAqO_-.js").then((module) => module.calculationsModule),
|
|
1731
|
+
"checkbox-radio": () => import("./chunks/checkbox-radio-0x7Tc0br.js").then((module) => module.checkboxRadioModule),
|
|
1732
|
+
"conditions": () => import("./chunks/conditions-4fXKhEJS.js").then((module) => module.conditionsModule),
|
|
1733
|
+
"date-picker": () => import("./chunks/date-picker-B6iZkjHS.js").then((module) => module.datePickerModule),
|
|
1734
|
+
"file-upload": () => import("./chunks/file-upload-Bh63PQSE.js").then((module) => module.fileUploadModule),
|
|
1735
|
+
"hidden": () => import("./chunks/hidden-CYnZYple.js").then((module) => module.hiddenModule),
|
|
1736
|
+
"phone-country": () => import("./chunks/phone-country-B6Me4lK0.js").then((module) => module.phoneCountryModule),
|
|
1737
|
+
"repeater": () => import("./chunks/repeater-CXD1eLSn.js").then((module) => module.repeaterModule),
|
|
1738
|
+
"rich-text": () => import("./chunks/rich-text-DkmZRhGj.js").then((module) => module.richTextModule),
|
|
1739
|
+
"signature": () => import("./chunks/signature-E9KyYXS1.js").then((module) => module.signatureModule),
|
|
1740
|
+
"summary": () => import("./chunks/summary-EcNE0cvg.js").then((module) => module.summaryModule),
|
|
1741
|
+
"table": () => import("./chunks/table-yxEDL6kA.js").then((module) => module.tableModule),
|
|
1742
|
+
"text-limit": () => import("./chunks/text-limit-D0H_Ca2c.js").then((module) => module.textLimitModule)
|
|
1743
|
+
};
|
|
1744
|
+
//#endregion
|
|
1745
|
+
//#region src/js/modules/payments/index.ts
|
|
1746
|
+
var builtinPaymentModuleLoaders = {
|
|
1747
|
+
"bpoint": () => import("./chunks/bpoint-Ciy3yY9Q.js").then((module) => module.bpointModule),
|
|
1748
|
+
"eway": () => import("./chunks/eway-DEAYcwT0.js").then((module) => module.ewayModule),
|
|
1749
|
+
"go-cardless": () => import("./chunks/go-cardless-CuND59rR.js").then((module) => module.goCardlessModule),
|
|
1750
|
+
"mollie": () => import("./chunks/mollie-DwlsgHZ1.js").then((module) => module.mollieModule),
|
|
1751
|
+
"moneris": () => import("./chunks/moneris-B_IFZFTx.js").then((module) => module.monerisModule),
|
|
1752
|
+
"opayo": () => import("./chunks/opayo-U2x_TOII.js").then((module) => module.opayoModule),
|
|
1753
|
+
"paddle": () => import("./chunks/paddle-BqXFrc79.js").then((module) => module.paddleModule),
|
|
1754
|
+
"paypal": () => import("./chunks/paypal-Cn_DYGDb.js").then((module) => module.paypalModule),
|
|
1755
|
+
"payway": () => import("./chunks/payway-Rnq796eC.js").then((module) => module.paywayModule),
|
|
1756
|
+
"square": () => import("./chunks/square-BLqK51rS.js").then((module) => module.squareModule),
|
|
1757
|
+
"stripe": () => import("./chunks/stripe-B8gHpZNC.js").then((module) => module.stripeModule)
|
|
1758
|
+
};
|
|
1759
|
+
//#endregion
|
|
1760
|
+
//#region src/js/modules/loader.ts
|
|
1761
|
+
var builtinModuleLoaders = {
|
|
1762
|
+
...builtinFieldModuleLoaders,
|
|
1763
|
+
...builtinAddressModuleLoaders,
|
|
1764
|
+
...builtinCaptchaModuleLoaders,
|
|
1765
|
+
...builtinPaymentModuleLoaders
|
|
1766
|
+
};
|
|
1767
|
+
var builtinModuleLoadCache = /* @__PURE__ */ new Map();
|
|
1768
|
+
var debug$2 = createDebug("general", "loader");
|
|
1769
|
+
var importModuleFromSrc = new Function("src", "return import(src);");
|
|
1770
|
+
async function emitModuleLifecycleEvent(emit, moduleId, phase, detail) {
|
|
1771
|
+
await emit(getGlobalModuleLifecycleEventName(phase), detail);
|
|
1772
|
+
await emit(getScopedModuleLifecycleEventName(moduleId, phase), detail);
|
|
1773
|
+
}
|
|
1774
|
+
function isModuleDefinition(definition) {
|
|
1775
|
+
return !!definition && typeof definition === "object" && typeof definition.id === "string" && typeof definition.setup === "function" && typeof definition.match === "function";
|
|
1776
|
+
}
|
|
1777
|
+
async function resolveBuiltinDefinition(moduleId, ctx) {
|
|
1778
|
+
const loader = builtinModuleLoaders[moduleId];
|
|
1779
|
+
if (!loader) return null;
|
|
1780
|
+
if (!builtinModuleLoadCache.has(moduleId)) builtinModuleLoadCache.set(moduleId, (async () => {
|
|
1781
|
+
try {
|
|
1782
|
+
const definition = await loader();
|
|
1783
|
+
if (!isModuleDefinition(definition)) return null;
|
|
1784
|
+
ctx.registry.register(definition);
|
|
1785
|
+
return definition;
|
|
1786
|
+
} catch (error) {
|
|
1787
|
+
console.error("[formie] Failed to load builtin module:", moduleId, error);
|
|
1788
|
+
debug$2.warn("Failed loading builtin module.", {
|
|
1789
|
+
moduleId,
|
|
1790
|
+
error
|
|
1791
|
+
});
|
|
1792
|
+
return null;
|
|
1793
|
+
}
|
|
1794
|
+
})());
|
|
1795
|
+
return builtinModuleLoadCache.get(moduleId) || null;
|
|
1796
|
+
}
|
|
1797
|
+
async function resolveDefinitionFromSrc(src) {
|
|
1798
|
+
try {
|
|
1799
|
+
const imported = await importModuleFromSrc(src);
|
|
1800
|
+
const definition = imported?.default || imported?.formieModule || null;
|
|
1801
|
+
if (!isModuleDefinition(definition)) return null;
|
|
1802
|
+
return definition;
|
|
1803
|
+
} catch (error) {
|
|
1804
|
+
console.error("[formie] Failed to load module from src:", src, error);
|
|
1805
|
+
debug$2.warn("Failed loading module from src.", {
|
|
1806
|
+
src,
|
|
1807
|
+
error
|
|
1808
|
+
});
|
|
1809
|
+
return null;
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
async function resolveDefinition(manifestItem, ctx) {
|
|
1813
|
+
const registered = ctx.registry.get(manifestItem.id);
|
|
1814
|
+
if (registered) return registered;
|
|
1815
|
+
const builtin = await resolveBuiltinDefinition(manifestItem.id, ctx);
|
|
1816
|
+
if (builtin) return builtin;
|
|
1817
|
+
if (manifestItem.src) {
|
|
1818
|
+
const fromSrc = await resolveDefinitionFromSrc(manifestItem.src);
|
|
1819
|
+
if (fromSrc) {
|
|
1820
|
+
ctx.registry.register(fromSrc);
|
|
1821
|
+
return fromSrc;
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
return null;
|
|
1825
|
+
}
|
|
1826
|
+
function escapeSelectorValue(value) {
|
|
1827
|
+
if (typeof window.CSS?.escape === "function") return window.CSS.escape(value);
|
|
1828
|
+
return value.replace(/["\\]/g, "\\$&");
|
|
1829
|
+
}
|
|
1830
|
+
function queryTargets(root, selector) {
|
|
1831
|
+
if (root.matches(selector)) return [root, ...Array.from(root.querySelectorAll(selector))];
|
|
1832
|
+
return Array.from(root.querySelectorAll(selector));
|
|
1833
|
+
}
|
|
1834
|
+
function resolveTarget(target, ctx) {
|
|
1835
|
+
const root = ctx.setupContext.root;
|
|
1836
|
+
const form = ctx.setupContext.form;
|
|
1837
|
+
const scope = target.targetType;
|
|
1838
|
+
const targetId = target.targetId;
|
|
1839
|
+
if (scope === "selector") return queryTargets(root, targetId).map((element) => {
|
|
1840
|
+
return {
|
|
1841
|
+
scope,
|
|
1842
|
+
element
|
|
1843
|
+
};
|
|
1844
|
+
});
|
|
1845
|
+
if (scope === "field") return queryTargets(root, `[data-formie-field-handle="${escapeSelectorValue(targetId)}"]`).map((element) => {
|
|
1846
|
+
return {
|
|
1847
|
+
scope,
|
|
1848
|
+
element
|
|
1849
|
+
};
|
|
1850
|
+
});
|
|
1851
|
+
if (scope === "page") return queryTargets(root, `[data-formie-page-id="${escapeSelectorValue(targetId)}"]`).map((element) => {
|
|
1852
|
+
return {
|
|
1853
|
+
scope,
|
|
1854
|
+
element
|
|
1855
|
+
};
|
|
1856
|
+
});
|
|
1857
|
+
if (scope === "button") return queryTargets(root, `[data-formie-action="${escapeSelectorValue(targetId)}"]`).map((element) => {
|
|
1858
|
+
return {
|
|
1859
|
+
scope,
|
|
1860
|
+
element
|
|
1861
|
+
};
|
|
1862
|
+
});
|
|
1863
|
+
return [{
|
|
1864
|
+
scope: "form",
|
|
1865
|
+
element: form || root
|
|
1866
|
+
}];
|
|
1867
|
+
}
|
|
1868
|
+
function resolveTargets(item, ctx) {
|
|
1869
|
+
return (item.targets && item.targets.length > 0 ? item.targets : [{
|
|
1870
|
+
targetType: "form",
|
|
1871
|
+
targetId: "form"
|
|
1872
|
+
}]).flatMap((target) => {
|
|
1873
|
+
return resolveTarget(target, ctx);
|
|
1874
|
+
});
|
|
1875
|
+
}
|
|
1876
|
+
async function loadModulesFromManifest(manifest, ctx) {
|
|
1877
|
+
const instances = [];
|
|
1878
|
+
debug$2.log("Loading module manifest.", { manifestCount: manifest.length });
|
|
1879
|
+
for (const item of manifest) {
|
|
1880
|
+
const definition = await resolveDefinition(item, ctx);
|
|
1881
|
+
if (!definition) {
|
|
1882
|
+
debug$2.warn("Skipping manifest item (definition not resolved).", {
|
|
1883
|
+
moduleId: item.id,
|
|
1884
|
+
src: item.src
|
|
1885
|
+
});
|
|
1886
|
+
continue;
|
|
1887
|
+
}
|
|
1888
|
+
const targets = resolveTargets(item, ctx);
|
|
1889
|
+
debug$2.log("Resolved module targets.", {
|
|
1890
|
+
moduleId: definition.id,
|
|
1891
|
+
targets: item.targets || [],
|
|
1892
|
+
targetCount: targets.length
|
|
1893
|
+
});
|
|
1894
|
+
if (targets.length === 0 && definition.kind === "address") console.warn(`[formie] Address module "${item.id}" skipped: no target element found for fieldHandle="${item.targets?.find((target) => target.targetType === "field")?.targetId ?? "?"}". Check that the Address field exists in the rendered form.`);
|
|
1895
|
+
for (const target of targets) {
|
|
1896
|
+
const matchContext = {
|
|
1897
|
+
...ctx.matchContext,
|
|
1898
|
+
target: target.element,
|
|
1899
|
+
scope: target.scope,
|
|
1900
|
+
manifestItem: item
|
|
1901
|
+
};
|
|
1902
|
+
if (!definition.match(matchContext)) {
|
|
1903
|
+
if (definition.kind === "address") console.warn(`[formie] Address module "${definition.id}" skipped: target element does not contain [data-formie-address-autocomplete-input]. Enable the Auto-Complete subfield.`);
|
|
1904
|
+
debug$2.log("Module target did not match predicate.", {
|
|
1905
|
+
moduleId: definition.id,
|
|
1906
|
+
scope: target.scope
|
|
1907
|
+
});
|
|
1908
|
+
continue;
|
|
1909
|
+
}
|
|
1910
|
+
const options = item.config || ctx.setupContext.options;
|
|
1911
|
+
const moduleEventName = definition.id;
|
|
1912
|
+
const lifecycleDetail = {
|
|
1913
|
+
moduleId: definition.id,
|
|
1914
|
+
moduleKind: definition.kind,
|
|
1915
|
+
target: target.element,
|
|
1916
|
+
scope: target.scope,
|
|
1917
|
+
options,
|
|
1918
|
+
manifestItem: item
|
|
1919
|
+
};
|
|
1920
|
+
await emitModuleLifecycleEvent(ctx.setupContext.emit, moduleEventName, "before-setup", lifecycleDetail);
|
|
1921
|
+
let instance = null;
|
|
1922
|
+
try {
|
|
1923
|
+
const setupResult = await definition.setup({
|
|
1924
|
+
...ctx.setupContext,
|
|
1925
|
+
target: target.element,
|
|
1926
|
+
scope: target.scope,
|
|
1927
|
+
options
|
|
1928
|
+
});
|
|
1929
|
+
if (setupResult) instance = setupResult;
|
|
1930
|
+
} catch (err) {
|
|
1931
|
+
console.error(`[formie] Module "${definition.id}" setup failed:`, err);
|
|
1932
|
+
debug$2.warn("Module setup failed.", {
|
|
1933
|
+
moduleId: definition.id,
|
|
1934
|
+
scope: target.scope,
|
|
1935
|
+
error: err
|
|
1936
|
+
});
|
|
1937
|
+
}
|
|
1938
|
+
await emitModuleLifecycleEvent(ctx.setupContext.emit, moduleEventName, "after-setup", {
|
|
1939
|
+
...lifecycleDetail,
|
|
1940
|
+
instanceCreated: !!instance
|
|
1941
|
+
});
|
|
1942
|
+
if (instance) {
|
|
1943
|
+
debug$2.log("Module instance created.", {
|
|
1944
|
+
moduleId: definition.id,
|
|
1945
|
+
scope: target.scope
|
|
1946
|
+
});
|
|
1947
|
+
instances.push({
|
|
1948
|
+
...instance,
|
|
1949
|
+
destroy: async () => {
|
|
1950
|
+
debug$2.log("Destroying module instance.", {
|
|
1951
|
+
moduleId: definition.id,
|
|
1952
|
+
scope: target.scope
|
|
1953
|
+
});
|
|
1954
|
+
await emitModuleLifecycleEvent(ctx.setupContext.emit, moduleEventName, "before-destroy", lifecycleDetail);
|
|
1955
|
+
await instance.destroy();
|
|
1956
|
+
await emitModuleLifecycleEvent(ctx.setupContext.emit, moduleEventName, "after-destroy", lifecycleDetail);
|
|
1957
|
+
debug$2.log("Module instance destroyed.", {
|
|
1958
|
+
moduleId: definition.id,
|
|
1959
|
+
scope: target.scope
|
|
1960
|
+
});
|
|
1961
|
+
}
|
|
1962
|
+
});
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
debug$2.log("Module manifest processing complete.", { instanceCount: instances.length });
|
|
1967
|
+
return instances;
|
|
1968
|
+
}
|
|
1969
|
+
//#endregion
|
|
1970
|
+
//#region src/js/utils/unload-warning.ts
|
|
1971
|
+
var DIRTY_TRACKING_IGNORED_FIELD_NAMES = new Set([
|
|
1972
|
+
"CRAFT_CSRF_TOKEN",
|
|
1973
|
+
"action",
|
|
1974
|
+
"redirect",
|
|
1975
|
+
"requestToken",
|
|
1976
|
+
"renderId",
|
|
1977
|
+
"submitAction",
|
|
1978
|
+
"pageId",
|
|
1979
|
+
"draftContextToken",
|
|
1980
|
+
"draftContext",
|
|
1981
|
+
"continuationToken"
|
|
1982
|
+
]);
|
|
1983
|
+
function serializeStableValue(value, seen) {
|
|
1984
|
+
if (value == null) return String(value);
|
|
1985
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
1986
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
1987
|
+
if (typeof value === "function") return "[function]";
|
|
1988
|
+
if (typeof File !== "undefined" && value instanceof File) return `[file:${value.name}:${value.size}:${value.type}]`;
|
|
1989
|
+
if (typeof Blob !== "undefined" && value instanceof Blob) return `[blob:${value.size}:${value.type}]`;
|
|
1990
|
+
if (Array.isArray(value)) return `[${value.map((item) => serializeStableValue(item, seen)).join(",")}]`;
|
|
1991
|
+
if (typeof value === "object") {
|
|
1992
|
+
if (seen.has(value)) return "[circular]";
|
|
1993
|
+
seen.add(value);
|
|
1994
|
+
const entries = Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => {
|
|
1995
|
+
return `${JSON.stringify(key)}:${serializeStableValue(item, seen)}`;
|
|
1996
|
+
});
|
|
1997
|
+
seen.delete(value);
|
|
1998
|
+
return `{${entries.join(",")}}`;
|
|
1999
|
+
}
|
|
2000
|
+
return JSON.stringify(String(value));
|
|
2001
|
+
}
|
|
2002
|
+
function stableSerialize(value) {
|
|
2003
|
+
return serializeStableValue(value, /* @__PURE__ */ new WeakSet());
|
|
2004
|
+
}
|
|
2005
|
+
function shouldTrackFieldName(name) {
|
|
2006
|
+
if (!name) return false;
|
|
2007
|
+
const normalizedName = name.endsWith("[]") ? name.slice(0, -2) : name;
|
|
2008
|
+
return !DIRTY_TRACKING_IGNORED_FIELD_NAMES.has(normalizedName);
|
|
2009
|
+
}
|
|
2010
|
+
function buildTrackedSnapshot(form) {
|
|
2011
|
+
return stableSerialize(Array.from(new FormData(form).entries()).filter(([name]) => {
|
|
2012
|
+
return shouldTrackFieldName(String(name || ""));
|
|
2013
|
+
}));
|
|
2014
|
+
}
|
|
2015
|
+
function createFormUnloadWarningGuard(form, options = {}) {
|
|
2016
|
+
let baselineSnapshot = null;
|
|
2017
|
+
let isReady = false;
|
|
2018
|
+
let isDirty = false;
|
|
2019
|
+
let animationFrameId = null;
|
|
2020
|
+
let dirtyTimerId = null;
|
|
2021
|
+
let baselineTimerId = null;
|
|
2022
|
+
const clearScheduledWork = () => {
|
|
2023
|
+
if (animationFrameId !== null) {
|
|
2024
|
+
window.cancelAnimationFrame(animationFrameId);
|
|
2025
|
+
animationFrameId = null;
|
|
2026
|
+
}
|
|
2027
|
+
if (dirtyTimerId !== null) {
|
|
2028
|
+
window.clearTimeout(dirtyTimerId);
|
|
2029
|
+
dirtyTimerId = null;
|
|
2030
|
+
}
|
|
2031
|
+
if (baselineTimerId !== null) {
|
|
2032
|
+
window.clearTimeout(baselineTimerId);
|
|
2033
|
+
baselineTimerId = null;
|
|
2034
|
+
}
|
|
2035
|
+
};
|
|
2036
|
+
const refreshDirtyState = () => {
|
|
2037
|
+
if (!isReady) return false;
|
|
2038
|
+
isDirty = buildTrackedSnapshot(form) !== baselineSnapshot;
|
|
2039
|
+
return isDirty;
|
|
2040
|
+
};
|
|
2041
|
+
const captureBaseline = () => {
|
|
2042
|
+
baselineSnapshot = buildTrackedSnapshot(form);
|
|
2043
|
+
isReady = true;
|
|
2044
|
+
isDirty = false;
|
|
2045
|
+
};
|
|
2046
|
+
const scheduleBaselineCapture = () => {
|
|
2047
|
+
clearScheduledWork();
|
|
2048
|
+
isReady = false;
|
|
2049
|
+
animationFrameId = window.requestAnimationFrame(() => {
|
|
2050
|
+
animationFrameId = null;
|
|
2051
|
+
baselineTimerId = window.setTimeout(() => {
|
|
2052
|
+
baselineTimerId = null;
|
|
2053
|
+
captureBaseline();
|
|
2054
|
+
}, 0);
|
|
2055
|
+
});
|
|
2056
|
+
};
|
|
2057
|
+
const scheduleDirtyRefresh = () => {
|
|
2058
|
+
if (dirtyTimerId !== null) window.clearTimeout(dirtyTimerId);
|
|
2059
|
+
dirtyTimerId = window.setTimeout(() => {
|
|
2060
|
+
dirtyTimerId = null;
|
|
2061
|
+
refreshDirtyState();
|
|
2062
|
+
}, 120);
|
|
2063
|
+
};
|
|
2064
|
+
const handleBeforeUnload = (event) => {
|
|
2065
|
+
if (options.shouldWarn && !options.shouldWarn()) return;
|
|
2066
|
+
if (!refreshDirtyState()) return;
|
|
2067
|
+
event.preventDefault();
|
|
2068
|
+
event.returnValue = "";
|
|
2069
|
+
};
|
|
2070
|
+
form.addEventListener("input", scheduleDirtyRefresh);
|
|
2071
|
+
form.addEventListener("change", scheduleDirtyRefresh);
|
|
2072
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
2073
|
+
scheduleBaselineCapture();
|
|
2074
|
+
return {
|
|
2075
|
+
captureBaseline,
|
|
2076
|
+
scheduleBaselineCapture,
|
|
2077
|
+
refreshDirtyState,
|
|
2078
|
+
destroy: () => {
|
|
2079
|
+
clearScheduledWork();
|
|
2080
|
+
form.removeEventListener("input", scheduleDirtyRefresh);
|
|
2081
|
+
form.removeEventListener("change", scheduleDirtyRefresh);
|
|
2082
|
+
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
2083
|
+
}
|
|
2084
|
+
};
|
|
2085
|
+
}
|
|
2086
|
+
//#endregion
|
|
2087
|
+
//#region src/js/core/create-formie-client.ts
|
|
2088
|
+
var ROOT_SELECTORS = "[data-formie]:not([data-formie-init=\"false\"]), [data-formie-form]:not([data-formie-init=\"false\"])";
|
|
2089
|
+
var DEFAULT_SUBMIT_DELAY_MS = 300;
|
|
2090
|
+
var DEFAULT_HEADLESS_RENDER_ACTION = "/actions/formie/server/forms/render";
|
|
2091
|
+
var DEFAULT_HEADLESS_GRAPHQL_ENDPOINT = "/api";
|
|
2092
|
+
var DEFAULT_HEADLESS_REFRESH_TOKENS_ACTION = "/actions/formie/server/forms/refresh-tokens";
|
|
2093
|
+
var DEFAULT_HEADLESS_SUBMIT_ACTION = "/actions/formie/server/submissions/submit";
|
|
2094
|
+
var DEFAULT_HEADLESS_SET_PAGE_ACTION = "/actions/formie/server/submissions/set-page";
|
|
2095
|
+
var DEFAULT_HEADLESS_CLEAR_SUBMISSION_ACTION = "/actions/formie/server/submissions/clear-submission";
|
|
2096
|
+
var DEFAULT_FILE_UPLOAD_HYDRATE_ACTION = "/actions/formie/file-upload/hydrate";
|
|
2097
|
+
var debug$1 = createDebug("general", "client");
|
|
2098
|
+
var compatibilityWarnings = /* @__PURE__ */ new Set();
|
|
2099
|
+
function parseBooleanOption(value, defaultValue) {
|
|
2100
|
+
if (value == null || value === "") return defaultValue;
|
|
2101
|
+
const normalized = value.toLowerCase();
|
|
2102
|
+
return !(normalized === "false" || normalized === "0" || normalized === "off");
|
|
2103
|
+
}
|
|
2104
|
+
function inferStaticCacheOnLoadFromDataset(dataset) {
|
|
2105
|
+
if (dataset.formieRefreshTokens != null && dataset.formieRefreshTokens !== "") return parseBooleanOption(dataset.formieRefreshTokens, false);
|
|
2106
|
+
if (dataset.formieStaticCache != null && dataset.formieStaticCache !== "") return parseBooleanOption(dataset.formieStaticCache, false);
|
|
2107
|
+
return false;
|
|
2108
|
+
}
|
|
2109
|
+
function inferOptionsFromElement(target) {
|
|
2110
|
+
const dataset = target instanceof HTMLElement ? target.dataset : {};
|
|
2111
|
+
return {
|
|
2112
|
+
mode: "server-rendered",
|
|
2113
|
+
transport: dataset.formieTransport || "rest",
|
|
2114
|
+
formHandle: dataset.formieHandle,
|
|
2115
|
+
endpoint: dataset.formieEndpoint,
|
|
2116
|
+
staticCache: inferStaticCacheOnLoadFromDataset(dataset),
|
|
2117
|
+
autoVisible: parseBooleanOption(dataset.formieAutoVisible, true),
|
|
2118
|
+
compatibility: parseBooleanOption(dataset.formieCompatibility, false)
|
|
2119
|
+
};
|
|
2120
|
+
}
|
|
2121
|
+
function normalizeMode(mode) {
|
|
2122
|
+
return mode || "server-rendered";
|
|
2123
|
+
}
|
|
2124
|
+
function normalizeTransport(transport) {
|
|
2125
|
+
return transport || "rest";
|
|
2126
|
+
}
|
|
2127
|
+
function getFormFromTarget(target) {
|
|
2128
|
+
if (target instanceof HTMLFormElement) return target;
|
|
2129
|
+
return target.querySelector("form");
|
|
2130
|
+
}
|
|
2131
|
+
function warnCompatibilityOnce(key, message) {
|
|
2132
|
+
if (compatibilityWarnings.has(key)) return;
|
|
2133
|
+
compatibilityWarnings.add(key);
|
|
2134
|
+
debug$1.warn(message);
|
|
2135
|
+
}
|
|
2136
|
+
function resolveEndpointAgainstBase(endpoint, baseEndpoint) {
|
|
2137
|
+
if (!endpoint) return endpoint;
|
|
2138
|
+
try {
|
|
2139
|
+
return new URL(endpoint).toString();
|
|
2140
|
+
} catch (_error) {}
|
|
2141
|
+
if (!baseEndpoint) return endpoint;
|
|
2142
|
+
try {
|
|
2143
|
+
return new URL(endpoint, baseEndpoint).toString();
|
|
2144
|
+
} catch (_error) {
|
|
2145
|
+
return endpoint;
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
function resolveHeadlessEndpoint(baseOrEndpoint, actionPath) {
|
|
2149
|
+
const candidate = (baseOrEndpoint || "").trim();
|
|
2150
|
+
if (!candidate) return actionPath;
|
|
2151
|
+
if (candidate.includes("/actions/")) return candidate;
|
|
2152
|
+
return resolveEndpointAgainstBase(actionPath, candidate);
|
|
2153
|
+
}
|
|
2154
|
+
function resolveHtmlRenderEndpoint(options, target) {
|
|
2155
|
+
return resolveHeadlessEndpoint(options.endpoint || target.dataset.formieEndpoint, DEFAULT_HEADLESS_RENDER_ACTION);
|
|
2156
|
+
}
|
|
2157
|
+
function resolveGraphqlEndpoint(options, target) {
|
|
2158
|
+
const candidate = (options.endpoint || target.dataset.formieEndpoint || "").trim();
|
|
2159
|
+
if (!candidate) return DEFAULT_HEADLESS_GRAPHQL_ENDPOINT;
|
|
2160
|
+
if (candidate.includes("/graphql") || candidate.endsWith("/api") || candidate.includes("/actions/graphql/")) return candidate;
|
|
2161
|
+
return resolveEndpointAgainstBase(DEFAULT_HEADLESS_GRAPHQL_ENDPOINT, candidate);
|
|
2162
|
+
}
|
|
2163
|
+
function resolveRefreshTokensEndpoint(options, target) {
|
|
2164
|
+
return resolveHeadlessEndpoint(target.dataset.formieRefreshTokensEndpoint || options.endpoint || target.dataset.formieEndpoint, DEFAULT_HEADLESS_REFRESH_TOKENS_ACTION);
|
|
2165
|
+
}
|
|
2166
|
+
function mergeSearchParams(sourceUrl, destinationUrl) {
|
|
2167
|
+
if (!sourceUrl) return destinationUrl;
|
|
2168
|
+
try {
|
|
2169
|
+
const source = new URL(sourceUrl, window.location.origin);
|
|
2170
|
+
const destination = new URL(destinationUrl, window.location.origin);
|
|
2171
|
+
source.searchParams.forEach((value, key) => {
|
|
2172
|
+
if (!destination.searchParams.has(key)) destination.searchParams.set(key, value);
|
|
2173
|
+
});
|
|
2174
|
+
return destination.toString();
|
|
2175
|
+
} catch (_error) {
|
|
2176
|
+
return destinationUrl;
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
function normalizeHeadlessManagedUrls(target, form, options) {
|
|
2180
|
+
const baseEndpoint = options.endpoint || target.dataset.formieEndpoint;
|
|
2181
|
+
const submitAction = resolveHeadlessEndpoint(baseEndpoint, DEFAULT_HEADLESS_SUBMIT_ACTION);
|
|
2182
|
+
const existingAction = form.getAttribute("action");
|
|
2183
|
+
form.setAttribute("action", mergeSearchParams(existingAction, submitAction));
|
|
2184
|
+
form.querySelectorAll("[data-formie-tab-link]").forEach((link) => {
|
|
2185
|
+
const existingHref = link.getAttribute("href");
|
|
2186
|
+
const setPageEndpoint = resolveHeadlessEndpoint(baseEndpoint, DEFAULT_HEADLESS_SET_PAGE_ACTION);
|
|
2187
|
+
link.setAttribute("href", mergeSearchParams(existingHref, setPageEndpoint));
|
|
2188
|
+
});
|
|
2189
|
+
form.querySelectorAll("[data-formie-file-upload-hydrate-endpoint]").forEach((input) => {
|
|
2190
|
+
input.setAttribute("data-formie-file-upload-hydrate-endpoint", resolveHeadlessEndpoint(baseEndpoint, DEFAULT_FILE_UPLOAD_HYDRATE_ACTION));
|
|
2191
|
+
});
|
|
2192
|
+
}
|
|
2193
|
+
function ensureSupportedHeadlessTransport(transport, mode) {
|
|
2194
|
+
if (transport === "graphql" && mode !== "server-rendered") throw new Error(`Formie ${mode} mode does not support GraphQL transport yet.`);
|
|
2195
|
+
}
|
|
2196
|
+
function parseBooleanDatasetValue(value) {
|
|
2197
|
+
if (value == null) return false;
|
|
2198
|
+
const normalized = value.trim().toLowerCase();
|
|
2199
|
+
return normalized === "true" || normalized === "1" || normalized === "";
|
|
2200
|
+
}
|
|
2201
|
+
function hasAutomaticSubmissionState(form) {
|
|
2202
|
+
return parseBooleanOption(form.dataset.formieAutomaticSubmissionState, true);
|
|
2203
|
+
}
|
|
2204
|
+
function resolveClearSubmissionEndpoint(options, target, form) {
|
|
2205
|
+
return resolveHeadlessEndpoint(form.dataset.formieClearSubmissionEndpoint || options.endpoint || target.dataset.formieEndpoint, DEFAULT_HEADLESS_CLEAR_SUBMISSION_ACTION);
|
|
2206
|
+
}
|
|
2207
|
+
function shouldEnableUnloadWarning(form) {
|
|
2208
|
+
return parseBooleanDatasetValue(form.dataset.formieUnloadWarning);
|
|
2209
|
+
}
|
|
2210
|
+
function markInternalNavigation(form, reason) {
|
|
2211
|
+
form.setAttribute("data-formie-internal-navigation", reason);
|
|
2212
|
+
}
|
|
2213
|
+
function clearInternalNavigation(form) {
|
|
2214
|
+
form.removeAttribute("data-formie-internal-navigation");
|
|
2215
|
+
}
|
|
2216
|
+
function hasInternalNavigation(form) {
|
|
2217
|
+
return form.getAttribute("data-formie-internal-navigation") !== null;
|
|
2218
|
+
}
|
|
2219
|
+
function urlHasSearchParam(sourceUrl, param) {
|
|
2220
|
+
if (!sourceUrl) return false;
|
|
2221
|
+
try {
|
|
2222
|
+
return new URL(sourceUrl, window.location.origin).searchParams.has(param);
|
|
2223
|
+
} catch (_error) {
|
|
2224
|
+
return false;
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
function formHasResumeTokenState(form) {
|
|
2228
|
+
return urlHasSearchParam(window.location.href, "resumeToken") || urlHasSearchParam(form.getAttribute("action"), "resumeToken");
|
|
2229
|
+
}
|
|
2230
|
+
function isSameTabClickEvent(event) {
|
|
2231
|
+
if (!(event instanceof MouseEvent)) return true;
|
|
2232
|
+
return event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey;
|
|
2233
|
+
}
|
|
2234
|
+
function parseIntegerDatasetValue(value, fallback = 0) {
|
|
2235
|
+
if (!value) return fallback;
|
|
2236
|
+
const parsed = Number.parseInt(value, 10);
|
|
2237
|
+
if (!Number.isFinite(parsed)) return fallback;
|
|
2238
|
+
return parsed;
|
|
2239
|
+
}
|
|
2240
|
+
function getSubmitDelayMs(form) {
|
|
2241
|
+
return Math.max(0, parseIntegerDatasetValue(form.dataset.formieSubmitDelay, DEFAULT_SUBMIT_DELAY_MS));
|
|
2242
|
+
}
|
|
2243
|
+
function shouldValidateOnSubmit(form) {
|
|
2244
|
+
return parseBooleanDatasetValue(form.dataset.formieValidationOnSubmit);
|
|
2245
|
+
}
|
|
2246
|
+
async function waitForSubmitDelay(form) {
|
|
2247
|
+
const delay = getSubmitDelayMs(form);
|
|
2248
|
+
if (delay < 1) return;
|
|
2249
|
+
await new Promise((resolve) => {
|
|
2250
|
+
window.setTimeout(resolve, delay);
|
|
2251
|
+
});
|
|
2252
|
+
}
|
|
2253
|
+
function parseJsonAttribute(element, attributeName) {
|
|
2254
|
+
const rawValue = element?.getAttribute(attributeName)?.trim();
|
|
2255
|
+
if (!rawValue) return null;
|
|
2256
|
+
try {
|
|
2257
|
+
return JSON.parse(rawValue);
|
|
2258
|
+
} catch (error) {
|
|
2259
|
+
console.error(`[formie] Failed to parse ${attributeName}.`, error);
|
|
2260
|
+
return null;
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
function getEmbeddedPayload(target, form) {
|
|
2264
|
+
const payloadRoot = form || (target instanceof HTMLFormElement ? target : null);
|
|
2265
|
+
if (!payloadRoot) return null;
|
|
2266
|
+
const modules = parseJsonAttribute(payloadRoot, "data-formie-modules");
|
|
2267
|
+
const theme = parseJsonAttribute(payloadRoot, "data-formie-theme");
|
|
2268
|
+
if (!modules && !theme) return null;
|
|
2269
|
+
return {
|
|
2270
|
+
modules: modules || void 0,
|
|
2271
|
+
theme: theme || void 0
|
|
2272
|
+
};
|
|
2273
|
+
}
|
|
2274
|
+
function isElementVisible(target) {
|
|
2275
|
+
if (!(target instanceof HTMLElement)) return true;
|
|
2276
|
+
if (!target.isConnected) return false;
|
|
2277
|
+
if (target.hidden || target.closest("[hidden]")) return false;
|
|
2278
|
+
const style = window.getComputedStyle(target);
|
|
2279
|
+
if (style.display === "none" || style.visibility === "hidden") return false;
|
|
2280
|
+
return target.getClientRects().length > 0;
|
|
2281
|
+
}
|
|
2282
|
+
function isWithinScope(target, scope) {
|
|
2283
|
+
if (scope === document) return true;
|
|
2284
|
+
if (scope instanceof Element) return scope === target || scope.contains(target);
|
|
2285
|
+
return true;
|
|
2286
|
+
}
|
|
2287
|
+
function getTargetDebugLabel(target) {
|
|
2288
|
+
const element = target;
|
|
2289
|
+
const id = element.id ? `#${element.id}` : "";
|
|
2290
|
+
const handle = element.dataset?.formieHandle ? `[handle="${element.dataset.formieHandle}"]` : "";
|
|
2291
|
+
return `${element.tagName ? element.tagName.toLowerCase() : "element"}${id}${handle}`;
|
|
2292
|
+
}
|
|
2293
|
+
function applyRefreshTokensToForm(form, refreshTokens) {
|
|
2294
|
+
if (!refreshTokens) return;
|
|
2295
|
+
if (refreshTokens.csrf?.param && refreshTokens.csrf?.token) {
|
|
2296
|
+
const csrfInput = form.querySelector(`input[name="${refreshTokens.csrf.param}"]`);
|
|
2297
|
+
if (csrfInput) csrfInput.value = refreshTokens.csrf.token;
|
|
2298
|
+
}
|
|
2299
|
+
if (refreshTokens.requestToken) {
|
|
2300
|
+
const requestTokenInput = form.querySelector("input[name=\"requestToken\"]");
|
|
2301
|
+
if (requestTokenInput) requestTokenInput.value = refreshTokens.requestToken;
|
|
2302
|
+
}
|
|
2303
|
+
if (refreshTokens.renderId) {
|
|
2304
|
+
const renderIdInput = form.querySelector("input[name=\"renderId\"]");
|
|
2305
|
+
if (renderIdInput) renderIdInput.value = refreshTokens.renderId;
|
|
2306
|
+
}
|
|
2307
|
+
if (refreshTokens.captchas && typeof refreshTokens.captchas === "object") Object.values(refreshTokens.captchas).forEach((captchaEntry) => {
|
|
2308
|
+
if (!captchaEntry || typeof captchaEntry !== "object") return;
|
|
2309
|
+
const entry = captchaEntry;
|
|
2310
|
+
if (!entry.sessionKey) return;
|
|
2311
|
+
const captchaInput = form.querySelector(`input[name="${entry.sessionKey}"]`);
|
|
2312
|
+
if (captchaInput && typeof entry.value === "string") captchaInput.value = entry.value;
|
|
2313
|
+
});
|
|
2314
|
+
}
|
|
2315
|
+
async function ensureHtmlRender(target, options) {
|
|
2316
|
+
const mode = normalizeMode(options.mode);
|
|
2317
|
+
const transport = normalizeTransport(options.transport);
|
|
2318
|
+
if (mode !== "server-rendered") return null;
|
|
2319
|
+
if (options.payload) {
|
|
2320
|
+
if (options.payload.html) target.innerHTML = options.payload.html;
|
|
2321
|
+
return options.payload;
|
|
2322
|
+
}
|
|
2323
|
+
ensureSupportedHeadlessTransport(transport, mode);
|
|
2324
|
+
const hasForm = !!getFormFromTarget(target);
|
|
2325
|
+
const formHandle = options.formHandle || target.dataset.formieHandle;
|
|
2326
|
+
if (hasForm || !formHandle) return null;
|
|
2327
|
+
const renderOptions = {
|
|
2328
|
+
mode,
|
|
2329
|
+
endpoint: options.endpoint,
|
|
2330
|
+
locale: options.locale,
|
|
2331
|
+
siteId: options.siteId,
|
|
2332
|
+
theme: options.theme,
|
|
2333
|
+
themeConfig: options.themeConfig
|
|
2334
|
+
};
|
|
2335
|
+
const endpoint = transport === "graphql" ? resolveGraphqlEndpoint(options, target) : resolveHtmlRenderEndpoint(options, target);
|
|
2336
|
+
const payload = transport === "graphql" ? await requestGraphqlRender(endpoint, formHandle, renderOptions) : await requestRender(endpoint, formHandle, {
|
|
2337
|
+
...renderOptions,
|
|
2338
|
+
endpoint
|
|
2339
|
+
});
|
|
2340
|
+
if (payload?.html) target.innerHTML = payload.html;
|
|
2341
|
+
return payload;
|
|
2342
|
+
}
|
|
2343
|
+
async function refreshTokensAfterSubmitIfNeeded(target, options, form) {
|
|
2344
|
+
if (options.refreshTokens === false) return;
|
|
2345
|
+
ensureSupportedHeadlessTransport(normalizeTransport(options.transport), normalizeMode(options.mode));
|
|
2346
|
+
const formHandle = options.formHandle || target.dataset.formieHandle;
|
|
2347
|
+
if (!formHandle) return;
|
|
2348
|
+
const refreshTokens = await requestRefreshTokens(resolveRefreshTokensEndpoint(options, target), formHandle, form.querySelector("input[name=\"renderId\"]")?.value || void 0);
|
|
2349
|
+
applyRefreshTokensToForm(form, refreshTokens);
|
|
2350
|
+
dispatchFormieDomEvent(target, "formie:refresh-tokens:refreshed", refreshTokens);
|
|
2351
|
+
}
|
|
2352
|
+
function bindFormEvents(target, form, options, bus, validator, unbinds) {
|
|
2353
|
+
const submitMethod = String(form.dataset.formieSubmitMethod || "").trim().toLowerCase();
|
|
2354
|
+
const clearSubmissionEndpoint = resolveClearSubmissionEndpoint(options, target, form);
|
|
2355
|
+
let allowNativeSubmit = false;
|
|
2356
|
+
const submitButtons = form.querySelectorAll("[data-formie-action]");
|
|
2357
|
+
const setPendingAction = (action) => {
|
|
2358
|
+
if (action) {
|
|
2359
|
+
form.setAttribute("data-formie-pending-action", action);
|
|
2360
|
+
return;
|
|
2361
|
+
}
|
|
2362
|
+
form.removeAttribute("data-formie-pending-action");
|
|
2363
|
+
};
|
|
2364
|
+
if (shouldEnableUnloadWarning(form)) {
|
|
2365
|
+
const unloadWarning = createFormUnloadWarningGuard(form, { shouldWarn: () => {
|
|
2366
|
+
return !hasInternalNavigation(form);
|
|
2367
|
+
} });
|
|
2368
|
+
const handleSubmitResult = (event) => {
|
|
2369
|
+
if (!(event instanceof CustomEvent)) return;
|
|
2370
|
+
const result = event.detail;
|
|
2371
|
+
if (!result?.ok) return;
|
|
2372
|
+
if (result.action === "save") unloadWarning.scheduleBaselineCapture();
|
|
2373
|
+
};
|
|
2374
|
+
const handleStateReset = () => {
|
|
2375
|
+
unloadWarning.scheduleBaselineCapture();
|
|
2376
|
+
};
|
|
2377
|
+
target.addEventListener("formie:submit:result", handleSubmitResult);
|
|
2378
|
+
form.addEventListener("formie:state:reset", handleStateReset);
|
|
2379
|
+
unbinds.push(() => {
|
|
2380
|
+
target.removeEventListener("formie:submit:result", handleSubmitResult);
|
|
2381
|
+
form.removeEventListener("formie:state:reset", handleStateReset);
|
|
2382
|
+
unloadWarning.destroy();
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2385
|
+
submitButtons.forEach((button) => {
|
|
2386
|
+
const handler = (event) => {
|
|
2387
|
+
const action = event.currentTarget.getAttribute("data-formie-action");
|
|
2388
|
+
const submitAction = form.querySelector("input[name=\"submitAction\"]");
|
|
2389
|
+
setPendingAction(action);
|
|
2390
|
+
if (action && submitAction) submitAction.value = action;
|
|
2391
|
+
};
|
|
2392
|
+
button.addEventListener("click", handler);
|
|
2393
|
+
unbinds.push(() => {
|
|
2394
|
+
button.removeEventListener("click", handler);
|
|
2395
|
+
});
|
|
2396
|
+
});
|
|
2397
|
+
form.querySelectorAll("[data-formie-tab-link]").forEach((link) => {
|
|
2398
|
+
const handler = async (event) => {
|
|
2399
|
+
if (submitMethod !== "ajax") {
|
|
2400
|
+
if (isSameTabClickEvent(event)) markInternalNavigation(form, "set-page");
|
|
2401
|
+
return;
|
|
2402
|
+
}
|
|
2403
|
+
event.preventDefault();
|
|
2404
|
+
const currentTarget = event.currentTarget;
|
|
2405
|
+
const nextPageId = currentTarget?.getAttribute("data-formie-page-id");
|
|
2406
|
+
const href = currentTarget?.getAttribute("href");
|
|
2407
|
+
if (!nextPageId || !href) return;
|
|
2408
|
+
applyPageState(form, nextPageId);
|
|
2409
|
+
dispatchFormieDomEvent(target, "formie:page:navigate", {
|
|
2410
|
+
pageId: nextPageId,
|
|
2411
|
+
href
|
|
2412
|
+
});
|
|
2413
|
+
try {
|
|
2414
|
+
dispatchFormieDomEvent(target, "formie:page:navigate:after", {
|
|
2415
|
+
pageId: nextPageId,
|
|
2416
|
+
href,
|
|
2417
|
+
response: await requestSetPage(href, form, nextPageId)
|
|
2418
|
+
});
|
|
2419
|
+
} catch (error) {
|
|
2420
|
+
console.error("[formie] Failed to persist page navigation state.", error);
|
|
2421
|
+
dispatchFormieDomEvent(target, "formie:page:navigate:error", {
|
|
2422
|
+
pageId: nextPageId,
|
|
2423
|
+
href,
|
|
2424
|
+
error
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
2427
|
+
};
|
|
2428
|
+
link.addEventListener("click", handler);
|
|
2429
|
+
unbinds.push(() => {
|
|
2430
|
+
link.removeEventListener("click", handler);
|
|
2431
|
+
});
|
|
2432
|
+
});
|
|
2433
|
+
if (!hasAutomaticSubmissionState(form)) {
|
|
2434
|
+
let requestedClearOnLeave = false;
|
|
2435
|
+
const leaveHandler = () => {
|
|
2436
|
+
if (requestedClearOnLeave || hasInternalNavigation(form) || formHasResumeTokenState(form)) return;
|
|
2437
|
+
requestedClearOnLeave = true;
|
|
2438
|
+
clearSubmissionOnUnload(clearSubmissionEndpoint, form);
|
|
2439
|
+
};
|
|
2440
|
+
window.addEventListener("pagehide", leaveHandler);
|
|
2441
|
+
window.addEventListener("beforeunload", leaveHandler);
|
|
2442
|
+
unbinds.push(() => {
|
|
2443
|
+
window.removeEventListener("pagehide", leaveHandler);
|
|
2444
|
+
window.removeEventListener("beforeunload", leaveHandler);
|
|
2445
|
+
});
|
|
2446
|
+
}
|
|
2447
|
+
const submitHandler = async (event) => {
|
|
2448
|
+
if (allowNativeSubmit) return;
|
|
2449
|
+
const isAjaxSubmit = submitMethod === "ajax";
|
|
2450
|
+
if (!isAjaxSubmit) event.preventDefault();
|
|
2451
|
+
else event.preventDefault();
|
|
2452
|
+
if (form.getAttribute("data-formie-loading") === "true") {
|
|
2453
|
+
if (!(form.getAttribute("data-formie-internal-resubmit") === "true")) return;
|
|
2454
|
+
form.removeAttribute("data-formie-internal-resubmit");
|
|
2455
|
+
} else form.removeAttribute("data-formie-internal-resubmit");
|
|
2456
|
+
const submitter = event.submitter;
|
|
2457
|
+
const actionFromSubmitter = submitter?.getAttribute("data-formie-action");
|
|
2458
|
+
const pendingAction = form.getAttribute("data-formie-pending-action");
|
|
2459
|
+
const submitAction = form.querySelector("input[name=\"submitAction\"]");
|
|
2460
|
+
const action = actionFromSubmitter || pendingAction || submitAction?.value || "submit";
|
|
2461
|
+
let result = null;
|
|
2462
|
+
let nativeSubmitStarted = false;
|
|
2463
|
+
try {
|
|
2464
|
+
if (isAjaxSubmit) result = await executeAjaxSubmitFlow({
|
|
2465
|
+
target,
|
|
2466
|
+
form,
|
|
2467
|
+
bus,
|
|
2468
|
+
validator,
|
|
2469
|
+
validateOnSubmit: shouldValidateOnSubmit(form),
|
|
2470
|
+
action,
|
|
2471
|
+
submitter,
|
|
2472
|
+
waitForSubmitDelay,
|
|
2473
|
+
onRefreshTokensAfterSubmit: async () => {
|
|
2474
|
+
await refreshTokensAfterSubmitIfNeeded(target, options, form);
|
|
2475
|
+
},
|
|
2476
|
+
dispatchSubmitResult: (submitResult) => {
|
|
2477
|
+
dispatchFormieDomEvent(target, "formie:submit:result", submitResult);
|
|
2478
|
+
}
|
|
2479
|
+
});
|
|
2480
|
+
else {
|
|
2481
|
+
clearSubmitFeedback(form);
|
|
2482
|
+
setSubmitLoading(form, submitter);
|
|
2483
|
+
await waitForSubmitDelay(form);
|
|
2484
|
+
result = await runSubmitPipeline(form, action, bus, {
|
|
2485
|
+
validator,
|
|
2486
|
+
validateOnSubmit: shouldValidateOnSubmit(form),
|
|
2487
|
+
preflightOnly: true
|
|
2488
|
+
});
|
|
2489
|
+
if (result.ok) {
|
|
2490
|
+
dispatchPageClientEventForSubmit(form, action);
|
|
2491
|
+
allowNativeSubmit = true;
|
|
2492
|
+
markInternalNavigation(form, "submit");
|
|
2493
|
+
setPendingAction(null);
|
|
2494
|
+
let nativeValidationFailed = false;
|
|
2495
|
+
const nativeInvalidHandler = () => {
|
|
2496
|
+
nativeValidationFailed = true;
|
|
2497
|
+
allowNativeSubmit = false;
|
|
2498
|
+
clearInternalNavigation(form);
|
|
2499
|
+
clearSubmitLoading(form);
|
|
2500
|
+
};
|
|
2501
|
+
if (typeof form.requestSubmit === "function") {
|
|
2502
|
+
form.addEventListener("invalid", nativeInvalidHandler, true);
|
|
2503
|
+
try {
|
|
2504
|
+
form.requestSubmit();
|
|
2505
|
+
} finally {
|
|
2506
|
+
form.removeEventListener("invalid", nativeInvalidHandler, true);
|
|
2507
|
+
}
|
|
2508
|
+
} else form.submit();
|
|
2509
|
+
if (nativeValidationFailed) return;
|
|
2510
|
+
nativeSubmitStarted = true;
|
|
2511
|
+
return;
|
|
2512
|
+
}
|
|
2513
|
+
applySubmitResultUi(form, result);
|
|
2514
|
+
dispatchFormieDomEvent(target, "formie:submit:result", result);
|
|
2515
|
+
clearInternalNavigation(form);
|
|
2516
|
+
}
|
|
2517
|
+
} catch (error) {
|
|
2518
|
+
allowNativeSubmit = false;
|
|
2519
|
+
result = {
|
|
2520
|
+
ok: false,
|
|
2521
|
+
code: "SUBMIT_ERROR",
|
|
2522
|
+
message: error instanceof Error ? error.message : "Submission failed.",
|
|
2523
|
+
formErrors: [error instanceof Error ? error.message : "Submission failed."]
|
|
2524
|
+
};
|
|
2525
|
+
applySubmitResultUi(form, result);
|
|
2526
|
+
dispatchFormieDomEvent(target, "formie:submit:result", result);
|
|
2527
|
+
clearInternalNavigation(form);
|
|
2528
|
+
} finally {
|
|
2529
|
+
setPendingAction(null);
|
|
2530
|
+
if (!isAjaxSubmit && !nativeSubmitStarted && !shouldKeepSubmitLoading(result)) clearSubmitLoading(form);
|
|
2531
|
+
}
|
|
2532
|
+
};
|
|
2533
|
+
form.addEventListener("submit", submitHandler);
|
|
2534
|
+
unbinds.push(() => {
|
|
2535
|
+
form.removeEventListener("submit", submitHandler);
|
|
2536
|
+
});
|
|
2537
|
+
}
|
|
2538
|
+
async function refreshTokensIfNeeded(target, options, form) {
|
|
2539
|
+
if (options.refreshTokens === false) return;
|
|
2540
|
+
if (!options.staticCache) return;
|
|
2541
|
+
ensureSupportedHeadlessTransport(normalizeTransport(options.transport), normalizeMode(options.mode));
|
|
2542
|
+
const formHandle = options.formHandle || target.dataset.formieHandle;
|
|
2543
|
+
const endpoint = resolveRefreshTokensEndpoint(options, target);
|
|
2544
|
+
const renderId = (form?.querySelector("input[name=\"renderId\"]"))?.value || void 0;
|
|
2545
|
+
if (!formHandle) return;
|
|
2546
|
+
const refreshTokens = await requestRefreshTokens(endpoint, formHandle, renderId);
|
|
2547
|
+
if (!refreshTokens || !form) return;
|
|
2548
|
+
applyRefreshTokensToForm(form, refreshTokens);
|
|
2549
|
+
dispatchFormieDomEvent(target, "formie:refresh-tokens:after", refreshTokens);
|
|
2550
|
+
}
|
|
2551
|
+
function createFormieClient() {
|
|
2552
|
+
const instances = /* @__PURE__ */ new Map();
|
|
2553
|
+
const moduleRegistry = new ModuleRegistry();
|
|
2554
|
+
const pendingVisibilityMounts = /* @__PURE__ */ new Map();
|
|
2555
|
+
const pendingUnmounts = /* @__PURE__ */ new Map();
|
|
2556
|
+
const stageNames = [
|
|
2557
|
+
"prepare",
|
|
2558
|
+
"normalize",
|
|
2559
|
+
"validate",
|
|
2560
|
+
"screen",
|
|
2561
|
+
"authorize",
|
|
2562
|
+
"dispatch",
|
|
2563
|
+
"finalize"
|
|
2564
|
+
];
|
|
2565
|
+
const unmount = async (target) => {
|
|
2566
|
+
const inFlightUnmount = pendingUnmounts.get(target);
|
|
2567
|
+
if (inFlightUnmount) {
|
|
2568
|
+
await inFlightUnmount;
|
|
2569
|
+
return;
|
|
2570
|
+
}
|
|
2571
|
+
const unmountPromise = (async () => {
|
|
2572
|
+
debug$1.log("Unmount requested.", { target: getTargetDebugLabel(target) });
|
|
2573
|
+
const pendingUnmount = pendingVisibilityMounts.get(target);
|
|
2574
|
+
if (pendingUnmount) {
|
|
2575
|
+
pendingUnmount();
|
|
2576
|
+
pendingVisibilityMounts.delete(target);
|
|
2577
|
+
}
|
|
2578
|
+
const state = instances.get(target);
|
|
2579
|
+
if (!state) {
|
|
2580
|
+
debug$1.log("Unmount skipped (no mounted state).", { target: getTargetDebugLabel(target) });
|
|
2581
|
+
return;
|
|
2582
|
+
}
|
|
2583
|
+
dispatchFormieDomEvent(target, "formie:unmount:before", { id: state.instance.id });
|
|
2584
|
+
state.unbinds.forEach((unbind) => {
|
|
2585
|
+
unbind();
|
|
2586
|
+
});
|
|
2587
|
+
state.unbinds = [];
|
|
2588
|
+
state.validator?.destroy();
|
|
2589
|
+
state.validator = null;
|
|
2590
|
+
for (const moduleInstance of state.modules) await moduleInstance.destroy();
|
|
2591
|
+
state.modules = [];
|
|
2592
|
+
state.bus.clear();
|
|
2593
|
+
instances.delete(target);
|
|
2594
|
+
dispatchFormieDomEvent(target, "formie:unmount:after", { id: state.instance.id });
|
|
2595
|
+
debug$1.log("Unmount complete.", {
|
|
2596
|
+
id: state.instance.id,
|
|
2597
|
+
target: getTargetDebugLabel(target)
|
|
2598
|
+
});
|
|
2599
|
+
})().finally(() => {
|
|
2600
|
+
pendingUnmounts.delete(target);
|
|
2601
|
+
});
|
|
2602
|
+
pendingUnmounts.set(target, unmountPromise);
|
|
2603
|
+
await unmountPromise;
|
|
2604
|
+
};
|
|
2605
|
+
const mount = async (target, options) => {
|
|
2606
|
+
debug$1.log("Mount requested.", {
|
|
2607
|
+
target: getTargetDebugLabel(target),
|
|
2608
|
+
mode: options.mode,
|
|
2609
|
+
autoVisible: options.autoVisible
|
|
2610
|
+
});
|
|
2611
|
+
const pendingMount = pendingVisibilityMounts.get(target);
|
|
2612
|
+
if (pendingMount) {
|
|
2613
|
+
pendingMount();
|
|
2614
|
+
pendingVisibilityMounts.delete(target);
|
|
2615
|
+
}
|
|
2616
|
+
const existing = instances.get(target);
|
|
2617
|
+
if (existing) {
|
|
2618
|
+
debug$1.log("Mount skipped (already mounted).", {
|
|
2619
|
+
id: existing.instance.id,
|
|
2620
|
+
target: getTargetDebugLabel(target)
|
|
2621
|
+
});
|
|
2622
|
+
return existing.instance;
|
|
2623
|
+
}
|
|
2624
|
+
const bus = new EventBus();
|
|
2625
|
+
const unbinds = [];
|
|
2626
|
+
const id = target?.id || `formie-${instances.size + 1}`;
|
|
2627
|
+
const mergedFromDom = inferOptionsFromElement(target);
|
|
2628
|
+
const normalizedOptions = {
|
|
2629
|
+
...mergedFromDom,
|
|
2630
|
+
...options,
|
|
2631
|
+
mode: normalizeMode(options.mode ?? mergedFromDom.mode),
|
|
2632
|
+
transport: normalizeTransport(options.transport ?? mergedFromDom.transport)
|
|
2633
|
+
};
|
|
2634
|
+
const compatibilityOptions = resolveLegacyCompatibilityOptions(normalizedOptions.compatibility);
|
|
2635
|
+
if (normalizedOptions.mode !== "server-rendered" && !getFormFromTarget(target)) throw new Error(`Formie ${normalizedOptions.mode} mode is not implemented yet in the browser client.`);
|
|
2636
|
+
const renderPayload = await ensureHtmlRender(target, normalizedOptions);
|
|
2637
|
+
const form = getFormFromTarget(target);
|
|
2638
|
+
normalizedOptions.staticCache = options.staticCache ?? (form ? inferStaticCacheOnLoadFromDataset(form.dataset) : inferStaticCacheOnLoadFromDataset(target.dataset));
|
|
2639
|
+
const embeddedPayload = getEmbeddedPayload(target, form);
|
|
2640
|
+
const payload = renderPayload || embeddedPayload ? {
|
|
2641
|
+
...renderPayload || {},
|
|
2642
|
+
...embeddedPayload || {}
|
|
2643
|
+
} : null;
|
|
2644
|
+
const themeClassMap = payload?.theme;
|
|
2645
|
+
const stateStore = {};
|
|
2646
|
+
const moduleManifest = (payload?.modules || []).filter((item) => {
|
|
2647
|
+
return !!item?.id && !!item?.type;
|
|
2648
|
+
});
|
|
2649
|
+
debug$1.log("Resolved mount payload.", {
|
|
2650
|
+
target: getTargetDebugLabel(target),
|
|
2651
|
+
hasRenderPayload: !!renderPayload,
|
|
2652
|
+
hasEmbeddedPayload: !!embeddedPayload,
|
|
2653
|
+
moduleCount: moduleManifest.length
|
|
2654
|
+
});
|
|
2655
|
+
const resolvedThemeClassMap = registerThemeClassMap(target, themeClassMap, form);
|
|
2656
|
+
const validator = form ? new FormieValidator(form, {
|
|
2657
|
+
live: parseBooleanDatasetValue(form.dataset.formieValidationOnFocus),
|
|
2658
|
+
errorMessage: form.dataset.formieErrorMessage || "",
|
|
2659
|
+
fieldContainerErrorClass: resolvedThemeClassMap.fieldLayoutError || [],
|
|
2660
|
+
inputErrorClass: resolvedThemeClassMap.fieldControlError || [],
|
|
2661
|
+
messagesClass: resolvedThemeClassMap.fieldErrors || [],
|
|
2662
|
+
messageClass: resolvedThemeClassMap.fieldError || []
|
|
2663
|
+
}) : null;
|
|
2664
|
+
if (form && validator) {
|
|
2665
|
+
const formWithValidationApi = form;
|
|
2666
|
+
formWithValidationApi.formieValidation = validator;
|
|
2667
|
+
stateStore.validation = validator;
|
|
2668
|
+
const validatorDetail = {
|
|
2669
|
+
validator,
|
|
2670
|
+
addValidator: validator.addValidator.bind(validator),
|
|
2671
|
+
removeValidator: validator.removeValidator.bind(validator)
|
|
2672
|
+
};
|
|
2673
|
+
dispatchFormieDomEvent(form, "formie:validator:ready", validatorDetail);
|
|
2674
|
+
dispatchFormieDomEvent(target, "formie:validator:ready", validatorDetail);
|
|
2675
|
+
}
|
|
2676
|
+
if (form) {
|
|
2677
|
+
if (renderPayload || normalizedOptions.endpoint || target.dataset.formieEndpoint) normalizeHeadlessManagedUrls(target, form, normalizedOptions);
|
|
2678
|
+
syncPageTabErrors(form);
|
|
2679
|
+
}
|
|
2680
|
+
if (Object.keys(resolvedThemeClassMap).length) dispatchFormieDomEvent(target, "formie:theme:applied", { hasClasses: true });
|
|
2681
|
+
const modules = await loadModulesFromManifest(moduleManifest, {
|
|
2682
|
+
registry: moduleRegistry,
|
|
2683
|
+
matchContext: {
|
|
2684
|
+
root: target,
|
|
2685
|
+
form,
|
|
2686
|
+
mode: normalizedOptions.mode
|
|
2687
|
+
},
|
|
2688
|
+
setupContext: {
|
|
2689
|
+
formId: id,
|
|
2690
|
+
root: target,
|
|
2691
|
+
form,
|
|
2692
|
+
target,
|
|
2693
|
+
scope: "form",
|
|
2694
|
+
state: stateStore,
|
|
2695
|
+
on: (eventName, callback) => {
|
|
2696
|
+
return bus.on(eventName, callback);
|
|
2697
|
+
},
|
|
2698
|
+
emit: (eventName, payload) => {
|
|
2699
|
+
dispatchFormieDomEvent(target, eventName, payload);
|
|
2700
|
+
return bus.emitSafe(eventName, payload).then((emitReport) => {
|
|
2701
|
+
if (emitReport.failed.length > 0) debug$1.warn("Lifecycle listeners failed.", {
|
|
2702
|
+
eventName,
|
|
2703
|
+
failed: emitReport.failed.length
|
|
2704
|
+
});
|
|
2705
|
+
});
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
});
|
|
2709
|
+
debug$1.log("Module setup complete.", {
|
|
2710
|
+
target: getTargetDebugLabel(target),
|
|
2711
|
+
moduleInstances: modules.length
|
|
2712
|
+
});
|
|
2713
|
+
const instance = {
|
|
2714
|
+
id,
|
|
2715
|
+
root: target,
|
|
2716
|
+
submit: async (action = "submit") => {
|
|
2717
|
+
debug$1.log("Submit requested.", {
|
|
2718
|
+
id,
|
|
2719
|
+
target: getTargetDebugLabel(target),
|
|
2720
|
+
action
|
|
2721
|
+
});
|
|
2722
|
+
if (!form) return {
|
|
2723
|
+
ok: false,
|
|
2724
|
+
code: "FORM_NOT_FOUND",
|
|
2725
|
+
message: "No form element found for mount target.",
|
|
2726
|
+
formErrors: ["No form element found for mount target."]
|
|
2727
|
+
};
|
|
2728
|
+
const submitAction = form.querySelector("input[name=\"submitAction\"]");
|
|
2729
|
+
if (submitAction) submitAction.value = action;
|
|
2730
|
+
if (form.getAttribute("data-formie-loading") === "true") return {
|
|
2731
|
+
ok: false,
|
|
2732
|
+
code: "SUBMIT_IN_PROGRESS",
|
|
2733
|
+
message: "Submission already in progress.",
|
|
2734
|
+
formErrors: []
|
|
2735
|
+
};
|
|
2736
|
+
const fallbackSubmitter = form.querySelector(`[data-formie-action="${action}"]`);
|
|
2737
|
+
const result = await executeAjaxSubmitFlow({
|
|
2738
|
+
id,
|
|
2739
|
+
target,
|
|
2740
|
+
form,
|
|
2741
|
+
bus,
|
|
2742
|
+
validator,
|
|
2743
|
+
validateOnSubmit: shouldValidateOnSubmit(form),
|
|
2744
|
+
action,
|
|
2745
|
+
submitter: fallbackSubmitter,
|
|
2746
|
+
waitForSubmitDelay,
|
|
2747
|
+
onRefreshTokensAfterSubmit: async () => {
|
|
2748
|
+
await refreshTokensAfterSubmitIfNeeded(target, normalizedOptions, form);
|
|
2749
|
+
},
|
|
2750
|
+
dispatchSubmitResult: (submitResult) => {
|
|
2751
|
+
dispatchFormieDomEvent(target, "formie:submit:result", submitResult);
|
|
2752
|
+
}
|
|
2753
|
+
});
|
|
2754
|
+
debug$1.log("Submit completed.", {
|
|
2755
|
+
id,
|
|
2756
|
+
action,
|
|
2757
|
+
ok: result.ok,
|
|
2758
|
+
code: result.code,
|
|
2759
|
+
message: result.message
|
|
2760
|
+
});
|
|
2761
|
+
return result;
|
|
2762
|
+
},
|
|
2763
|
+
destroy: async () => {
|
|
2764
|
+
await unmount(target);
|
|
2765
|
+
},
|
|
2766
|
+
on: (eventName, callback) => {
|
|
2767
|
+
return bus.on(eventName, callback);
|
|
2768
|
+
}
|
|
2769
|
+
};
|
|
2770
|
+
if (form) {
|
|
2771
|
+
bindLegacyValidatorCompatibility({
|
|
2772
|
+
target,
|
|
2773
|
+
form,
|
|
2774
|
+
validatorDetail: validator ? {
|
|
2775
|
+
validator,
|
|
2776
|
+
addValidator: validator.addValidator.bind(validator),
|
|
2777
|
+
removeValidator: validator.removeValidator.bind(validator)
|
|
2778
|
+
} : null,
|
|
2779
|
+
options: compatibilityOptions,
|
|
2780
|
+
unbinds
|
|
2781
|
+
});
|
|
2782
|
+
bindLegacyDomEventCompatibility({
|
|
2783
|
+
target,
|
|
2784
|
+
form,
|
|
2785
|
+
instance,
|
|
2786
|
+
options: compatibilityOptions,
|
|
2787
|
+
unbinds
|
|
2788
|
+
});
|
|
2789
|
+
}
|
|
2790
|
+
if (form) {
|
|
2791
|
+
bindFormEvents(target, form, normalizedOptions, bus, validator, unbinds);
|
|
2792
|
+
await refreshTokensIfNeeded(target, normalizedOptions, form);
|
|
2793
|
+
}
|
|
2794
|
+
stageNames.forEach((stageName) => {
|
|
2795
|
+
const beforeDomUnbind = bus.on(`formie:stage:${stageName}:before`, async (payload) => {
|
|
2796
|
+
dispatchFormieDomEvent(target, `formie:stage:${stageName}:before`, payload);
|
|
2797
|
+
});
|
|
2798
|
+
const beforeUnbind = bus.on(`formie:stage:${stageName}:before`, async (payload) => {
|
|
2799
|
+
for (const moduleInstance of modules) if (moduleInstance.onBeforeStage) await moduleInstance.onBeforeStage(payload);
|
|
2800
|
+
});
|
|
2801
|
+
const afterDomUnbind = bus.on(`formie:stage:${stageName}:after`, async (payload) => {
|
|
2802
|
+
dispatchFormieDomEvent(target, `formie:stage:${stageName}:after`, payload);
|
|
2803
|
+
});
|
|
2804
|
+
const afterUnbind = bus.on(`formie:stage:${stageName}:after`, async (payload) => {
|
|
2805
|
+
const stagePayload = payload;
|
|
2806
|
+
for (const moduleInstance of modules) if (moduleInstance.onAfterStage) await moduleInstance.onAfterStage(stagePayload, stagePayload.result);
|
|
2807
|
+
});
|
|
2808
|
+
unbinds.push(beforeDomUnbind, beforeUnbind, afterDomUnbind, afterUnbind);
|
|
2809
|
+
});
|
|
2810
|
+
const submitBeforeUnbind = bus.on("formie:submit:before", async (payload) => {
|
|
2811
|
+
dispatchFormieDomEvent(target, "formie:submit:before", payload);
|
|
2812
|
+
});
|
|
2813
|
+
const submitAfterUnbind = bus.on("formie:submit:after", async (payload) => {
|
|
2814
|
+
dispatchFormieDomEvent(target, "formie:submit:after", payload);
|
|
2815
|
+
});
|
|
2816
|
+
const submitFinalBeforeUnbind = bus.on("formie:submit:final:before", async (payload) => {
|
|
2817
|
+
dispatchFormieDomEvent(target, "formie:submit:final:before", payload);
|
|
2818
|
+
});
|
|
2819
|
+
const submitFinalAfterUnbind = bus.on("formie:submit:final:after", async (payload) => {
|
|
2820
|
+
dispatchFormieDomEvent(target, "formie:submit:final:after", payload);
|
|
2821
|
+
});
|
|
2822
|
+
unbinds.push(submitBeforeUnbind, submitAfterUnbind, submitFinalBeforeUnbind, submitFinalAfterUnbind);
|
|
2823
|
+
instances.set(target, {
|
|
2824
|
+
options: normalizedOptions,
|
|
2825
|
+
bus,
|
|
2826
|
+
form,
|
|
2827
|
+
validator,
|
|
2828
|
+
modules,
|
|
2829
|
+
unbinds,
|
|
2830
|
+
instance
|
|
2831
|
+
});
|
|
2832
|
+
dispatchFormieDomEvent(target, "formie:mount:after", {
|
|
2833
|
+
id,
|
|
2834
|
+
mode: normalizedOptions.mode
|
|
2835
|
+
});
|
|
2836
|
+
debug$1.log("Mount complete.", {
|
|
2837
|
+
id,
|
|
2838
|
+
target: getTargetDebugLabel(target),
|
|
2839
|
+
mode: normalizedOptions.mode
|
|
2840
|
+
});
|
|
2841
|
+
return instance;
|
|
2842
|
+
};
|
|
2843
|
+
const mountWhenVisible = (target, options) => {
|
|
2844
|
+
if (!options.autoVisible || isElementVisible(target) || typeof IntersectionObserver === "undefined") return mount(target, options);
|
|
2845
|
+
if (instances.has(target)) return Promise.resolve(instances.get(target)?.instance || null);
|
|
2846
|
+
if (pendingVisibilityMounts.has(target)) {
|
|
2847
|
+
debug$1.log("Mount deferred (already waiting visibility).", { target: getTargetDebugLabel(target) });
|
|
2848
|
+
return Promise.resolve(null);
|
|
2849
|
+
}
|
|
2850
|
+
const observer = new IntersectionObserver((entries) => {
|
|
2851
|
+
if (!entries.some((entry) => {
|
|
2852
|
+
return entry.target === target && entry.isIntersecting;
|
|
2853
|
+
})) return;
|
|
2854
|
+
observer.disconnect();
|
|
2855
|
+
pendingVisibilityMounts.delete(target);
|
|
2856
|
+
debug$1.log("Visibility reached, proceeding mount.", { target: getTargetDebugLabel(target) });
|
|
2857
|
+
mount(target, {
|
|
2858
|
+
...options,
|
|
2859
|
+
autoVisible: false
|
|
2860
|
+
});
|
|
2861
|
+
}, { threshold: .01 });
|
|
2862
|
+
observer.observe(target);
|
|
2863
|
+
pendingVisibilityMounts.set(target, () => {
|
|
2864
|
+
observer.disconnect();
|
|
2865
|
+
});
|
|
2866
|
+
debug$1.log("Mount deferred until visible.", { target: getTargetDebugLabel(target) });
|
|
2867
|
+
return Promise.resolve(null);
|
|
2868
|
+
};
|
|
2869
|
+
const update = async (target, options) => {
|
|
2870
|
+
const state = instances.get(target);
|
|
2871
|
+
if (!state) return mount(target, {
|
|
2872
|
+
...inferOptionsFromElement(target),
|
|
2873
|
+
...options,
|
|
2874
|
+
mode: options.mode || "server-rendered"
|
|
2875
|
+
});
|
|
2876
|
+
state.options = {
|
|
2877
|
+
...state.options,
|
|
2878
|
+
...options
|
|
2879
|
+
};
|
|
2880
|
+
const resolvedThemeClassMap = registerThemeClassMap(target, options.payload?.theme || state.options.payload?.theme || getEmbeddedPayload(target, state.form)?.theme, state.form);
|
|
2881
|
+
if (state.validator) {
|
|
2882
|
+
state.validator.config.fieldContainerErrorClass = resolvedThemeClassMap.fieldLayoutError || [];
|
|
2883
|
+
state.validator.config.inputErrorClass = resolvedThemeClassMap.fieldControlError || [];
|
|
2884
|
+
state.validator.config.messagesClass = resolvedThemeClassMap.fieldErrors || [];
|
|
2885
|
+
state.validator.config.messageClass = resolvedThemeClassMap.fieldError || [];
|
|
2886
|
+
}
|
|
2887
|
+
if (Object.keys(resolvedThemeClassMap).length) dispatchFormieDomEvent(target, "formie:theme:applied", {
|
|
2888
|
+
hasClasses: true,
|
|
2889
|
+
reason: "update"
|
|
2890
|
+
});
|
|
2891
|
+
return state.instance;
|
|
2892
|
+
};
|
|
2893
|
+
const getInstance = (target) => {
|
|
2894
|
+
return instances.get(target)?.instance || null;
|
|
2895
|
+
};
|
|
2896
|
+
const refreshForCache = async (targetOrId) => {
|
|
2897
|
+
warnCompatibilityOnce("refreshForCache", "Global `Formie.refreshForCache()` has been deprecated. Use built-in static-cache token refresh handling instead.");
|
|
2898
|
+
let target = null;
|
|
2899
|
+
if (typeof targetOrId === "string") {
|
|
2900
|
+
const byId = document.getElementById(targetOrId);
|
|
2901
|
+
if (byId) target = byId;
|
|
2902
|
+
else target = document.querySelector(`[data-formie-form-id="${targetOrId}"]`);
|
|
2903
|
+
} else target = targetOrId;
|
|
2904
|
+
if (!target) {
|
|
2905
|
+
debug$1.warn("refreshForCache target not found.", { targetOrId });
|
|
2906
|
+
return;
|
|
2907
|
+
}
|
|
2908
|
+
const state = instances.get(target);
|
|
2909
|
+
const form = getFormFromTarget(target);
|
|
2910
|
+
const options = state?.options || inferOptionsFromElement(target);
|
|
2911
|
+
if (!form) {
|
|
2912
|
+
debug$1.warn("refreshForCache found no form element for target.", { target: getTargetDebugLabel(target) });
|
|
2913
|
+
return;
|
|
2914
|
+
}
|
|
2915
|
+
const formHandle = options.formHandle || target.dataset.formieHandle || form.dataset.formieHandle;
|
|
2916
|
+
const endpoint = resolveRefreshTokensEndpoint(options, target);
|
|
2917
|
+
const renderId = form.querySelector("input[name=\"renderId\"]")?.value || void 0;
|
|
2918
|
+
if (!formHandle) {
|
|
2919
|
+
debug$1.warn("refreshForCache found no form handle for target.", { target: getTargetDebugLabel(target) });
|
|
2920
|
+
return;
|
|
2921
|
+
}
|
|
2922
|
+
const refreshTokens = await requestRefreshTokens(endpoint, formHandle, renderId);
|
|
2923
|
+
if (!refreshTokens) return;
|
|
2924
|
+
applyRefreshTokensToForm(form, refreshTokens);
|
|
2925
|
+
dispatchFormieDomEvent(target, "formie:refresh-tokens:after", refreshTokens);
|
|
2926
|
+
};
|
|
2927
|
+
const registerModule = (moduleDefinition, options) => {
|
|
2928
|
+
return moduleRegistry.register(moduleDefinition, options);
|
|
2929
|
+
};
|
|
2930
|
+
const unregisterModule = (moduleId) => {
|
|
2931
|
+
moduleRegistry.unregister(moduleId);
|
|
2932
|
+
};
|
|
2933
|
+
const getRegisteredModules = () => {
|
|
2934
|
+
return moduleRegistry.getAll();
|
|
2935
|
+
};
|
|
2936
|
+
const scan = async (root) => {
|
|
2937
|
+
const scope = root || document;
|
|
2938
|
+
const targets = Array.from(scope.querySelectorAll(ROOT_SELECTORS));
|
|
2939
|
+
debug$1.log("Scan started.", {
|
|
2940
|
+
scope: scope === document ? "document" : scope,
|
|
2941
|
+
targetCount: targets.length
|
|
2942
|
+
});
|
|
2943
|
+
const instances = (await Promise.all(targets.map((target) => {
|
|
2944
|
+
return mountWhenVisible(target, inferOptionsFromElement(target));
|
|
2945
|
+
}))).filter((item) => !!item);
|
|
2946
|
+
debug$1.log("Scan finished.", {
|
|
2947
|
+
mountedCount: instances.length,
|
|
2948
|
+
deferredCount: targets.length - instances.length
|
|
2949
|
+
});
|
|
2950
|
+
return instances;
|
|
2951
|
+
};
|
|
2952
|
+
const observe = (root) => {
|
|
2953
|
+
if (typeof MutationObserver === "undefined") return () => {};
|
|
2954
|
+
const scope = root || document;
|
|
2955
|
+
debug$1.log("Observer started.", { scope: scope === document ? "document" : scope });
|
|
2956
|
+
const observer = new MutationObserver((mutations) => {
|
|
2957
|
+
mutations.forEach((mutation) => {
|
|
2958
|
+
mutation.addedNodes.forEach((node) => {
|
|
2959
|
+
if (!(node instanceof Element)) return;
|
|
2960
|
+
if (node.matches(ROOT_SELECTORS)) {
|
|
2961
|
+
debug$1.log("Observer detected new root.", { target: getTargetDebugLabel(node) });
|
|
2962
|
+
mountWhenVisible(node, inferOptionsFromElement(node));
|
|
2963
|
+
}
|
|
2964
|
+
node.querySelectorAll(ROOT_SELECTORS).forEach((child) => {
|
|
2965
|
+
debug$1.log("Observer detected new nested root.", { target: getTargetDebugLabel(child) });
|
|
2966
|
+
mountWhenVisible(child, inferOptionsFromElement(child));
|
|
2967
|
+
});
|
|
2968
|
+
});
|
|
2969
|
+
mutation.removedNodes.forEach((node) => {
|
|
2970
|
+
if (!(node instanceof Element)) return;
|
|
2971
|
+
if (instances.has(node)) {
|
|
2972
|
+
debug$1.log("Observer detected removed root.", { target: getTargetDebugLabel(node) });
|
|
2973
|
+
unmount(node);
|
|
2974
|
+
}
|
|
2975
|
+
node.querySelectorAll(ROOT_SELECTORS).forEach((child) => {
|
|
2976
|
+
if (instances.has(child)) {
|
|
2977
|
+
debug$1.log("Observer detected removed nested root.", { target: getTargetDebugLabel(child) });
|
|
2978
|
+
unmount(child);
|
|
2979
|
+
}
|
|
2980
|
+
});
|
|
2981
|
+
});
|
|
2982
|
+
});
|
|
2983
|
+
});
|
|
2984
|
+
observer.observe(scope, {
|
|
2985
|
+
childList: true,
|
|
2986
|
+
subtree: true
|
|
2987
|
+
});
|
|
2988
|
+
return () => {
|
|
2989
|
+
observer.disconnect();
|
|
2990
|
+
debug$1.log("Observer stopped.");
|
|
2991
|
+
pendingVisibilityMounts.forEach((cleanup, target) => {
|
|
2992
|
+
if (isWithinScope(target, scope)) {
|
|
2993
|
+
cleanup();
|
|
2994
|
+
pendingVisibilityMounts.delete(target);
|
|
2995
|
+
}
|
|
2996
|
+
});
|
|
2997
|
+
const roots = [];
|
|
2998
|
+
if (scope instanceof Element && scope.matches(ROOT_SELECTORS)) roots.push(scope);
|
|
2999
|
+
scope.querySelectorAll(ROOT_SELECTORS).forEach((target) => {
|
|
3000
|
+
roots.push(target);
|
|
3001
|
+
});
|
|
3002
|
+
roots.forEach((target) => {
|
|
3003
|
+
if (instances.has(target)) unmount(target);
|
|
3004
|
+
});
|
|
3005
|
+
};
|
|
3006
|
+
};
|
|
3007
|
+
return {
|
|
3008
|
+
mount,
|
|
3009
|
+
unmount,
|
|
3010
|
+
update,
|
|
3011
|
+
getInstance,
|
|
3012
|
+
refreshForCache,
|
|
3013
|
+
registerModule,
|
|
3014
|
+
unregisterModule,
|
|
3015
|
+
getRegisteredModules,
|
|
3016
|
+
scan,
|
|
3017
|
+
observe
|
|
3018
|
+
};
|
|
3019
|
+
}
|
|
3020
|
+
//#endregion
|
|
3021
|
+
//#region src/js/core/hydrate-modules.ts
|
|
3022
|
+
var debug = createDebug("general", "module-hydrator");
|
|
3023
|
+
async function hydrateFormieModules(options) {
|
|
3024
|
+
const root = options.root;
|
|
3025
|
+
const form = options.form ?? (root instanceof HTMLFormElement ? root : root.closest("form"));
|
|
3026
|
+
const modules = options.modules ?? [];
|
|
3027
|
+
const mode = options.mode ?? "server-rendered";
|
|
3028
|
+
const registry = options.registry ?? new ModuleRegistry();
|
|
3029
|
+
const bus = new EventBus();
|
|
3030
|
+
const instances = await loadModulesFromManifest(modules, {
|
|
3031
|
+
registry,
|
|
3032
|
+
setupContext: {
|
|
3033
|
+
formId: form?.id || root.id || "formie-modules",
|
|
3034
|
+
root,
|
|
3035
|
+
form,
|
|
3036
|
+
target: root,
|
|
3037
|
+
scope: "form",
|
|
3038
|
+
state: {},
|
|
3039
|
+
options: {},
|
|
3040
|
+
on: (eventName, callback) => {
|
|
3041
|
+
return bus.on(eventName, callback);
|
|
3042
|
+
},
|
|
3043
|
+
emit: async (eventName, payload) => {
|
|
3044
|
+
await bus.emit(eventName, payload);
|
|
3045
|
+
}
|
|
3046
|
+
},
|
|
3047
|
+
matchContext: {
|
|
3048
|
+
root,
|
|
3049
|
+
form,
|
|
3050
|
+
mode
|
|
3051
|
+
}
|
|
3052
|
+
});
|
|
3053
|
+
debug.log("Hydrated module manifest.", {
|
|
3054
|
+
moduleCount: modules.length,
|
|
3055
|
+
instanceCount: instances.length,
|
|
3056
|
+
mode
|
|
3057
|
+
});
|
|
3058
|
+
return {
|
|
3059
|
+
destroy: async () => {
|
|
3060
|
+
await destroyModuleInstances(instances);
|
|
3061
|
+
bus.clear();
|
|
3062
|
+
},
|
|
3063
|
+
on: (eventName, callback) => {
|
|
3064
|
+
return bus.on(eventName, callback);
|
|
3065
|
+
},
|
|
3066
|
+
emit: async (eventName, payload) => {
|
|
3067
|
+
await bus.emit(eventName, payload);
|
|
3068
|
+
},
|
|
3069
|
+
registerModule: (moduleDefinition, registrationOptions = {}) => {
|
|
3070
|
+
return registry.register(moduleDefinition, registrationOptions);
|
|
3071
|
+
},
|
|
3072
|
+
unregisterModule: (moduleId) => {
|
|
3073
|
+
registry.unregister(moduleId);
|
|
3074
|
+
},
|
|
3075
|
+
getRegisteredModules: () => {
|
|
3076
|
+
return registry.getAll();
|
|
3077
|
+
}
|
|
3078
|
+
};
|
|
3079
|
+
}
|
|
3080
|
+
async function destroyModuleInstances(instances) {
|
|
3081
|
+
for (const instance of instances) try {
|
|
3082
|
+
await instance.destroy();
|
|
3083
|
+
} catch (error) {
|
|
3084
|
+
console.error("[formie] Failed to destroy module instance.", error);
|
|
3085
|
+
debug.warn("Failed destroying module instance.", { error });
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
//#endregion
|
|
3089
|
+
//#region src/js/core/formie.ts
|
|
3090
|
+
function isElement(value) {
|
|
3091
|
+
return value instanceof Element;
|
|
3092
|
+
}
|
|
3093
|
+
function isSuccessfulResult(result) {
|
|
3094
|
+
return result.ok;
|
|
3095
|
+
}
|
|
3096
|
+
function describeTarget(target) {
|
|
3097
|
+
if (typeof target === "string") return `selector "${target}"`;
|
|
3098
|
+
if (isElement(target)) return `element "${target.tagName.toLowerCase()}"`;
|
|
3099
|
+
return "provided element collection";
|
|
3100
|
+
}
|
|
3101
|
+
function toUniqueElements(values) {
|
|
3102
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3103
|
+
const elements = [];
|
|
3104
|
+
for (const value of values) {
|
|
3105
|
+
if (!isElement(value) || seen.has(value)) continue;
|
|
3106
|
+
seen.add(value);
|
|
3107
|
+
elements.push(value);
|
|
3108
|
+
}
|
|
3109
|
+
return elements;
|
|
3110
|
+
}
|
|
3111
|
+
function resolveElements(target) {
|
|
3112
|
+
if (typeof target === "string") return Array.from(document.querySelectorAll(target));
|
|
3113
|
+
if (isElement(target)) return [target];
|
|
3114
|
+
return toUniqueElements(target);
|
|
3115
|
+
}
|
|
3116
|
+
function waitForDomReady() {
|
|
3117
|
+
if (document.readyState !== "loading") return Promise.resolve();
|
|
3118
|
+
return new Promise((resolve) => {
|
|
3119
|
+
document.addEventListener("DOMContentLoaded", () => resolve(), { once: true });
|
|
3120
|
+
});
|
|
3121
|
+
}
|
|
3122
|
+
async function resolveElementsWhenReady(target) {
|
|
3123
|
+
const elements = resolveElements(target);
|
|
3124
|
+
if (elements.length > 0 || typeof target !== "string") return elements;
|
|
3125
|
+
await waitForDomReady();
|
|
3126
|
+
return resolveElements(target);
|
|
3127
|
+
}
|
|
3128
|
+
function resolveObservationScope(target) {
|
|
3129
|
+
if (typeof target === "string") return document;
|
|
3130
|
+
if (isElement(target)) return target.getRootNode();
|
|
3131
|
+
return document;
|
|
3132
|
+
}
|
|
3133
|
+
function buildMountOptions(options) {
|
|
3134
|
+
const { element: _element, observe: _observe, allowEmpty: _allowEmpty, client: _client, onReady: _onReady, onResult: _onResult, onSuccess: _onSuccess, onError: _onError, onEvent: _onEvent, ...mountOptions } = options;
|
|
3135
|
+
return {
|
|
3136
|
+
mode: "server-rendered",
|
|
3137
|
+
...mountOptions
|
|
3138
|
+
};
|
|
3139
|
+
}
|
|
3140
|
+
async function mountResolvedElements(options, client, states, targets) {
|
|
3141
|
+
const mounted = [];
|
|
3142
|
+
const mountOptions = buildMountOptions(options);
|
|
3143
|
+
for (const target of targets) {
|
|
3144
|
+
const existing = states.get(target);
|
|
3145
|
+
if (existing) {
|
|
3146
|
+
mounted.push(existing.instance);
|
|
3147
|
+
continue;
|
|
3148
|
+
}
|
|
3149
|
+
const instance = await client.mount(target, mountOptions);
|
|
3150
|
+
const unsubs = [];
|
|
3151
|
+
options.onReady?.(instance);
|
|
3152
|
+
unsubs.push(instance.on("formie:submit:result", (payload) => {
|
|
3153
|
+
const result = payload;
|
|
3154
|
+
options.onResult?.(result, instance);
|
|
3155
|
+
if (isSuccessfulResult(result)) options.onSuccess?.(result, instance);
|
|
3156
|
+
else options.onError?.(result, instance);
|
|
3157
|
+
}));
|
|
3158
|
+
if (options.onEvent) for (const eventName of FORMIE_HTML_EVENT_NAMES) unsubs.push(instance.on(eventName, (payload) => {
|
|
3159
|
+
options.onEvent?.({
|
|
3160
|
+
name: eventName,
|
|
3161
|
+
payload
|
|
3162
|
+
}, instance);
|
|
3163
|
+
}));
|
|
3164
|
+
states.set(target, {
|
|
3165
|
+
instance,
|
|
3166
|
+
unsubs
|
|
3167
|
+
});
|
|
3168
|
+
mounted.push(instance);
|
|
3169
|
+
}
|
|
3170
|
+
return mounted;
|
|
3171
|
+
}
|
|
3172
|
+
async function formie(options) {
|
|
3173
|
+
const client = options.client ?? createFormieClient();
|
|
3174
|
+
const states = /* @__PURE__ */ new Map();
|
|
3175
|
+
const matchedElements = await resolveElementsWhenReady(options.element);
|
|
3176
|
+
if (matchedElements.length === 0 && !options.allowEmpty) throw new Error(`Formie could not find any elements for ${describeTarget(options.element)}.`);
|
|
3177
|
+
await mountResolvedElements(options, client, states, matchedElements);
|
|
3178
|
+
const stopObserving = options.observe ? client.observe(resolveObservationScope(options.element)) : null;
|
|
3179
|
+
return {
|
|
3180
|
+
client,
|
|
3181
|
+
get instances() {
|
|
3182
|
+
return Array.from(states.values()).map(({ instance }) => instance);
|
|
3183
|
+
},
|
|
3184
|
+
get(target) {
|
|
3185
|
+
const element = typeof target === "string" ? document.querySelector(target) : target;
|
|
3186
|
+
if (!element) return null;
|
|
3187
|
+
return states.get(element)?.instance ?? client.getInstance(element);
|
|
3188
|
+
},
|
|
3189
|
+
async rescan() {
|
|
3190
|
+
const nextTargets = resolveElements(options.element);
|
|
3191
|
+
if (nextTargets.length === 0) return Array.from(states.values()).map(({ instance }) => instance);
|
|
3192
|
+
return mountResolvedElements(options, client, states, nextTargets);
|
|
3193
|
+
},
|
|
3194
|
+
async destroy() {
|
|
3195
|
+
stopObserving?.();
|
|
3196
|
+
const mountedEntries = Array.from(states.entries());
|
|
3197
|
+
for (const [target, state] of mountedEntries) {
|
|
3198
|
+
state.unsubs.forEach((unsubscribe) => unsubscribe());
|
|
3199
|
+
await client.unmount(target);
|
|
3200
|
+
states.delete(target);
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
};
|
|
3204
|
+
}
|
|
3205
|
+
//#endregion
|
|
3206
|
+
export { FORMIE_HTML_EVENT_NAMES, FormieValidator, LEGACY_FORMIE_DOM_EVENT_BRIDGES, LEGACY_FORMIE_VALIDATOR_EVENT_BRIDGES, ModuleRegistry, bindLegacyDomEventCompatibility, bindLegacyValidatorCompatibility, buildFieldValueRegistry, createDebug, createFormieClient, debugLog, debugWarn, defineAddressModule, defineCaptchaModule, definePassiveCaptchaModule, definePaymentModule, fieldKeyToInputName, formie, getFieldModuleEventName, getFormieTranslations, getGlobalModuleLifecycleEventName, getScopedModuleLifecycleEventName, hydrateFormieModules, inputNameToFieldKey, isFormieDebugEnabled, mergeFormieTranslations, normalizeFieldKey, normalizeFormieEventName, parseFieldReference, resolveFieldReferenceFromFormData, resolveFieldReferenceLive, resolveLegacyCompatibilityOptions, setFormieDebugEnabled, setFormieTranslations, t, toDomEventName, translate };
|