var FA__WF_HASH_ID = "g1wBM"; var FA__BASE_URL = "https:\/\/aspeninstitute.tfaforms.net"; var FA__BASE_URL_ORIGIN = new URL(FA__BASE_URL).origin; var FA__CONTAINER = null; var FA__injectedStyles = []; var FA__isInitialLoad = true; /** * Appends the JS-embed signalling values as query params instead of sending them as the * X-Requested-With / X-FA-Embedded headers. Neither header is CORS-safelisted, so sending * them cross-origin forces an OPTIONS preflight; passing them as params keeps the request * "simple" and skips the preflight. JsEmbedHeadersMiddleware promotes them back into request * headers server-side so AJAX/embed detection is unchanged. * * Relative/root-relative URLs (e.g. a form action of "/api_v2/wf/...") are resolved against * FA__BASE_URL_ORIGIN, not window.location.href. This script runs in the host page's context, so * resolving against the host location would target the customer's origin instead of FormAssembly. */ function FA__withEmbedParams(url) { var resolved = new URL(url, FA__BASE_URL_ORIGIN); resolved.searchParams.set('X-Requested-With', 'XMLHttpRequest'); resolved.searchParams.set('X-FA-Embedded', '1'); resolved.searchParams.set('X-FA-Parent-URL', window.location.href); return resolved.toString(); } /** * Fetches a REST URL and renders the HTML response into the embed container. */ function FA__fetchAndRender(url) { fetch(FA__withEmbedParams(url), { redirect: 'follow', }) .then(function (r) { return r.text(); }) .then(function (html) { FA__renderHtml(html, FA__attachSubmitInterceptor); }) .catch(function (err) { console.error('FormAssembly embed error:', err); }); } /** * Resume entry point used by the please-wait screen. The server emits an absolute * /wf/{token} URL when the lock is released; rewrite it to /rest/wf/{token} so * the resumed step stays inside the embed container. */ window.FA__resumeInEmbed = function (nextUrl) { FA__fetchAndRender(nextUrl.replace('/wf/', '/rest/wf/')); }; /** * Returns the target container element where the workflow will be embedded. * Uses data-qp-target-id attribute on the embedding script tag, falling back to document.body. */ function FA__getContainer() { var script = document.querySelector('script[data-qp-target-id]'); if (script) { var id = script.getAttribute('data-qp-target-id'); if (id) { return document.getElementById(id); } } var fallback = document.createElement('div'); document.body.appendChild(fallback); return fallback; } /** * Reinitializes form behaviors and components after the container DOM has been replaced. * Handles wFORMS behaviors, Kalendae calendar fallback, and reCAPTCHA state reset. */ function FA__reinitializeComponents() { // wFORMS: clear stale instances and reapply behaviors to new DOM if (typeof wFORMS !== 'undefined' && typeof wFORMS.applyBehaviors === 'function') { wFORMS.instances = {}; document.querySelectorAll('body > .kalendae').forEach(function (el) { el.remove(); }); var forms = FA__CONTAINER.querySelectorAll('form'); forms.forEach(function (form) { wFORMS.applyBehaviors(form); }); if (wFORMS.behaviors && wFORMS.behaviors.validation && typeof wFORMS.behaviors.validation.enableResumeLater === 'function') { wFORMS.behaviors.validation.enableResumeLater(); } if (wFORMS.behaviors && wFORMS.behaviors.prefill && FA__isInitialLoad) { forms.forEach(function (form) { wFORMS.behaviors.prefill.skip = false; wFORMS.behaviors.prefill.applyTo(form); }); FA__isInitialLoad = false; } } // Kalendae fallback: wFORMS calendar behavior fails to reinitialize on re-render if (typeof Kalendae !== 'undefined') { var dateFields = FA__CONTAINER.querySelectorAll('.validate-datecal'); if (dateFields.length > 0 && !document.querySelector('body > .kalendae')) { var calBehavior = typeof wFORMS !== 'undefined' && wFORMS.behaviors && wFORMS.behaviors.calendar; if (calBehavior) calBehavior.generateDefaultSettings(); var opts = (calBehavior && calBehavior.instance && calBehavior.instance.prototype.defaultOptions) || { format: 'MM/DD/YYYY', selection: 'single', side: 'bottom', dayOutOfMonthClickable: true }; dateFields.forEach(function (input) { new Kalendae.Input(input, opts); }); } } // jSignature fallback: if applyBehaviors didn't create the signature canvas, call applyTo directly if (typeof wFORMS !== 'undefined' && wFORMS.behaviors && wFORMS.behaviors.esignature) { if (FA__CONTAINER.querySelector('.wformsSigDiv') && !FA__CONTAINER.querySelector('.wformsSigDiv canvas')) { FA__CONTAINER.querySelectorAll('form').forEach(function (form) { if (form.querySelector('.wFormsSignature')) { wFORMS.behaviors.esignature.applyTo(form); } }); } } // reCAPTCHA: reset state so the widget re-renders on the new DOM. // The enterprise script is skipped on subsequent forms (already loaded), so gCaptchaReadyCallback // never fires and captchaReady stays 0. Set it directly since grecaptcha IS available. var captchaDiv = FA__CONTAINER.querySelector('#g-recaptcha-render-div'); if (captchaDiv) { window.hasCaptchaRendered = false; window.isCreatingCaptcha = false; if (typeof grecaptcha !== 'undefined' && grecaptcha.enterprise && typeof grecaptcha.enterprise.render === 'function') { window.captchaReady = 1; } if (typeof disableSubmitButton === 'function') disableSubmitButton(); if (typeof wformsReadyCallback === 'function') wformsReadyCallback(); } // ALTCHA: the custom element auto-upgrades immediately on DOM insertion, so its load event // may fire before inline scripts attach a listener. Re-attach and handle the already-loaded case. var altchaWidget = FA__CONTAINER.querySelector('altcha-widget'); if (altchaWidget) { altchaWidget.addEventListener('load', function () { requestAnimationFrame(function () { altchaWidget.classList.add('visible'); }); }); if (altchaWidget.value) altchaWidget.classList.add('visible'); } } /** * Injects HTML into the container, injects styles into , then loads external scripts * sequentially before re-running inline scripts and reinitializing components. */ function FA__renderHtml(html, callback) { // Parse into a temporary element to separate node types var tmp = document.createElement('div'); tmp.innerHTML = html; FA__injectedStyles.forEach(function (el) { el.remove(); }); FA__injectedStyles = []; Array.from(tmp.querySelectorAll('link[rel="stylesheet"], style')).forEach(function (el) { var href = el.getAttribute('href'); if (href && document.querySelector('link[href="' + href + '"]')) return; var clone = el.cloneNode(true); document.head.appendChild(clone); FA__injectedStyles.push(clone); }); // Collect scripts in document order. Inline scripts can depend on a preceding