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.