PageSourceSearch

https://brincdrones.com/wp-content/themes/bdrones/assets/js/hubspot-contact-forms/hero-cards.js?ver=1790164896

js brincdrones.com collected 2026-09-28 05:38:08 UTC 16,648 bytes, 483 lines download raw bytes

1class HeroCards {
2  constructor(section) {
3    this.section = section;
4    this.sectionAboveTitle = section.querySelector('.js-section-above-title');
5    this.sectionTitle = section.querySelector('.js-section-title');
6    this.stepsTitle = section.querySelector('.js-steps__title');
7    this.stepNumber = section.querySelector('.js-current-step-number');
8    this.currentStepWrapper = section.querySelector('.js-current-step-wrapper');
9    this.howManyStepsWrapper = section.querySelector('.js-how-many-steps-wrapper');
10    this.stepsList = section.querySelector('.js-steps-list');
11    this.stepsListItem = section.querySelectorAll('.js-steps-list-item');
12    this.howManySteps = section.querySelector('.js-how-many-steps');
13    this.backToFirstStepButton = section.querySelector('.js-back-to-first-step-button');
14    this.stepsLine = section.querySelector('.js-steps-line');
15    this.stepsRow = section.querySelector('.js-steps__row');
16    this.steps = section.querySelector('.js-steps');
17
18    this.activeTl = null;
19    this.activeForm = null;
20
21    this.sectionAnimation();
22    this.initHubspotForms();
23    this.bindEvents();
24    this.showForm();
25    this.backToFirstStep();
26    this.handleBackToChoose();
27  }
28
29  bindEvents() {
30    window.addEventListener('message', (event) => {
31      if (!event.data || event.data.type !== 'hsFormCallback') return;
32      if (event.data.eventName === 'onFormReady') this.initHubspotForms();
33      if (event.data.eventName === 'onFormSubmitted') this.handleFormSuccess(event.data.data);
34    });
35
36    const observer = new MutationObserver(() => this.initHubspotForms());
37    observer.observe(document.body, { childList: true, subtree: true });
38
39    window.addEventListener('resize', () => this.alignSubmitButtons());
40  }
41
42  alignSubmitButtons() {
43    this.section.querySelectorAll('.hs-form[data-hs-init]').forEach((form) => {
44      const richtext = form.querySelector('.form-columns-0:has(.hs-richtext)');
45      const submit = form.querySelector('.hs_submit.hs-submit');
46      if (!richtext || !submit) return;
47
48      const offset = (richtext.offsetHeight - submit.offsetHeight) / 2;
49      submit.style.marginTop = offset > 0 ? `${offset}px` : '0';
50    });
51  }
52
53  initHubspotForms() {
54    document.querySelectorAll('.hs-form:not([data-hs-init])').forEach((form) => {
55      form.setAttribute('data-hs-init', '1');
56      this.replaceSubmitInForm(form);
57      this.shuffleSelectOptions(form);
58      jQuery(form)
59        .find('select')
60        .each(function () {
61          jQuery(this).select2({ dropdownParent: jQuery(this).parent() });
62        });
63    });
64  }
65
66  shuffleSelectOptions(form) {
67    const shuffledFields = ['self_reported_source'];
68
69    shuffledFields.forEach((name) => {
70      const select = form.querySelector(`select[name="${name}"]`);
71      if (!select || select.querySelector('optgroup')) return;
72
73      const options = Array.from(select.options);
74      const pinnedTop = options.filter((opt) => opt.disabled || opt.value === '');
75      const pinnedBottom = options.filter((opt) => !pinnedTop.includes(opt) && this.isLastOption(opt));
76      const shuffled = options.filter((opt) => !pinnedTop.includes(opt) && !pinnedBottom.includes(opt));
77
78      for (let i = shuffled.length - 1; i > 0; i--) {
79        const j = Math.floor(Math.random() * (i + 1));
80        [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
81      }
82
83      const selectedValue = select.value;
84      const fragment = document.createDocumentFragment();
85      [...pinnedTop, ...shuffled, ...pinnedBottom].forEach((opt) => fragment.appendChild(opt));
86      select.appendChild(fragment);
87      select.value = selectedValue;
88    });
89  }
90
91  isLastOption(option) {
92    return option.value.trim().toLowerCase() === 'other';
93  }
94
95  replaceSubmitInForm(form) {
96    form.querySelectorAll('input[type="submit"]').forEach((input) => {
97      const btn = document.createElement('button');
98      btn.type = 'submit';
99      btn.textContent = input.value;
100      btn.className = input.className;
101
102      Array.from(input.attributes).forEach((attr) => {
103        if (attr.name !== 'type' && attr.name !== 'value' && attr.name !== 'class') {
104          btn.setAttribute(attr.name, attr.value);
105        }
106      });
107
108      input.replaceWith(btn);
109    });
110  }
111
112  showForm() {
113    const cards = this.section.querySelectorAll('.js-step-card');
114    if (!cards.length) return;
115
116    cards.forEach((card) => {
117      card.addEventListener('click', () => {
118        const noFormHash = card.getAttribute('data-no-form');
119        if (noFormHash) {
120          history.replaceState(null, '', `#${noFormHash}`);
121        }
122
123        const currentForm = this.section.querySelector(`[data-form-id='${card.getAttribute('data-form')}']`);
124
125        this.forScreenReaders({ form: currentForm, hide: false });
126        this.activeForm = currentForm;
127        if (this.stepNumber) this.stepNumber.textContent = '02';
128
129        const tl = gsap.timeline();
130        this.activeTl = tl;
131
132        tl.addLabel('start-animation');
133
134        if (currentForm.hasAttribute('data-submitted')) {
135          const headline = currentForm.querySelector('.js-form-headline');
136          const thankMess = currentForm.querySelector('.js-thank-mess-wrapper');
137
138          [this.stepsTitle, this.stepsList, this.howManySteps, this.stepsRow].filter(
138Boolean).forEach((el) => {
139            tl.to(
140              el,
141              { opacity: 0, duration: 0.35, ease: 'power2.in', onComplete: () => (el.style.display = 'none') },
142              'start-animation'
143            );
144          });
145
146          if (this.stepsLine) {
147            tl.fromTo(
148              this.stepsLine,
149              { '--line-width': '50%' },
150              { '--line-width': '100%', duration: 1.2, ease: 'power2.out' },
151              'start-animation'
152            );
153          }
154
155          tl.addLabel('show-thanks', 'start-animation+=0.35');
156
157          if (headline) tl.set(headline, { display: 'none' }, 'show-thanks');
158          tl.set(currentForm, { clearProps: 'opacity', display: 'block' }, 'show-thanks');
159
160          if (this.backToFirstStepButton) {
161            tl.set(this.backToFirstStepButton, { display: 'flex' }, 'show-thanks');
162            tl.fromTo(
163              this.backToFirstStepButton,
164              { opacity: 0 },
165              { opacity: 1, duration: 0.35, ease: 'power2.out' },
166              'show-thanks'
167            );
168          }
169          if (thankMess) {
170            tl.set(thankMess, { display: 'block' }, 'show-thanks');
171            tl.fromTo(thankMess, { opacity: 0 }, { opacity: 1, duration: 0.4, ease: 'power2.out' }, 'show-thanks');
172          }
173          return;
174        }
175
176        if (this.stepsTitle) {
177          tl.to(
178            this.stepsTitle,
179            {
180              opacity: 0,
181              duration: 0.35,
182              ease: 'power2.in',
183              onComplete: () => (this.stepsTitle.style.display = 'none')
184            },
185            'start-animation'
186          );
187        }
188        if (this.stepsList) {
189          tl.to(
190            this.stepsList,
191            {
192              opacity: 0,
193              duration: 0.35,
194              ease: 'power2.in',
195              onComplete: () => (this.stepsList.style.display = 'none')
196            },
197            'start-animation'
198          );
199        }
200        if (this.howManySteps) {
201          tl.to(
202            this.howManySteps,
203            {
204              opacity: 0,
205              duration: 0.35,
206              ease: 'power2.in',
207              onComplete: () => (this.howManySteps.style.display = 'none')
208            },
209            'start-animation'
210          );
211        }
212        if (this.stepsLine) {
213          tl.fromTo(
214            this.stepsLine,
215            { '--line-width': '50%' },
216            { '--line-width': '100%', duration: 1.2, ease: 'power2.out' },
217            'start-animation'
218          );
219        }
220
221        tl.addLabel('show-animation', 'start-animation+=0.35');
222
223        if (this.backToFirstStepButton) {
224          tl.set(this.backToFirstStepButton, { display: 'flex' }, 'show-animation');
225          tl.fromTo(
226            this.backToFirstStepButton,
227            { opacity: 0 },
228            { opacity: 1, duration: 0.35, ease: 'power2.out' },
229            'show-animation'
230          );
231        }
232        if (currentForm) {
233          tl.set(currentForm, { display: 'block' }, 'show-animation');
234          tl.fromTo(
235            currentForm,
236            { opacity: 0 },
237            {
238              opacity: 1,
239              duration: 0.4,
240              ease: 'power2.out',
241              onComplete: () => this.alignSubmitButtons()
242            },
243            'show-animation'
244          );
245        }
246      });
247    });
248  }
249
250  backToFirstStep() {
251    if (!this.backToFirstStepButton) return;
252
253    this.backToFirstStepButton.addEventListener('click', () => {
254      history.replaceState(null, '', window.location.pathname + window.location.search);
255
256      if (this.activeTl) {
257        this.activeTl.kill();
258        this.activeTl = null;
259      }
260
261      const formToHide = this.activeForm;
262      this.activeForm = null;
263
264      if (formToHide) this.forScreenReaders({ form: formToHide, hide: true });
265      if (this.stepNumber) this.stepNumber.textContent = '01';
266
267      const tl = gsap.timeline();
268      tl.addLabel('hide-form');
269
270      if (formToHide) {
271        tl.to(
272          formToHide,
273          { opacity: 0, duration: 0.35, ease: 'power2.in', onComplete: () => (formToHide.style.display = 'none') },
274          'hide-form'
275        );
276      }
277      if (this.backToFirstStepButton) {
278        tl.to(
279          this.backToFirstStepButton,
280          {
281            opacity: 0,
282            duration: 0.35,
283            ease: 'power2.in',
284            onComplete: () => (this.backToFirstStepButton.style.display = 'none')
285          },
286          'hide-form'
287        );
288      }
289      if (this.stepsLine) {
290        tl.to(this.stepsLine, { '--line-width': '50%', duration: 0.5, ease: 'power2.in' }, 'hide-form');
291      }
292
293      tl.addLabel('show-steps', 'hide-form+=0.35');
294
295      if (this.stepsTitle) {
296        tl.set(this.stepsTitle, { display: '' }, 'show-steps');
297        tl.fromTo(this.stepsTitle, { opacity: 0 }, { opacity: 1, duration: 0.35, ease: 'power2.out' }, 'show-steps');
298      }
299      if (this.stepsList) {
300        tl.set(this.stepsList, { display: '' }, 'show-steps');
301        tl.fromTo(this.stepsList, { opacity: 0 }, { opacity: 1, duration: 0.35, ease: 'power2.out' }, 'show-steps');
302      }
303      if (this.howManySteps) {
304        tl.set(this.howManySteps, { display: '' }, 'show-steps');
305        tl.fromTo(this.howManySteps, { opacity: 0 }, { opacity: 1, duration: 0.35, ease: 'power2.out' }, 'show-steps');
306      }
307    });
308  }
309
310  handleFormSuccess(formData) {
311    if (!this.activeForm) return;
312
313    if (this.activeTl) {
314      this.activeTl.kill();
315      this.activeTl = null;
316    }
317
318    const headline = this.activeForm.querySelector('.js-form-headline');
319    const thankMess = this.activeForm.querySelector('.js-thank-mess-wrapper');
320
321    if (thankMess && Array.isArray(formData)) {
322      const nameField = formData.find((f) => f.name === 'firstname') || formData.find((f) => f.name === 'name');
323      if (nameField && nameField.value) {
324        const titleEl = thankMess.querySelector('.js-thank-mess-wrapper-title');
325        if (titleEl) titleEl.textContent = nameField.value;
326      }
327    }
328
329    const tl = gsap.timeline();
330    tl.addLabel('hide-parts');
331
332    if (headline) {
333      tl.to(
334        headline,
335        { opacity: 0, duration: 0.3, ease: 'power2.in', onComplete: () => (headline.style.display = 'none') },
336        'hide-parts'
337      );
338    }
339    if (this.stepsRow) {
340      tl.to(
341        this.stepsRow,
342        { opacity: 0, duration: 0.3, ease: 'power2.in', onComplete: () => (this.stepsRow.style.display = 'none') },
343        'hide-parts'
344      );
345    }
346
347    tl.addLabel('show-thanks', 'hide-parts+=0.3');
348
349    if (thankMess) {
350      this.activeForm.setAttribute('data-submitted', '1');
351      const formHash = this.activeForm.getAttribute('data-formhash');
352      if (formHash) history.replaceState(null, '', `#${formHash}`);
353      tl.set(thankMess, { display: 'block' }, 'show-thanks');
354      tl.fromTo(thankMess, { opacity: 0 }, { opacity: 1, duration: 0.4, ease: 'power2.out' }, 'show-thanks');
355    }
356
357    tl.call(
358      () => {
359        this.section.scrollIntoView({ behavior: 'smooth', block: 'start' });
360      },
361      [],
362      'show-thanks+=0.4'
363    );
364  }
365
366  handleBackToChoose() {
367    this.section.addEventListener('click', (e) => {
368      if (!e.target.closest('.js-back-to-choose')) return;
369
370      history.replaceState(null, '', window.location.pathname + window.location.search);
371
372      if (this.activeTl) {
373        this.activeTl.kill();
374        this.activeTl = null;
375      }
376
377      const formToReset = this.activeForm;
378      this.activeForm = null;
379
380      if (formToReset) this.forScreenReaders({ form: formToReset, hide: true });
381      if (this.stepNumber) this.stepNumber.textContent = '01';
382
383      const tl = gsap.timeline();
384      tl.addLabel('hide-thanks');
385
386      if (formToReset) {
387        const thankMess = formToReset.querySelector('.js-thank-mess-wrapper');
388        if (thankMess) {
389          tl.to(
390            thankMess,
391            {
392              opacity: 0,
393              duration: 0.3,
394              ease: 'power2.in',
395              onComplete: () => {
396                thankMess.style.display = 'none';
397                const headline = formToReset.querySelector('.js-form-headline');
398                if (headline) gsap.set(headline, { clearProps: 'all' });
399                formToReset.style.display = 'none';
400                gsap.set(formToReset, { opacity: 1 });
401              }
402            },
403            'hide-thanks'
404          );
405        }
406      }
407      if (this.backToFirstStepButton) {
408        tl.to(
409          this.backToFirstStepButton,
410          {
411            opacity: 0,
412            duration: 0.3,
413            ease: 'power2.in',
414            onComplete: () => (this.backToFirstStepButton.style.display = 'none')
415          },
416          'hide-thanks'
417        );
418      }
419
420      tl.addLabel('show-steps', 'hide-thanks+=0.3');
421
422      if (this.stepsRow) {
423        tl.set(this.stepsRow, { display: '' }, 'show-steps');
424        tl.fromTo(this.stepsRow, { opacity: 0 }, { opacity: 1, duration: 0.35, ease: 'power2.out' }, 'show-steps');
425      }
426      if (this.stepsTitle) {
427        tl.set(this.stepsTitle, { display: '' }, 'show-steps');
428        tl.fromTo(this.stepsTitle, { opacity: 0 }, { opacity: 1, duration: 0.35, ease: 'power2.out' }, 'show-steps');
429      }
430      if (this.stepsList) {
431        tl.set(this.stepsList, { display: '' }, 'show-steps');
432        tl.fromTo(this.stepsList, { opacity: 0 }, { opacity: 1, duration: 0.35, ease: 'power2.out' }, 'show-steps');
433      }
434      if (this.howManySteps) {
435        tl.set(this.howManySteps, { display: '' }, 'show-steps');
436        tl.fromTo(this.howManySteps, { opacity: 0 }, { opacity: 1, duration: 0.35, ease: 'power2.out' }, 'show-steps');
437      }
438      if (this.stepsLine) {
439        tl.set(this.stepsLine, { '--line-width': '50%' }, 'show-steps');
440      }
441    });
442  }
443
444  forScreenReaders({ form, hide = true }) {
445    if (hide) {
446      form.setAttribute('aria-hidden', 'true');
447      form.setAttribute('tabindex', '-1');
448      form.style.pointerEvents = 'none';
449    } else {
450      form.setAttribute('aria-hidden', 'false');
451      form.setAttribute('tabindex', '0');
452      form.style.pointerEvents = 'all';
453    }
454  }
455
456  sectionAnimation() {
457    const sectionTl = gsap.timeline();
458
459    sectionTl.addLabel('start-section-animation');
460    this.sectionAboveTitle && sectionTl.fadeInBlur_fromTo(this.sectionAboveTitle, {}, 0);
461    this.sectionTitle && sectionTl.fadeInBlur_fromTo(this.sectionTitle, {}, 0);
462    this.steps && sectionTl.fadeInBlur_fromTo(this.steps, {}, 0);
463    this.stepsLine &&
464      sectionTl.fromTo(
465        this.stepsLine,
466        { '--line-width': '0' },
467        { '--line-width': '50%' },
468        'start-section-animation+=0.3'
469      );
470    this.stepsTitle && sectionTl.fadeInBlur_fromTo(this.stepsTitle, {}, 'start-section-animation+=0.4');
471    this.stepsListItem.length &&
472      sectionTl.fadeIn(this.stepsListItem, { stagger: 0.08 }, 'start-section-animation+=0.5');
473    this.currentStepWrapper &&
474      sectionTl.fadeInBlur_fromTo(this.currentStepWrapper, {}, 'start-section-animation+=0.25');
475    this.howManyStepsWrapper &&
476      sectionTl.fadeInBlur_fromTo(this.howManyStepsWrapper, {}, 'start-section-animation+=0.25');
477  }
478}
479
480document.addEventListener('DOMContentLoaded', () => {
481  const section = document.querySelector('.hero-cards');
482  if (section) new HeroCards(section);
483});

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.