PageSourceSearch

https://teachmephysiology.com/wp-content/themes/teachme2024/js/script.js?ver=1790669647

js teachmephysiology.com collected 2026-10-01 11:43:38 UTC 51,736 bytes, 1,468 lines download raw bytes

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.