1let megaMenuTimeout; 2let stickyAside; 3let stickyAsideObserver; 4let stickyAsideUpdateFrame; 5const tmThemeStrings = window.tmThemeI18n || {}; 6const tmCurrentLanguage = window.tmThemeLanguage || ''; 7const tmLanguageSuggestionConfig = window.tmLanguageSuggestion || {}; 8const tmBioDigitalApiUrl = 'https://developer.biodigital.com/builds/api/2/human-api.min.js?ver=1.0.0'; 9let tmBiodigitalLoadPromise; 10let tmArticleModelLoadTriggered = false; 11let tmArticleModelInitialized = false; 12let tmLanguageSuggestionBanner; 13let tmLanguageSuggestionServerData; 14let tmLanguageSuggestionInitialised = false; 15 16function tmThemeText(key) { 17 return tmThemeStrings[key] || ''; 18} 19 20function scheduleStickyAsideUpdate() { 21 if (!stickyAside) { 22 return; 23 } 24 25 if (stickyAsideUpdateFrame) { 26 cancelAnimationFrame(stickyAsideUpdateFrame); 27 } 28 29 stickyAsideUpdateFrame = requestAnimationFrame(function () { 30 stickyAside.update(); 31 }); 32} 33 34function observeStickyAsideContainer() { 35 const container = document.querySelector('[data-sticky-container]'); 36 37 if (!container || typeof window.ResizeObserver === 'undefined') { 38 return; 39 } 40 41 stickyAsideObserver = new ResizeObserver(scheduleStickyAsideUpdate); 42 stickyAsideObserver.observe(container); 43} 44 45function ensureBiodigitalApiLoaded() { 46 if (typeof window.HumanAPI !== 'undefined') { 47 return Promise.resolve(window.HumanAPI); 48 } 49 50 if (tmBiodigitalLoadPromise) { 51 return tmBiodigitalLoadPromise; 52 } 53 54 tmBiodigitalLoadPromise = new Promise((resolve, reject) => { 55 const existingScript = document.querySelector('script[data-tm-biodigital-loader="true"], script[src*="developer.biodigital.com/builds/api/2/human-api.min.js"]'); 56 57 const handleLoad = () => { 58 if (typeof window.HumanAPI !== 'undefined') { 59 resolve(window.HumanAPI); 60 return; 61 } 62 63 tmBiodigitalLoadPromise = undefined; 64 reject(new Error('BioDigital API unavailable')); 65 }; 66 67 const handleError = () => { 68 tmBiodigitalLoadPromise = undefined; 69 reject(new Error('Failed to load BioDigital API')); 70 }; 71 72 if (existingScript) { 73 existingScript.addEventListener('load', handleLoad, { once: true }); 74 existingScript.addEventListener('error', handleError, { once: true }); 75 return; 76 } 77 78 const script = document.createElement('script'); 79 script.src = tmBioDigitalApiUrl; 80 script.async = true; 81 script.defer = true; 82 script.dataset.tmBiodigitalLoader = 'true'; 83 script.addEventListener('load', handleLoad, { once: true }); 84 script.addEventListener('error', handleError, { once: true }); 85 document.head.appendChild(script); 86 }); 87 88 return tmBiodigitalLoadPromise; 89} 90 91function initializeArticleModelIfReady() { 92 const iframe = document.getElementById('embedded-human'); 93 94 if (!iframe || !iframe.dataset.biodigitalSrc || !iframe.getAttribute('src')) { 95 return; 96 } 97 98 if (tmArticleModelInitialized || typeof window.HumanAPI === 'undefined') { 99 return; 100 } 101 102 tmArticleModelInitialized = true; 103 setupModel('embedded-human'); 104} 105 106function loadArticleBioDigitalModel() { 107 const iframe = document.getElementById('embedded-human'); 108 109 if (!iframe || !iframe.dataset.biodigitalSrc) { 110 return Promise.resolve(false); 111 } 112 113 if (!iframe.getAttribute('src')) { 114 iframe.setAttribute('src', iframe.dataset.biodigitalSrc); 115 } 116 117 return ensureBiodigitalApiLoaded() 118 .then(() => { 119 initializeArticleModelIfReady(); 120 return true; 121 }) 122 .catch(() => false); 123} 124 125function setupArticleBioDigitalModel() { 126 const iframe = document.getElementById('embedded-human'); 127 128 if (!iframe || !iframe.dataset.biodigitalSrc) { 129 return; 130 } 131 132 const triggerLoad = () => { 133 if (tmArticleModelLoadTriggered) { 134 return; 135 } 136 137 tmArticleModelLoadTriggered = true; 138 loadArticleBioDigitalModel(); 139 }; 140 141 if (typeof window.IntersectionObserver === 'undefined') { 142 triggerLoad(); 143 return; 144 } 145 146 const modelContainer = document.getElementById('model-container'); 147 if (!modelContainer) { 148 triggerLoad(); 149 return; 150 } 151 152 const observer = new IntersectionObserver((entries) => {
153 entries.forEach((entry) => { 154 if (entry.isIntersecting) { 155 observer.disconnect(); 156 triggerLoad(); 157 } 158 }); 159 }, { 160 rootMargin: '100px 0px' 161 }); 162 163 observer.observe(modelContainer); 164} 165 166function tmLanguageSuggestionNormaliseLanguage(language) { 167 return (language || '').toString().trim().toLowerCase().replace(/_/g, '-'); 168} 169 170function tmLanguageSuggestionIsDismissed() { 171 try { 172 return window.localStorage.getItem('tm_language_suggestion_dismissed') === '1'; 173 } catch (error) { 174 return false; 175 } 176} 177 178function tmLanguageSuggestionGetPreferredLanguage() { 179 if (!tmLanguageSuggestionConfig.languages || typeof tmLanguageSuggestionConfig.languages !== 'object') { 180 return ''; 181 } 182 183 let browserLanguages = Array.isArray(navigator.languages) ? navigator.languages : []; 184 if (!browserLanguages.length && navigator.language) { 185 browserLanguages = [navigator.language]; 186 } 187 188 const supportedLanguages = Object.keys(tmLanguageSuggestionConfig.languages); 189 190 for (const browserLanguage of browserLanguages) { 191 const normalisedLanguage = tmLanguageSuggestionNormaliseLanguage(browserLanguage); 192 if (!normalisedLanguage) { 193 continue; 194 } 195 196 const exactMatch = supportedLanguages.find(function (language) { 197 return tmLanguageSuggestionNormaliseLanguage(language) === normalisedLanguage; 198 }); 199 if (exactMatch) { 200 return exactMatch; 201 } 202 203 const primaryLanguage = normalisedLanguage.split('-')[0]; 204 const primaryMatch = supportedLanguages.find(function (language) { 205 return tmLanguageSuggestionNormaliseLanguage(language) === primaryLanguage; 206 }); 207 if (primaryMatch) { 208 return primaryMatch; 209 } 210 211 const regionalMatches = supportedLanguages.filter(function (language) { 212 return tmLanguageSuggestionNormaliseLanguage(language).split('-')[0] === primaryLanguage; 213 }); 214 if (regionalMatches.length === 1) { 215 return regionalMatches[0]; 216 } 217 } 218 219 return ''; 220} 221 222function tmLanguageSuggestionSyncOffset() { 223 let height = 0; 224 225 if (tmLanguageSuggestionBanner && tmLanguageSuggestionBanner.length && !tmLanguageSuggestionBanner.prop('hidden')) { 226 const adminBar = jQuery('#wpadminbar'); 227 const reservedTop = adminBar.length ? adminBar.outerHeight() : 0; 228 const bannerBottom = tmLanguageSuggestionBanner[0].getBoundingClientRect().bottom; 229 height = Math.max(0, Math.min(tmLanguageSuggestionBanner.outerHeight(), bannerBottom - reservedTop)); 230 } 231 232 jQuery(':root').css('--tm-language-suggestion-height', height + 'px'); 233} 234 235function tmLanguageSuggestionHide() { 236 if (!tmLanguageSuggestionBanner || !tmLanguageSuggestionBanner.length) { 237 return; 238 } 239 240 tmLanguageSuggestionBanner.prop('hidden', true).attr('aria-hidden', 'true'); 241 jQuery('body').removeClass('tm-language-suggestion-visible'); 242 tmLanguageSuggestionSyncOffset(); 243 tmUpdateSubjectsMenuPosition(); 244} 245 246function tmLanguageSuggestionPersistDismissal(keepalive) { 247 if (!tmLanguageSuggestionServerData || !tmLanguageSuggestionServerData.nonce) { 248 return; 249 } 250 251 const requestData = 'action=tm_dismiss_language_suggestion&nonce=' + encodeURIComponent(tmLanguageSuggestionServerData.nonce); 252 253 if (window.fetch) { 254 window.fetch(ajaxurl, { 255 method: 'POST', 256 credentials: 'same-origin', 257 headers: { 258 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' 259 }, 260 body: requestData, 261 keepalive: !!keepalive 262 }).then(function (response) { 263 return response.json(); 264 }).then(function (response) { 265 if (response.success && response.data && response.data.dismissed) { 266 tmLanguageSuggestionServerData.dismissed = true; 267 } 268 }).catch(function () { 269 // Local storage retains the dismissal until the next authenticated sync. 270 }); 271 return; 272 } 273 274 jQuery.post(ajaxurl, requestData).done(function (response) { 275 if (response.success && response.data && response.data.dismissed) { 276 tmLanguageSuggestionServerData.dismissed = true; 277 } 278 }); 279} 280 281function tmLanguageSuggestionDismiss(destination) { 282 try { 283 window.localStorage.setItem('tm_language_suggestion_dismissed', '1'); 284 } catch (error) { 285 // The signed-in preference is still saved server-side where available. 286 } 287 288 tmLanguageSuggestionHide(); 289 tmLanguageSuggestionPersistDismissal(!!destination); 290 291 if (destination) { 292 window.location.assign(destination); 293 } 294} 295 296function tmLanguageSuggestionShowForLanguage(languageCode) { 297 if (!tmLanguageSuggestionBanner || !tmLanguageSuggestionBanner.length || !tmLanguageSuggestionConfig.languages) { 298 return; 299 } 300 301 if ((tmLanguageSuggestionServerData && tmLanguageSuggestionServerData.dismissed) || tmLanguageSuggestionIsDismissed()) { 302 return; 303 } 304 305 if (!languageCode || languageCode === tmLanguageSuggestionConfig.currentLanguage) { 306 return; 307 } 308 309 const language = tmLanguageSuggestionConfig.languages[languageCode]; 310 if (!language || !language.url || !language.label) { 311 return; 312 } 313 314 const messages = language.messages || {}; 315 const messageElement = jQuery('#tm-language-suggestion-message'); 316 const continueElement = jQuery('#tm-language-suggestion-continue'); 317 const dismissElement = jQuery('#tm-language-suggestion-dismiss'); 318 319 if (!messages.message || !messages.continue || !messageElement.length || !continueElement.length || !dismissElement.length) { 320 return; 321 } 322 323 messageElement.text(messages.message); 324 continueElement.text(messages.continue).attr('href', language.url); 325 dismissElement.attr('aria-label', messages.dismissLabel || 'Dismiss'); 326 dismissElement.find('.visually-hidden').text(messages.dismissLabel || 'Dismiss'); 327 tmLanguageSuggestionBanner.prop('hidden', false).attr('aria-hidden', 'false'); 328 jQuery('body').addClass('tm-language-suggestion-visible'); 329 tmLanguageSuggestionSyncOffset(); 330 tmUpdateSubjectsMenuPosition(); 331} 332 333function tmLanguageSuggestionInitialise() { 334 if (tmLanguageSuggestionInitialised || !tmLanguageSuggestionConfig.languages || Object.keys(tmLanguageSuggestionConfig.languages).length < 2) { 335 return; 336 } 337 338 if (jQuery('body').hasClass('teachme-app-view') || /TeachMe.*-iOS|TeachMe.*-Android/.test(navigator.userAgent)) { 339 return; 340 } 341 342 tmLanguageSuggestionBanner = jQuery('#tm-language-suggestion'); 343 if (!tmLanguageSuggestionBanner.length) { 344 return; 345 } 346
347 tmLanguageSuggestionInitialised = true; 348 349 const continueElement = jQuery('#tm-language-suggestion-continue'); 350 const dismissElement = jQuery('#tm-language-suggestion-dismiss'); 351 continueElement.on('click', function (event) { 352 event.preventDefault(); 353 tmLanguageSuggestionDismiss(jQuery(this).prop('href')); 354 }); 355 dismissElement.on('click', function () { 356 tmLanguageSuggestionDismiss(); 357 }); 358 359 if (window.ResizeObserver) { 360 new ResizeObserver(tmLanguageSuggestionSyncOffset).observe(tmLanguageSuggestionBanner[0]); 361 } 362 jQuery(window).on('resize scroll', tmLanguageSuggestionSyncOffset); 363 364 if (jQuery('body').hasClass('logged-in')) { 365 return; 366 } 367 368 tmLanguageSuggestionShowForLanguage(tmLanguageSuggestionGetPreferredLanguage()); 369} 370 371function tmLanguageSuggestionSetServerData(data) { 372 if (!tmLanguageSuggestionInitialised || !data || typeof data !== 'object') { 373 return; 374 } 375 376 tmLanguageSuggestionServerData = { 377 dismissed: !!data.dismissed || tmLanguageSuggestionIsDismissed(), 378 nonce: data.nonce || '' 379 }; 380 381 if (tmLanguageSuggestionServerData.dismissed && !data.dismissed) { 382 tmLanguageSuggestionPersistDismissal(false); 383 } 384 385 tmLanguageSuggestionShowForLanguage(tmLanguageSuggestionGetPreferredLanguage()); 386} 387 388jQuery(document).ready(function () { 389 createMobileMenu(); 390 setup3DModel(); 391 setupReviews(); 392 tmLanguageSuggestionInitialise(); 393 updateContent(); 394 setupStepNavigation(); 395 396 jQuery('a[href^="#"]').on('click', function (e) { 397 if (jQuery(this).attr('href') == '#') return; 398 399 const target = jQuery(jQuery(this).attr('href')); 400 401 if (target.length) { 402 e.preventDefault(); 403 404 jQuery('html, body').animate({ scrollTop: target.offset().top - jQuery('header').outerHeight() }, 250, 'linear'); 405 } 406 }); 407 408 if (jQuery("[data-bs-target='" + location.hash.replace('#', '#tab-') + "']").length > 0) { 409 new bootstrap.Tab(jQuery("[data-bs-target='" + location.hash.replace('#', '#tab-') + "']").get(0)).show() 410 } 411 412 if (jQuery('.subjects-menu-ul').children().length == 0) { 413 jQuery('.subjects-menu, .subjects-mobile-container').remove(); 414 jQuery('header').removeClass('pb-lg-5'); 415 } 416 417 if (jQuery('[rel="lightbox"]').length > 0) 418 jQuery('[rel="lightbox"]').magnificPopup({ 419 type: 'image' 420 }); 421 422 jQuery('.print-page').on('click', function (e) { 423 e.preventDefault(); 424 window.print(); 425 }); 426 427 jQuery('.shop_table').removeClass('shop_table').addClass('table table'); 428 429 jQuery('.rate').click(function () { 430 jQuery(this).prevAll('.rate').addClass('selected'); 431 jQuery(this).addClass('selected'); 432 jQuery(this).nextAll('.rate').removeClass('selected'); 433 }); 434 435 jQuery('.rate-form').submit(function (e) { 436 e.preventDefault(); 437 if (jQuery(this).find('.rate.selected').length > 0) { 438 if (jQuery(this).data('submitting')) { 439 return; 440 } 441 442 jQuery('.rate-form .alert').remove(); 443 if (jQuery('.rate-form .spinner-border').length === 0) { 444 jQuery('.rate-form button[type="submit"]').before('<div class="spinner-border spinner-border-sm me-4" role="status"></div>'); 445 } 446 jQuery('.rate-form fieldset').prop('disabled', true); 447 jQuery('.rate-form button[type="submit"]').prop('disabled', true); 448 449 if (typeof turnstile !== 'undefined' && window.turnstileRatingWidget) { 450 turnstile.execute(window.turnstileRatingWidget, { 451 callback: function (token) { 452 rateSubmit(token); 453 } 454 }); 455 } else { 456 jQuery('.rate-form .spinner-border').remove(); 457 jQuery('.rate-form fieldset').prop('disabled', false); 458 jQuery('.rate-form button[type="submit"]').prop('disabled', false); 459 jQuery('.rate-form').prepend( 460 jQuery('<div />', { 461 'class': 'alert alert-danger', 462 text: tmThemeText('captchaUnavailable') || 'Captcha not available' 463 }) 464 ); 465 } 466 } else { 467 jQuery('.rate-form').prepend( 468 jQuery('<div />', { 469 'class': 'alert alert-danger', 470 text: tmThemeText('ratingRequired') 471 }) 472 ); 473 } 474 }); 475 476 jQuery('.expandable-content .read-more').on('click', function (e) { 477 e.preventDefault(); 478 let content = jQuery(this).siblings('.content'); 479 if (jQuery(this).parent().hasClass('expanded')) { 480 let ogh = content.data('original-height'); 481 content.animate({ 482 height: ogh 483 }, 500, function () { 484 content.removeAttr('style'); 485 }); 486 jQuery(this).text(tmThemeText('readMore')); 487 } else { 488 let ogh = content.height(); 489 content.height('auto').data('original-height', ogh); 490 let h = content.height(); 491 content.removeAttr('style'); 492 content.animate({ 493 height: h 494 }, 500); 495 jQuery(this).text(tmThemeText('readLess')); 496 } 497 jQuery(this).parent().toggleClass('expanded'); 498 }); 499 500 jQuery('.mobile-menu .has-sub-menu > a').on('click', function (e) { 501 e.preventDefault(); 502 let parent = jQuery(this).parents('li:first') 503 let subMenu = jQuery(this).siblings('.sub-menu'); 504 subMenu.removeClass('d-none'); 505 setTimeout(() => { 506 subMenu.addClass('shown'); 507 setTimeout(() => { 508 parent.siblings('li').addClass('d-none'); 509 }, 300); 510 }, 1); 511 }); 512 513 jQuery('.mobile-menu .menu-back').on('click', function (e) { 514 e.preventDefault(); 515 let parent = jQuery(this).parents('.has-sub-menu:first'); 516 let subMenu = jQuery(this).parents('.sub-menu:first'); 517 518 parent.siblings('li').removeClass('d-none'); 519 subMenu.removeClass('shown'); 520 setTimeout(() => { 521 subMenu.addClass('d-none'); 522 }, 300); 523 }); 524 525 jQuery('.menu-reveal').on('click', function (e) { 526 e.preventDefault(); 527 jQuery('body').addClass('mobile-menu-shown'); 528 jQuery('.mobile-menu').addClass('shown'); 529 setTimeout(() => { 530 jQuery('.mobile-menu').addClass('show-menu'); 531 }, 1); 532 }); 533 534 jQuery('.menu-close').on('click', function (e) { 535 e.preventDefault(); 536 jQuery('body').removeClass('mobile-menu-shown'); 537 jQuery('.mobile-menu').removeClass('show-menu'); 538 setTimeout(() => { 539 jQuery('.mobile-menu').removeClass('shown'); 540 }, 500); 541 }); 542 543 jQuery('.search-reveal').on('click', function (e) { 544 e.preventDefault(); 545 jQuery(this).toggleClass('shown'); 546 jQuery('.mobile-search').toggleClass('shown'); 547 548 if (jQuery(this).hasClass('shown')) { 549 jQuery('body').addClass('with-overlay'); 550 setTimeout(() => { 551 jQuery('body').addClass('search-shown'); 552 }, 1); 553 } else { 554 jQuery('body').removeClass('search-shown'); 555 setTimeout(() => { 556 jQuery('body').removeClass('with-overlay'); 557 }, 1); 558 } 559 }); 560 561 jQuery('.subjects-menu-ul > li > a').on('click', function (e) { 562 if (jQuery(this).data('id') == 0) return; 563 564 const megaMenu = jQuery('.mega-menu'); 565 const menuId = jQuery(this).data('id'); 566 const currentParent = megaMenu.find('[data-parent]:not(.d-none)').data('parent'); 567 568 if (megaMenu.is(':visible') && currentParent == menuId) { 569 return; 570 } 571 572 e.preventDefault(); 573 jQuery('.mega-menu [data-parent="' + menuId + '"]').removeClass('d-none').siblings().addClass('d-none'); 574 megaMenu.fadeIn(200); 575 }); 576 577 jQuery('.mega-menu-close-icon').on('click', function () { 578 jQuery('.mega-menu').fadeOut(200); 579 }); 580 581 jQuery(document).on('click', function (e) { 582 if (jQuery('.mega-menu').is(':visible')) { 583 if (!jQuery(e.target).closest('.mega-menu').length && 584 !jQuery(e.target).closest('.subjects-menu-ul').length) { 585 jQuery('.mega-menu-close-icon').trigger('click'); 586 } 587 } 588 }); 589 590 jQuery('.search-form-control input').on('focus', function () { 591 jQuery('body').addClass('with-overlay'); 592 setTimeout(() => { 593 jQuery('body').addClass('search-shown'); 594 }, 1); 595 }); 596 597 jQuery('body').on('click', function (e) { 598 if (!jQuery('body').hasClass('with-overlay')) return; 599 600 const clickedWithinSearchUi = jQuery(e.target).closest('.search-form-control, .mobile-search, .search-reveal, .close-search-focus').length > 0; 601 602 if (!clickedWithinSearchUi) { 603 jQuery('.close-search-focus').trigger('click'); 604 } 605 }); 606 607 jQuery('.close-search-focus').on('click', function (e) { 608 e.preventDefault(); 609 jQuery('body').removeClass('search-shown'); 610 setTimeout(() => { 611 jQuery('body').removeClass('with-overlay'); 612 }, 1); 613 }); 614 615 jQuery('.content-menu li.has-children > span').on('click', function (e) { 616 e.preventDefault(); 617 jQuery(this).parent().toggleClass('active'); 618 if (jQuery(this).parent().hasClass('active')) 619 jQuery(this).text('-'); 620 else 621 jQuery(this).text('+'); 622 }); 623 624 if (jQuery('aside .content-menu').length > 0) { 625 stickyAside = new Sticky('aside .content-menu'); 626 observeStickyAsideContainer(); 627 } 628 629 if (jQuery('.owl-carousel').length > 0) { 630 window.dissectionCarousel = jQuery('.owl-carousel').owlCarousel({ 631 loop: true, 632 nav: true, 633 dots: true, 634 autoHeight: true, 635 items: 1, 636 navText: [ 637 '<i class="fa-solid fa-chevron-left"></i>', 638 '<i class="fa-solid fa-chevron-right"></i>' 639 ] 640 }); 641 // Listen to events only for .diessction-images-carousel 642 jQuery('.diessction-images-carousel').on('click', '.owl-prev, .owl-next', function () { 643 const modalId = '#dissectionImageModal'; 644 const modalElement = jQuery(modalId); 645 646 if (modalElement.length > 0) { 647 modalElement.addClass('show'); 648 } else { 649 console.error(`Modal with ID ${modalId} not found.`); 650 } 651 }); 652 } 653 jQuery('.disable-3d-modal').on('click', function (e) { 654 const modalId = '#3dModalPremium'; 655 const modalElement = jQuery(modalId); 656 657 if (modalElement.length > 0) { 658 modalElement.addClass('show'); 659 } else { 660 console.error(`Modal with ID ${modalId} not found.`); 661 } 662 }); 663 664 jQuery(document).on('click', '[data-dissection-donor-toggle]', function () { 665 jQuery('#dissectionDonorModal').addClass('show').attr('aria-hidden', 'false'); 666 667 if (jQuery('#dissection-container').outerHeight() < jQuery('#dissectionDonorModal .modal-body').outerHeight()) { 668 jQuery('#dissection-container').height(jQuery('#dissectionDonorModal .modal-body').outerHeight() + 64); 669 } 670 }); 671 672 jQuery(document).on('click', '[data-dissection-donor-dismiss]', function () { 673 jQuery('#dissectionDonorModal').removeClass('show').attr('aria-hidden', 'true'); 674 jQuery('#dissection-container').removeAttr('style'); 675 }); 676 677 jQuery('#show-more-search-results').on('click', function (e) { 678 e.preventDefault(); 679 if (jQuery(this).hasClass('disabled')) return; 680 681 jQuery(this).addClass('disabled').text(tmThemeText('loading')); 682 683 jQuery.get(jQuery(this).attr('href'), function (data) { 684 let items = jQuery(data).find('.search-result-item'); 685 items.each(async function () { 686 jQuery(this).hide(); 687 jQuery('#search-results').append(jQuery(this)); 688 jQuery(this).fadeIn(500); 689 await new Promise(r => setTimeout(r, 100)); 690 }); 691 let showMore = jQuery(data).find('#show-more-search-results'); 692 if (showMore.length > 0) { 693 let remaining = jQuery(data).find('#search-results-left').text(); 694 695 jQuery('#search-results-left').text(remaining); 696 jQuery('#show-more-search-results').removeClass('disabled').text(tmThemeText('showMore')).attr('href', showMore.attr('href')); 697 } else { 698 jQuery('#show-more-search-results').parents('section:first').remove(); 699 } 700 }); 701 }); 702 703 if (jQuery('.exam-date-selector').length > 0) { 704 let options = { 705 element: jQuery('.exam-date-selector').get(0), 706 format: 'YYYY-MM-DD', 707 css: [ 708 easepick_css 709 ], 710 plugins: ['LockPlugin'], 711 LockPlugin: { 712 minDate: new Date(), 713 }, 714 setup(picker) { 715 picker.on('select', (e) => { 716 //send to wordpress ajax and update jQuery('.days-to-exam') based on returned value 717 jQuery.post(ajaxurl, { 718 'action': 'set_exam_date', 719 'exam_date': jQuery('.exam-date-selector').text() 720 }, function (data) { 721 jQuery('.days-to-exam').text(data); 722 }); 723 }); 724 } 725 }; 726 727 const examDate = jQuery('.exam-date-selector').data('examdate'); 728 if (examDate) { 729 options.date = examDate; 730 } 731 732 new easepick.create(options); 733 jQuery('.exam-date-selector-link').on('click', function (e) { 734 e.preventDefault(); 735 jQuery('.exam-date-selector').trigger('click'); 736 }); 737 } 738 739 if (jQuery('article').length > 0) { 740 jQuery('article u > a').each(function () { 741 jQuery(this).unwrap(); 742 }); 743 } 744 745 jQuery('.goBack').on('click', function (e) { 746 e.preventDefault(); 747 //check if the previous page is on this site, if not or there is no history use the home page instead 748 if (document.referrer.indexOf(window.location.origin) !== -1 && window.history.length > 1) 749 window.history.back(); 750 else 751 window.location.href = '/'; 752 }); 753 754 jQuery(window).trigger('resize'); 755 756 // Password toggle 757 jQuery('.toggle-password').on('click', function () { 758 const input = jQuery(this).siblings('.password-field'); 759 const type = input.attr('type') === 'password' ? 'text' : 'password'; 760 input.attr('type', type); 761 jQuery(this).toggleClass('fa-eye fa-eye-slash'); 762 }); 763}); 764 765function tmUpdateSubjectsMenuPosition() { 766 if (jQuery(window).width() >= 1200) { 767 const languageSuggestionHeight = tmLanguageSuggestionBanner && tmLanguageSuggestionBanner.length && !tmLanguageSuggestionBanner.prop('hidden') ? tmLanguageSuggestionBanner.outerHeight() : 0; 768 769 if (jQuery(window).scrollTop() >= 102 + languageSuggestionHeight) { 770 jQuery('.subjects-menu').addClass('fixed-subjects-menu'); 771 } else { 772 jQuery('.subjects-menu').removeClass('fixed-subjects-menu'); 773 } 774 } else { 775 jQuery('.subjects-menu').removeClass('fixed-subjects-menu'); 776 } 777} 778 779jQuery(window).on('scroll resize', function () { 780 tmUpdateSubjectsMenuPosition(); 781 782 jQuery('header').toggleClass('is-scrolled', jQuery(window).scrollTop() > 16); 783}); 784 785function updateContent() { 786 let isLoggedOut = !jQuery('body').hasClass('logged-in'); 787 788 if (isLoggedOut) { 789 if (!page_id) { 790 loadContentViaAjax('missing_page_id'); 791 return; 792 } 793 794 let cacheLanguage = tmCurrentLanguage ? '-' + encodeURIComponent(tmCurrentLanguage) : ''; 795 let cacheUrl = '/wp-content/uploads/nav-cache/page-' + page_id + cacheLanguage + '.json'; 796 797 jQuery.ajax({ 798 url: cacheUrl, 799 dataType: 'text', 800 cache: true, 801 success: function (response, _status, xhr) { 802
802 try { 803 let data = JSON.parse(response); 804 processUpdateContentData(data); 805 } catch (e) { 806 let contentType = (xhr.getResponseHeader('Content-Type') || 'unknown').split(';')[0].replace(/\//g, '-'); 807 let preview = btoa((response || '').substring(0, 200)); 808 loadContentViaAjax('parse_fail|' + contentType + '|' + preview); 809 } 810 }, 811 error: function (xhr, status) { 812 loadContentViaAjax('cache_' + xhr.status + '_' + status); 813 } 814 }); 815 } else { 816 loadContentViaAjax('logged_in'); 817 } 818} 819 820function loadContentViaAjax(reason) { 821 let data = { 822 'action': 'update_content', 823 'id': page_id, 824 'ajax_reason': reason || 'unknown', 825 }; 826 827 data.tm_language = tmCurrentLanguage; 828 829 if (jQuery('article #quizQuestionsArea').length > 0) 830 data.get_quiz_content = true; 831 832 if (window.maybeShowPopup && !tmCurrentLanguage) 833 data.maybe_show_popup = true; 834 835 if (window.additionalData) 836 data.additional_data = window.additionalData; 837 838 let cartProductId = localStorage.getItem('cartProductId'); 839 if (cartProductId) 840 data.cartProductId = cartProductId; 841 842 let profileType = localStorage.getItem('profileType'); 843 if (profileType) 844 data.profileType = profileType; 845 846 jQuery.post(ajaxurl, data, function (responseData) { 847 processUpdateContentData(responseData); 848 }, 'json'); 849} 850 851function processUpdateContentData(data) { 852 jQuery('body').toggleClass('institution-user', !!data.institutionUser); 853 854 jQuery('.nav-buttons').html(data.topNav); 855 jQuery('.mobile-nav-buttons').html(data.topNavMobile); 856 857 if (data.languageSuggestion) { 858 tmLanguageSuggestionSetServerData(data.languageSuggestion); 859 } 860 861 if (data.navVariant && !localStorage.getItem('navVariant')) { 862 localStorage.setItem('navVariant', data.navVariant); 863 gtag('event', 'experiment_viewed', { 864 'experiment_id': 'top-navigation-update', 865 'variation_id': parseInt(data.navVariant) 866 }); 867 } 868 869 if (data.signupVariant && !localStorage.getItem('signupVariant')) { 870 localStorage.setItem('signupVariant', data.signupVariant); 871 gtag('event', 'experiment_viewed', { 872 'experiment_id': 'sign-button', 873 'variation_id': parseInt(data.signupVariant) 874 }); 875 } 876 877 if (data.signup) { 878 jQuery('.signup-btn').text(data.signup); 879 } 880 881 if (data.quizQuestions) { 882 if (data.quizMessage) 883 jQuery('#quizMessage').html(data.quizMessage); 884 else 885 jQuery('#quizResultsArea .simple-card').remove(); 886 887 window.quizQuestions = data.quizQuestions; 888 jQuery('.maxQuestions').text(window.quizQuestions.length); 889 nextQuizQuestion(); 890 } else if (jQuery('#quizQuestionsArea').length > 0 && typeof nextQuizQuestion === 'function') { 891 nextQuizQuestion(); 892 } 893 894 if (!tmCurrentLanguage && data.showPopup) { 895 setupPopup(data.popupContent, false); 896 } 897 898 if (data.showVerification) 899 setupPopup(data.popupContent, false); 900 901 if (data.noWebAds) 902 jQuery('.ad-container').remove(); 903 else 904 window.dispatchEvent(new Event('loadAds')); 905 906 if (data.clearCartProductId) { 907 localStorage.removeItem('cartProductId'); 908 } 909 910 if (data.clearProfileType) { 911 localStorage.removeItem('profileType'); 912 } 913 914 if (data.redirect) 915 window.location.href = data.redirect; 916} 917 918function setupPopup(content, launchNow = false) { 919 jQuery('#popupModal .tab-content').html(content); 920 if (launchNow) { 921 /*let modal = new bootstrap.Modal(document.getElementById('popupModal'), { 922 backdrop: 'static', 923 keyboard: false 924 }); 925 modal.show();*/ 926 jQuery('#popupModal').modal('show'); 927 } 928} 929 930function setup3DModel() { 931 jQuery('.expandModel').click(function (e) { 932 e.preventDefault(); 933 934 let parent = ''; 935 936 if (jQuery(this).parents('#model-container').length > 0) 937 parent = jQuery('#model-container'); 938 else if (jQuery(this).parents('#dissection-container').length > 0) 939 parent = jQuery('#dissection-container'); 940 941 if (parent.attr('id') === 'model-container') { 942 tmArticleModelLoadTriggered = true; 943 loadArticleBioDigitalModel(); 944 } 945 946 parent.toggleClass('expanded'); 947 948 if (parent.hasClass('expanded')) { 949 parent.removeClass('position-relative'); 950 jQuery('body').addClass('modal-open'); 951 } else { 952 parent.addClass('position-relative'); 953 jQuery('body').removeClass('modal-open'); 954 } 955 956 if (window.dissectionCarousel) 957 window.dissectionCarousel.trigger('refresh.owl.carousel'); 958 }); 959 960 if (jQuery('#embedded-human[data-biodigital-src]').length > 0) { 961 setupArticleBioDigitalModel(); 962 } else if (jQuery('#embedded-human').length > 0) { 963 setupModel('embedded-human'); 964 } 965} 966 967function setupModel(id) { 968 const human = new HumanAPI(id); 969 const options = window.biodigitalOptions || {}; 970 971 // Set background if color exists 972 if (options.background_color) {
973 human.send("ui.setBackground", { 974 colors: [[255, 255, 255], options.background_color] 975 }); 976 } 977 978 // Set UI controls with fallback defaults 979 const displayConfig = { 980 fullscreen: options.fullscreen ?? false, 981 tools: options.tools ?? true, 982 help: options.help ?? true, 983 share: options.share ?? false, 984 zoomIn: options.zoomIn ?? true, 985 zoomOut: options.zoomOut ?? true, 986 info: options.info ?? true 987 }; 988 989 human.send('ui.setDisplay', displayConfig); 990 991 let animateUpdate = true; 992 human.on('scene.picked', function () { 993 animateUpdate = false; 994 }); 995} 996 997function setupReviews() { 998 if (jQuery('.rating-col').length > 0) { 999 setInterval(() => { 1000 jQuery('.rating-col:visible').fadeOut(500, function () { 1001 let next = jQuery(this).next(); 1002 if (next.length === 0) { 1003 next = jQuery(this).siblings().first(); 1004 } 1005 next.fadeIn(500); 1006 }); 1007 }, 5000); 1008 } 1009 if (jQuery('.rating-swiper').length > 0) { 1010 const swiperContainer = jQuery('.rating-swiper'); 1011 const slideCount = swiperContainer.find('.swiper-slide').length; 1012 1013 let swiperInstance; 1014 1015 const initSwiper = () => { 1016 if (swiperInstance) { 1017 swiperInstance.destroy(true, true); 1018 } 1019 1020 const windowWidth = window.innerWidth; 1021 const slidesPerView = windowWidth >= 992 ? 3 : 1; 1022 const shouldLoop = slideCount > slidesPerView; 1023 1024 const swiperOptions = { 1025 slidesPerView: slidesPerView, 1026 spaceBetween: 10, 1027 autoplay: { 1028 delay: 15000, 1029 disableOnInteraction: false 1030 }, 1031 pagination: shouldLoop ? { 1032 el: '.swiper-pagination', 1033 clickable: true, 1034 dynamicBullets: true 1035 } : false, 1036 loop: shouldLoop, 1037 breakpoints: { 1038 992: { 1039 slidesPerView: 3 1040 } 1041 } 1042 }; 1043 1044 swiperInstance = new Swiper('.rating-swiper', swiperOptions); 1045 equalizeSwiperSlideHeights(); 1046 }; 1047 1048 function equalizeSwiperSlideHeights() { 1049 let maxHeight = 0; 1050 jQuery('.rating-swiper .swiper-slide').css('height', 'auto'); 1051 jQuery('.rating-swiper .swiper-slide').each(function () { 1052 if (jQuery(this).height() > maxHeight) { 1053 maxHeight = jQuery(this).height(); 1054 } 1055 }); 1056 jQuery('.rating-swiper .swiper-slide').css('height', maxHeight + 'px'); 1057 if (swiperInstance) { 1058 swiperInstance.update(); 1059 } 1060 } 1061 1062 initSwiper(); 1063 1064 let resizeTimer; 1065 jQuery(window).on('resize', function () { 1066 clearTimeout(resizeTimer); 1067 resizeTimer = setTimeout(function () { 1068 initSwiper(); 1069 }, 250); 1070 }); 1071 } 1072} 1073 1074function createMobileMenu() { 1075 jQuery('.subjects-menu-ul > li').each(function () { 1076 var $topMenuLink = jQuery(this).children('a:first'); 1077 var topMenuId = $topMenuLink.data('id'); 1078 var topMenuTitle = $topMenuLink.text(); 1079 var topMenuUrl = $topMenuLink.attr('href'); 1080 1081 var hasMegaMenu = jQuery('.mega-menu [data-parent="' + topMenuId + '"]').length > 0; 1082 var hasDirectChildren = jQuery(this).children('ul').length > 0; 1083 1084 if (hasMegaMenu) { 1085 var $topLi = jQuery('<li class="has-sub-menu"></li>').append(jQuery('<a href="#">' + topMenuTitle + '<i class="fa-solid fa-chevron-right float-end"></i></a>')); 1086 1087 var $subMenu = jQuery('<ul class="sub-menu pt-4"></ul>').appendTo($topLi); 1088 1089 $subMenu.append( 1090 jQuery('<li class="pb-3"></li>').append( 1091 jQuery('<a />', { href: '#', 'class': 'menu-back' }) 1092 .append(jQuery('<i />', { 'class': 'fa-solid fa-chevron-left float-start me-2' })) 1093 .append(document.createTextNode(tmThemeText('backToSubjects'))) 1094 ) 1095 ); 1096 1097 $subMenu.append(jQuery('<li>' + topMenuTitle + '<ul class="underline-lis px-0"></ul></li>')); 1098 1099 jQuery('.mega-menu [data-parent="' + topMenuId + '"] div').each(function () { 1100 var $subMenuLink = jQuery(this).find('p a:first'); 1101 var subMenuTitle = $subMenuLink.text(); 1102 var subMenuUrl = $subMenuLink.attr('href'); 1103 var $subChildren = jQuery(this).find('li'); 1104 1105 if ($subChildren.length === 0) { 1106 $subMenu.children('li:last-child').children('ul').append( 1107 jQuery('<li></li>').append(jQuery('<a href="' + subMenuUrl + '">' + subMenuTitle + '</a>')) 1108 ); 1109 return; 1110 } 1111 1112 var $subLi = jQuery('<li class="has-sub-menu"></li>').append(jQuery('<a href="#">
1112' + subMenuTitle + '<i class="fa-solid fa-chevron-right float-end"></i></a>')); 1113 1114 var $innerSubMenu = jQuery('<ul class="sub-menu pt-4"></ul>').appendTo($subLi); 1115 1116 $innerSubMenu.append( 1117 jQuery('<li class="pb-3"></li>').append( 1118 jQuery('<a />', { href: '#', 'class': 'menu-back' }) 1119 .append(jQuery('<i />', { 'class': 'fa-solid fa-chevron-left float-start me-2' })) 1120 .append(document.createTextNode(tmThemeText('backTo') + ' ' + topMenuTitle)) 1121 ) 1122 ); 1123 1124 $innerSubMenu.append(jQuery('<li>' + subMenuTitle + '<ul class="underline-lis px-0"></ul></li>')); 1125 1126 $subChildren.each(function () { 1127 var subChildTitle = jQuery(this).text(); 1128 var subChildUrl = jQuery(this).find('a').attr('href'); 1129 1130 $innerSubMenu.find('li:last-child ul').append(jQuery('<li></li>').append(jQuery('<a href="' + subChildUrl + '">' + subChildTitle + '</a>'))); 1131 }); 1132 1133 $subMenu.children('li:last-child').children('ul').append($subLi); 1134 }); 1135 1136 jQuery('#mobile-subjects-menu').append($topLi); 1137 } else if (hasDirectChildren) { 1138 var $topLi = jQuery('<li class="has-sub-menu"></li>').append(jQuery('<a href="#">' + topMenuTitle + '<i class="fa-solid fa-chevron-right float-end"></i></a>')); 1139 1140 var $subMenu = jQuery('<ul class="sub-menu pt-4"></ul>').appendTo($topLi); 1141 1142 $subMenu.append( 1143 jQuery('<li class="pb-3"></li>').append( 1144 jQuery('<a />', { href: '#', 'class': 'menu-back' }) 1145 .append(jQuery('<i />', { 'class': 'fa-solid fa-chevron-left float-start me-2' })) 1146 .append(document.createTextNode(tmThemeText('backToSubjects'))) 1147 ) 1148 ); 1149 1150 $subMenu.append(jQuery('<li>' + topMenuTitle + '<ul class="underline-lis px-0"></ul></li>')); 1151 1152 jQuery(this).children('ul').children('li').each(function () { 1153 var childTitle = jQuery(this).children('a').text(); 1154 var childUrl = jQuery(this).children('a').attr('href'); 1155 1156 $subMenu.find('li:last-child ul').append(jQuery('<li></li>').append(jQuery('<a href="' + childUrl + '">' + childTitle + '</a>'))); 1157 }); 1158 1159 jQuery('#mobile-subjects-menu').append($topLi); 1160 } else { 1161 var $topLi = jQuery('<li></li>').append(jQuery('<a href="' + topMenuUrl + '">' + topMenuTitle + '</a>')); 1162 jQuery('#mobile-subjects-menu').append($topLi); 1163 } 1164 }); 1165 jQuery('.sub-menu').addClass('d-none'); 1166} 1167 1168jQuery(window).on('scroll', function () { 1169 if (!window.scrolled) { 1170 window.scrolled = true; 1171 jQuery(window).trigger('resize'); 1172 } 1173}); 1174 1175jQuery(window).on('load', function () { 1176 scheduleStickyAsideUpdate(); 1177}); 1178 1179jQuery(window).on('resize', function () { 1180 jQuery('.image-zoom').each(function () { 1181 jQuery(this).height(jQuery(this).children('img').height()); 1182 }); 1183 1184 jQuery('.mh-width').each(function () { 1185 if (jQuery(this).children('img').length > 0) { 1186 jQuery(this).children('img').css('max-height', jQuery(this).width()); 1187 } else { 1188 jQuery(this).css('max-height', jQuery(this).width()); 1189 } 1190 }); 1191 1192 if (stickyAside) { 1193 if (jQuery(window).width() < 1200) { 1194 stickyAside.destroy(); 1195 jQuery('aside .content-menu').attr('data-margin-top', '100'); 1196 stickyAside = new Sticky('aside .content-menu'); 1197 } else { 1198 stickyAside.destroy(); 1199 jQuery('aside .content-menu').attr('data-margin-top', '150'); 1200 stickyAside = new Sticky('aside .content-menu'); 1201 } 1202 } 1203 1204 doCheckoutUpdates(); 1205 1206 if (jQuery(window).width() < 1200 && jQuery('.search-form-control').parents('#desktop-search-container').length > 0) { 1207 jQuery('.search-form-control').appendTo('#mobile-search-container'); 1208 } else if (jQuery(window).width() >= 1200 && jQuery('.search-form-control').parents('#mobile-search-container').length > 0) { 1209 jQuery('.search-form-control').appendTo('#desktop-search-container'); 1210 } 1211}); 1212 1213function doCheckoutUpdates() { 1214 if (jQuery('.checkout-side').length > 0 && jQuery('.checkout .bg-dark').length > 0) { 1215 const left = jQuery('.full-page-content .bg-dark').offset().left; 1216 jQuery('.checkout-side').css({ 1217 left: left, 1218 width: jQuery(window).width() - left, 1219 height: jQuery('.full-page-content .bg-dark').outerHeight() + jQuery('.full-page-content .bg-dark').offset().top 1220 }); 1221 } 1222 1223 if (jQuery('#woocommerce_eu_vat_compliance_vat_number h3').length > 0) { 1224 //change h3 to h4 with class 'mb-0 mt-3' 1225 jQuery('#woocommerce_eu_vat_compliance_vat_number h3').replaceWith('<h4 class="mb-0 mt-3">' + jQuery('#woocommerce_eu_vat_compliance_vat_number h3').text() + '</h4>'); 1226 jQuery('#vat_number').addClass('form-control'); 1227 } 1228} 1229 1230function rateSubmit(token) { 1231 jQuery('.rate-form').data('submitting', true); 1232 1233 jQuery.post(ajaxurl, { 1234 'action': 'rate_article', 1235 'id': page_id, 1236 'feedback': jQuery('.rate-form textarea[name="feedback"]').val(), 1237 'rating': jQuery('.rate-form .rate.selected').length, 1238 'captcha': token || jQuery('.rate-form [name="cf-turnstile-response"]').val() 1239 }, function (data) { 1240 jQuery('.rate-form .spinner-border').remove(); 1241 jQuery('.rate-form fieldset').prop('disabled', false); 1242 jQuery('.rate-form button[type="submit"]').prop('disabled', false); 1243 1244 if (data.success) { 1245 jQuery('.rate-form') 1246 .empty() 1247 .append( 1248 jQuery('<div />', { 1249 'class': 'alert alert-success', 1250 text: tmThemeText('ratingSuccess') 1251 }) 1252 ); 1253 } else { 1254 if (typeof turnstile !== 'undefined' && window.turnstileRatingWidget) { 1255 turnstile.reset(window.turnstileRatingWidget); 1256 } 1257 jQuery('.rate-form').data('submitting', false); 1258 jQuery('.rate-form').prepend( 1259 jQuery('<div />', { 1260 'class': 'alert alert-danger', 1261 text: tmThemeText('ratingError') 1262 }) 1263 ); 1264 } 1265 }, 'json').fail(function () { 1266 jQuery('.rate-form .spinner-border').remove(); 1267 jQuery('.rate-form fieldset').prop('disabled', false); 1268 jQuery('.rate-form button[type="submit"]').prop('disabled', false); 1269 jQuery('.rate-form').data('submitting', false); 1270 if (typeof turnstile !== 'undefined' && window.turnstileRatingWidget) { 1271 turnstile.reset(window.turnstileRatingWidget); 1272 } 1273 jQuery('.rate-form').prepend( 1274 jQuery('<div />', { 1275 'class': 'alert alert-danger', 1276 text: tmThemeText('networkError') || 'Network error. Please try again.' 1277 }) 1278 ); 1279 }); 1280} 1281 1282jQuery(document.body).on('init_checkout update_checkout updated_checkout checkout_error applied_coupon_in_checkout removed_coupon_in_checkout', doCheckoutUpdates); 1283 1284function tmInitTurnstileWidgets() { 1285 if (typeof turnstile === 'undefined') { 1286 return; 1287 } 1288 1289 if (document.getElementById('turnstile-signup') && typeof window.turnstileSignupWidget === 'undefined') { 1290 window.turnstileSignupWidget = turnstile.render('#turnstile-signup', { 1291 sitekey: '0x4AAAAAACaK9nm8jVucMDYV', 1292 }); 1293 } 1294 1295 if (document.getElementById('turnstile-rating') && typeof window.turnstileRatingWidget === 'undefined') { 1296 window.turnstileRatingWidget = turnstile.render('#turnstile-rating', { 1297 sitekey: '0x4AAAAAACaK9nm8jVucMDYV', 1298 }); 1299 } 1300 1301 if (document.getElementById('turnstile-report') && typeof window.turnstileReportWidget === 'undefined') { 1302 window.turnstileReportWidget = turnstile.render('#turnstile-report', { 1303 sitekey: '0x4AAAAAACaK9nm8jVucMDYV', 1304 }); 1305 } 1306 1307 if (document.getElementById('turnstile-3d-report') && typeof window.turnstile3DReportWidget === 'undefined') { 1308 window.turnstile3DReportWidget = turnstile.render('#turnstile-3d-report', { 1309 sitekey: '0x4AAAAAACaK9nm8jVucMDYV', 1310 }); 1311 } 1312} 1313 1314window.tmInitTurnstileWidgets = tmInitTurnstileWidgets; 1315 1316jQuery(document).ready(function () { 1317 tmInitTurnstileWidgets(); 1318 1319 if (typeof turnstile !== 'undefined') { 1320 return; 1321 } 1322 1323 let turnstileAttempts = 0; 1324 const turnstileInterval = window.setInterval(function () { 1325 turnstileAttempts++; 1326 tmInitTurnstileWidgets(); 1327 1328 if (typeof turnstile !== 'undefined' || turnstileAttempts >= 20) { 1329 window.clearInterval(turnstileInterval); 1330 } 1331 }, 250); 1332}); 1333 1334 1335// Step transition functionality 1336function animateStepTransition(currentStepEl, nextStepEl, direction) { 1337 const exitClass = direction > 0 ? "exit-to-left" : "exit-to-right"; 1338 const enterClass = direction > 0 ? "enter-from-right" : "enter-from-left"; 1339 const nextHeight = getNextStepHeight(currentStepEl, nextStepEl); 1340 1341 jQuery(currentStepEl).removeClass('active').addClass(exitClass); 1342 jQuery(nextStepEl).addClass(enterClass); 1343 1344 setTimeout(() => { 1345 jQuery(nextStepEl).show().removeClass(enterClass); 1346 }, 1); 1347 1348 jQuery('.steps-card-form').height(nextHeight); 1349 1350 setTimeout(() => { 1351 jQuery(currentStepEl).hide().removeClass(exitClass); 1352 jQuery(nextStepEl).addClass('active'); 1353 jQuery('.steps-card-form').height('auto'); 1354 }, 300); 1355} 1356 1357function getNextStepHeight(currentStepEl, nextStepEl) { 1358 const currentHeight = jQuery('.steps-card-form').height(); 1359 jQuery('.steps-card-form').height('auto'); 1360 jQuery(currentStepEl).hide(); 1361 jQuery(nextStepEl).addClass('active').show(); 1362 const height = jQuery('.steps-card-form').height(); 1363 jQuery('.steps-card-form').height(currentHeight); 1364 jQuery(nextStepEl).removeClass('active').hide(); 1365 jQuery(currentStepEl).show(); 1366 return height; 1367} 1368 1369function updateProgressBar(currentStepIndex) { 1370 jQuery('.progress-step-wrapper').each(function () { 1371 const $wrapper = jQuery(this); 1372 const isWithDisabled = $wrapper.hasClass('with-disabled'); 1373 1374 $wrapper.find('.progress-step').each(function (index) { 1375 const $dot = jQuery(this); 1376 const dotStep = parseInt($dot.attr('data-step')) || (index + 1); // Use data-step if available, fallback to index+1 1377 1378 $dot.removeClass('completed active disabled'); 1379 1380 if (dotStep < currentStepIndex) { 1381 $dot.addClass('completed'); 1382 if (isWithDisabled && dotStep <= 2) { 1383 $dot.addClass('disabled'); // only for steps 1, 2, 3 1384 } 1385 } else if (dotStep === currentStepIndex) { 1386 $dot.addClass('active'); 1387 } else { 1388 $dot.addClass('disabled'); 1389 } 1390 }); 1391 }); 1392} 1393 1394function showStep(step, direction = 1, instant = false) { 1395 const $steps = jQuery('.step'); 1396 const $progressSteps = jQuery('.progress-step'); 1397 1398 // Find the currently active step 1399 const $activeStep = $steps.filter('.active');
1400 let currentStepNum = 1; // Default to step 1 1401 1402 if ($activeStep.length) { 1403 // Extract step number from class (e.g., 'step-3' -> 3) 1404 const stepClasses = $activeStep.attr('class').match(/step-(\d+)/); 1405 if (stepClasses) { 1406 currentStepNum = parseInt(stepClasses[1]); 1407 } 1408 } 1409 1410 const $currentStepEl = jQuery('.step-' + currentStepNum); 1411 const $targetStepEl = jQuery('.step-' + step); 1412 1413 if ($targetStepEl.length) { 1414 if (instant) { 1415 $steps.hide().removeClass('active'); 1416 $targetStepEl.show().addClass('active'); 1417 } else { 1418 animateStepTransition($currentStepEl[0], $targetStepEl[0], direction); 1419 } 1420 1421 updateProgressBar(step); 1422 1423 jQuery(window).trigger('step-changed', step); 1424 } 1425} 1426 1427function setupStepNavigation() { 1428 jQuery(document).on('click', '.progress-step', function (e) { 1429 e.preventDefault(); 1430 1431 // Get the step number from data attribute 1432 const targetStep = parseInt(jQuery(this).attr('data-step')); 1433 1434 if (!targetStep) return; 1435 1436 // Find the currently active step 1437 const $steps = jQuery('.step'); 1438 const $activeStep = $steps.filter('.active'); 1439 let currentStepNum = 1; // Default to step 1 1440 1441 if ($activeStep.length) { 1442 // Extract step number from class (e.g., 'step-3' -> 3) 1443 const stepClasses = $activeStep.attr('class').match(/step-(\d+)/); 1444 if (stepClasses) { 1445 currentStepNum = parseInt(stepClasses[1]); 1446 } 1447 } 1448 1449 if (targetStep === currentStepNum) return; 1450 1451 // Don't allow clicking on steps 1 or 2 (email and verification) 1452 if (targetStep <= 2) return; 1453 1454 // Don't allow clicking from step 2 (verification) 1455 if (currentStepNum === 2) return; 1456 1457 // Only allow clicking on completed steps (steps before current) 1458 if (targetStep < currentStepNum && targetStep >= 3) { 1459 // Add visual feedback 1460 jQuery(this).addClass('clicking'); 1461 setTimeout(() => { 1462 jQuery(this).removeClass('clicking'); 1463 }, 300); 1464 1465 jQuery(window).trigger('progress-step-clicked', targetStep); 1466 } 1467 }); 1468}
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.