PageSourceSearch

https://www.assent.com/wp-content/themes/assent-wp/assets/js/form-loading-spinner.js?ver=1790837021

js assent.com collected 2026-10-02 04:21:48 UTC 8,381 bytes, 224 lines download raw bytes

1(function() {
2  'use strict';
3
4  /* How long to wait for ZoomInfo FormComplete before showing the form anyway.
5     This is the intentional wait: FormComplete stamps the form and we would
6     rather show a stamped form than flash an empty one. */
7  const FALLBACK_TIMEOUT = 3000;
8
9  /* Cosmetic pause between "we have decided to reveal" and actually revealing.
10     This used to be a hard-coded 1000ms that ran on EVERY form, including forms
11     FormComplete had already finished with - a full second of dead time on a
12     perfectly healthy page load. Reveal is now immediate. If a fade is wanted,
13     do it with a CSS transition on .form-ready rather than by delaying here. */
14  const REVEAL_DELAY = 0;
15
16  const FORM_SELECTORS = [
17    'form[action="/form-handler"]',
18    'form.form--wrap',
19    'form[name="demoForm"]',
20    'form[data-formtype="demo"]',
21    'form[id*="pardot"]',
22    'form[id*="demo"]'
23  ].join(', ');
24
25  const pendingForms = new Map();
26
27  /* Set by ZoomInfo's _zi_fc.onReady (registered in wp_head, see
28     includes/theme-scripts.php). May already be true before this footer script
29     parses, which is why it is read rather than only listened for. */
30  function ziReady() {
31    return window.__assentZiReady === true;
32  }
33
34  /* "Nothing left to wait for" - a page-level flag, not a per-form class.
35     Form visibility used to depend entirely on .loaded sitting on the form
36     itself. Anything that replaces a form in the DOM strips that class, so a
37     VWO campaign swapping a form in re-hid every field; this script put .loaded
38     back; the campaign restored its own copy; repeat. Marking <html> instead
39     means a replaced form is visible immediately without this script touching
40     it, which removes the mutation the campaign was reacting to.
41     Paired with the html:not(.assent-forms-ready) guard in
42     assets/css/form-loading-spinner.css. */
43  function markGloballyReady() {
44    document.documentElement.classList.add('assent-forms-ready');
45  }
46
47  function globallyReady() {
48    return document.documentElement.classList.contains('assent-forms-ready');
49  }
50
51  function isZoomInfoReady(form) {
52    return form.hasAttribute('data-zi-mapped-form') ||
53           form.querySelector('input[name*="ZoomInfo"]') ||
54           form.querySelector('[data-zi-field]');
55  }
56
57  /**
58   * Reveal a form. Idempotent.
59   *
60   * Everything that hides the form is undone together, in one pass:
61   *   - inline styles written server-side by display_form() in functions.php
62   *   - the .form-loading class
63   *   - the spinner element
64   *   - the .loaded class, which is what form-loading-spinner.css:40
65   *     (form:not(.loaded) .form-field { display:none !important }) keys off
66   *
67   * Previously .form-ready was added immediately but everything else waited
68   * 1000ms, so the form sat in a half-revealed state where the spinner was gone
69   * from the logic's point of view but the fields were still display:none.
70   */
71  function revealForm(form, spinner, observer) {
72    if (form.classList.contains('form-ready')) return;
73    form.classList.add('form-ready');
74
75    if (observer) observer.disconnect();
76    pendingForms.delete(form);
77
78    const apply = function() {
79      form.classList.add('loaded');
80
81      if (!spinner) spinner = form.querySelector('.form-loading-spinner');
82      if (spinner && spinner.parentNode) spinner.remove();
83
84      form.classList.remove('form-loading');
85      form.style.removeProperty('visibility');
86      form.style.removeProperty('pointer-events');
87      form.style.removeProperty('position');
88      form.style.removeProperty('min-height');
89      form.style.removeProperty('max-height');
90      form.style.removeProperty('margin-top');
91      form.style.removeProperty('margin-bottom');
92
93      /* Measure after the browser has applied the removals above, otherwise
94         offsetHeight can be read against the old max-height:72px box. */
95      if (typeof globalGapMaker === 'function') {
96        requestAnimationFrame(function() {
97          const consentStmt = form.querySelector('.consent-stmt');
98          if (consentStmt) {
99            form.style.marginBottom = (30 + consentStmt.offsetHeight) + 'px';
100          }
101        });
102      }
103    };
104
105    if (REVEAL_DELAY > 0) setTimeout(apply, REVEAL_DELAY);
106    else apply();
107  }
108
109  /* Reveal every form on the page. Called by ZoomInfo's onReady callback. */
110  function revealAll() {
111    markGloballyReady();
112
113    pendingForms.forEach(function(entry, form) {
114      revealForm(form, entry.spinner, entry.observer);
115    });
116    document.querySelectorAll(FORM_SELECTORS).forEach(function(form) {
117      revealForm(form, form.querySelector('.form-loading-spinner'), null);
118    });
119  }
120
121  function initFormLoader(form) {
122    if (form.dataset.loaderInitialized) return;
123    form.dataset.loaderInitialized = 'true';
124
125    /* Already stamped, or FormComplete has finished everywhere: show it now.
126       The old code returned early when ZoomInfo was done WITHOUT revealing,
127       which left any form added after that point hidden by its inline styles
128       with no timer left to clear them. */
129    if (ziReady() || isZoomInfoReady(form)) {
130      revealForm(form, form.querySelector('.form-loading-spinner'), null);
131      return;
132    }
133
134    let spinner = form.querySelector('.form-loading-spinner');
135
136    if (!spinner) {
137      form.style.visibility = 'hidden';
138      form.style.pointerEvents = 'none';
139      form.style.position = 'relative';
140      form.style.minHeight = '72px';
141      form.style.maxHeight = '72px';
142      form.style.setProperty('margin-top', '24px', 'important');
143      form.style.marginBottom = '62px';
144      form.classList.add('form-loading');
145      spinner = document.createElement('div');
146      spinner.className = 'form-loading-spinner';
147      form.insertBefore(spinner, form.firstChild);
148    }
149
150    const observer = new MutationObserver(function() {
151      if (isZoomInfoReady(form)) revealForm(form, spinner, observer);
152    });
153    observer.observe(form, { childList: true, subtree: true, attributes: true });
154
155    pendingForms.set(form, { spinner: spinner, observer: observer });
156
157    setTimeout(function() {
158      revealForm(form, spinner, observer);
159    }, FALLBACK_TIMEOUT);
160  }
161
162  function initAllForms() {
163    document.querySelectorAll(FORM_SELECTORS).forEach(initFormLoader);
164  }
165
166  /* Only needed while we are still waiting for FormComplete: a form that shows
167     up after that needs no spinner, no timer and no observer, because the CSS
168     no longer hides it. Bailing out here is what stops this script and a VWO
169     campaign taking turns rewriting the same form. */
170  function watchForNewForms() {
171    new MutationObserver(function(mutations) {
172      if (globallyReady()) return;
173
174      mutations.forEach(function(mutation) {
175        mutation.addedNodes.forEach(function(node) {
176          if (node.nodeType !== Node.ELEMENT_NODE) return;
177          if (node.matches && node.matches(FORM_SELECTORS)) initFormLoader(node);
178          if (node.querySelectorAll) node.querySelectorAll(FORM_SELECTORS).forEach(initFormLoader);
179        });
180      });
181    }).observe(document.body, { childList: true, subtree: true });
182  }
183
184  /* Catch forms FormComplete stamped without a mutation the observer saw
185     (replaces the old checkIfZoomInfoAlreadyRan). */
186  function sweepStamped() {
187    document.querySelectorAll(FORM_SELECTORS).forEach(function(form) {
188      if (!isZoomInfoReady(form)) return;
189      const entry = pendingForms.get(form);
190      revealForm(form, entry ? entry.spinner : null, entry ? entry.observer : null);
191    });
192  }
193
194  function start() {
195    initAllForms();
196    watchForNewForms();
197    if (ziReady()) revealAll();
198    else sweepStamped();
199  }
200
201  /* initialization */
202  if (document.readyState === 'loading') {
203    document.addEventListener('DOMContentLoaded', start);
204  } else {
205    start();
206  }
207
208  /* Backstop: if FormComplete never reports ready, stop hiding fields anyway
209     after the same wait a single form would have used. Without this a page
210     where ZoomInfo fails outright would keep the CSS rule live indefinitely. */
211  setTimeout(markGloballyReady, FALLBACK_TIMEOUT);
212
213  window.addEventListener('load', function() {
214    initAllForms();
215    if (ziReady()) revealAll();
216    else sweepStamped();
217  });
218
219  window.AssentFormLoader = {
220    init: initAllForms,
221    initForm: initFormLoader,
222    revealAll: revealAll
223  };
224})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.