PageSourceSearch

https://sbaprolife.org/wp-content/themes/SBA/assets/js/custom.js?ver=1782465686

js sbaprolife.org collected 2026-10-02 03:21:30 UTC 25,805 bytes, 968 lines download raw bytes

1//Mobile Slide Menu
2// document.addEventListener("DOMContentLoaded", function () {
3// 	const menuElement = document.getElementById("mobile-menu");
4// 	const menu = new SlideMenu(menuElement, {
5// 		position: "right",
6// 	});
7
8// 	menuElement.addEventListener("sm.back-after", function () {
9// 		//jQuery(".mobile-contact").show();
10
11// 		jQuery("#main-cta-box").show();
12// 		jQuery("#about-cta-box").hide();
13// 		jQuery("#impact-cta-box").hide();
14// 		jQuery("#resources-cta-box").hide();
15// 		jQuery("#media-cta-box").hide();
16// 		jQuery("#involved-cta-box").hide();
17// 	});
18// });
19
20//Mobile Donate Menu
21// document.addEventListener("DOMContentLoaded", function () {
22// 	const menuElement = document.getElementById("mob-donate-menu");
23// 	const menu = new SlideMenu(menuElement, {
24// 		position: "right",
25// 	});
26// });
27
28//Mega Menu Donate
29jQuery(document).ready(function ($) {
30	var $btn = $("#donate-mega-menu-btn");
31	var $desktopMenu = $("#donate-mega-menu");
32	var $mobileMenu = $("#donate-mega-menu-mobile");
33
34	function isMobile() {
35		return window.matchMedia("(max-width: 1023px)").matches;
36	}
37
38	$btn.on("click", function (e) {
39		e.preventDefault();
40		e.stopPropagation();
41
42		var megaMenu = $(".max-mega-menu").data("maxmegamenu");
43		if (megaMenu && typeof megaMenu.hideAllPanels === "function") {
44			megaMenu.hideAllPanels();
45		}
46
47		if (isMobile()) {
48			$mobileMenu.toggleClass("is-open");
49			$desktopMenu.slideUp();
50		} else {
51			$desktopMenu.stop(true, true).slideToggle();
52			$mobileMenu.removeClass("is-open");
53		}
54	});
55
56	// Close on mobile close button
57	$(".mob-nav-close").on("click", function (e) {
58		e.preventDefault();
59		e.stopPropagation();
60		$mobileMenu.removeClass("is-open");
61	});
62
63	// Prevent inside clicks from bubbling
64	$desktopMenu.add($mobileMenu).on("click", function (e) {
65		e.stopPropagation();
66	});
67
68	// Click outside to close
69	$(document).on("click", function () {
70		$desktopMenu.slideUp();
71		$mobileMenu.removeClass("is-open");
72	});
73
74	// Reset on resize
75	$(window).on("resize", function () {
76		$desktopMenu.hide();
77		$mobileMenu.removeClass("is-open");
78	});
79
80	$("#page-selector")
81		.on("keydown", function (e) {
82			// Handle Enter key for keyboard users
83			if (e.which === 13) {
84				const val = $(this).val();
85				if (val) window.location.href = val;
86			}
87		})
88		.on("change", function (e) {
89			const val = $(this).val();
90			if (val) window.location.href = val;
91		});
92
93	//Table responsive
94	$(".sc-table").basictable({
95		breakpoint: 1023,
96	});
97
98	// Selectors
99	const $menu = $("#slide-out-menu");
100	const $overlay = $("#menu-overlay");
101	const $body = $("body");
102
103	// 1. Open Main Menu
104	$("#open-mobile-menu").on("click", function (e) {
105		e.preventDefault();
106		$menu.addClass("is-open");
107		$overlay.addClass("is-visible");
108		$body.css("overflow", "hidden"); // Prevent background scroll
109	});
110
111	// 2. Close Main Menu (Function)
112	function closeMenu() {
113		$menu.removeClass("is-open");
114		$overlay.removeClass("is-visible");
115		$body.css("overflow", "");
116
117		// Reset sub-panels after transition to avoid visual glitch
118		setTimeout(function () {
119			$(".sub-panel").removeClass("is-active");
120		}, 300);
121	}
122
123	// Close triggers
124	$("#close-mobile-menu, #menu-overlay").on("click", closeMenu);
125
126	// 3. Drill Down: Open Sub Panel
127	$(".submenu-trigger").on("click", function (e) {
128		e.preventDefault();
129		// Find the sibling sub-panel and slide it in
130		$(this).next(".sub-panel").addClass("is-active");
131	});
132
133	// 4. Go Back: Click the Header Title
134	$(".back-trigger").on("click", function (e) {
135		e.preventDefault();
136		// Remove active class from the closest parent panel
137		$(this).closest(".sub-panel").removeClass("is-active");
138	});
139});
140
141jQuery(window).load(function () {
142	if (jQuery(".search-module-init").length > 0) {
143		jQuery(".search-module-init").click(function () {
144			jQuery(this).parent().addClass("active");
145			jQuery(".search-field").focus();
146		});
147
148		jQuery(document).on("click", function (event) {
149			if (!jQuery(event.target).closest(".search-module").length) {
150				jQuery(".search-module").removeClass("active");
151			}
152			event.stopPropagation();
153		});
154	}
155
156	//Donate Hero
157	if (jQuery(".cta-form").length > 0) {
158		jQuery(".cta-form").each(function () {
159			jQuery(this).on("submit", function (e) {
160				e.preventDefault();
161
162				var frequency = jQuery(this).find("input[name='frequency']:checked").val();
163				var radioAmount = jQuery(this).find('input[name="amount"]:checked').val();
164				var otherAmount = jQuery(this).find('input[name="other-amount"]').val();
165				var baseUrl = jQuery(this).data("url");
166
167				var finalAmount = radioAmount;
168				if (otherAmount && otherAmount.length > 0) {
169					finalAmount = otherAmount;
170				}
171
172				var donateData = "amount=" + encodeURIComponent(finalAmount) + "&frequency=" + encodeURIComponent(frequency);
173
174				if (frequency !== "once" && frequency !== "one-time") {
175					donateData += "&recurring=recurring";
176				}
177
178				var url = baseUrl + "?utm_source=menu&" + donateData;
179
180				window.open(url, "_blank");
181			});
182		});
183	}
184
185	// Accessibility Scripts Starts
186	function getIframeSrc($iframe) {
187		return $iframe.attr("src") || $iframe.data("src") || $iframe.attr("data-src") || "";
188	}
189
190	function getYouTubeVideoId(src) {
191		const match = src.match(/(?:youtube(?:-nocookie)?\.com\/(?:embed\/|watch\?v=)|youtu\.be\/)([^"&?/ ]{11})/i);
192		return match ? match[1] : "";
193	}
194
195	function getEmbedProvider(src) {
196		if (/youtube(?:-nocookie)?\.com|youtu\.be/i.test(src)) {
197			return "youtube";
198		}
199
200		if (/facebook\.com\/plugins\/video\.php/i.test(src)) {
201			return "facebook";
202		}
203
204		return "";
205	}
206
207	function setIframeTitle($iframe, title) {
208		if (!$iframe.attr("title")) {
209			$iframe.attr("title", title);
210		}
211	}
212
213	// Function to add accessible titles to video embeds.
214	function fetchIframeTitle(iframe) {
215		const $iframe = jQuery(iframe);
216		const src = getIframeSrc($iframe); // Support for lazyload data-src
217
218		if (!src || $iframe.attr("title")) {
219			return;
220		}
221
222		const provider = getEmbedProvider(src);
223
224		if (provider === "youtube") {
225			const videoId = getYouTubeVideoId(src);
226
227			if (!videoId) {
228				setIframeTitle($iframe, "YouTube video player");
229				return;
230			}
231
232			const oEmbedUrl = `https://www.youtube.com/oembed?url=${encodeURIComponent("https://www.youtube.com/watch?v=" + videoId)}&format=json`;
233			$iframe.attr("title", "YouTube video player");
234
235			jQuery
236				.getJSON(oEmbedUrl, function (data) {
237					if (data && data.title) {
238						$iframe.attr("title", data.title);
239					}
240				})
241				.fail(function () {
242					// Fallback if the fetch fails (e.g. video is private)
243					setIframeTitle($iframe, "YouTube video player");
244				});
245
246			return;
247		}
248
249		if (provider === "facebook") {
250			setIframeTitle($iframe, "Facebook video player");
251		}
252	}
253
254	// Backward-compatible wrapper for any existing calls.
255	function fetchYouTubeTitle(iframe) {
256		fetchIframeTitle(iframe);
257	}
258
259	function processIframe(iframe) {
260		if (iframe && iframe.tagName && iframe.tagName.toLowerCase() === "iframe") {
261			fetchIframeTitle(iframe);
262		}
263	}
264
265	// Run on existing iframes
266	jQuery("iframe").each(function () {
267		processIframe(this);
268	});
269
270	// --- Lazyload Support ---
271	// Watch for lazyload src changes and iframes injected after page load.
272	const observer = new MutationObserver((mutations) => {
273		mutations.forEach((mutation) => {
274			if (mutation.type === "attributes" && (mutation.attributeName === "src" || mutation.attributeName === "data-src")) {
275				processIframe(mutation.target);
276			}
277
278			if (mutation.type === "childList") {
279				jQuery(mutation.addedNodes).each(function () {
280					processIframe(this);
281					jQuery(this).find("iframe").each(function () {
282						processIframe(this);
283					});
284				});
285			}
286		});
287	});
288
289	// Start watching the document for iframe lazy-loading triggers.
290	observer.observe(document.body, {
291		attributes: true,
292		attributeFilter: ["src", "data-src"],
293		childList: true,
294		subtree: true,
295	});
296
297	// 1. Target all tables
298	// jQuery("table").each(function () {
299	// 	const $table = jQuery(this);
300
301	// 	// 2. Logic: Only add it if the table doesn't have a <caption> or <th>
302	// 	// Real data tables usually have headers; layout tables do not.
303	// 	if ($table.find("th").length === 0 && $table.find("caption").length === 0) {
304	// 		$table.attr("role", "presentation");
305	// 	}
306	// });
307	// Accessibility Scripts Ends
308});
309
310jQuery(document).ready(function ($) {
311	//mobile menu bottom boxes
312	jQuery(".about-link").click(function () {
313		jQuery("#main-cta-box").hide();
314		jQuery("#about-cta-box").show();
315		jQuery("#impact-cta-box").hide();
316		jQuery("#resources-cta-box").hide();
317		jQuery("#media-cta-box").hide();
318		jQuery("#involved-cta-box").hide();
319	});
320	jQuery(".impact-link").click(function () {
321		jQuery("#main-cta-box").hide();
322		jQuery("#about-cta-box").hide();
323		jQuery("#impact-cta-box").show();
324		jQuery("#resources-cta-box").hide();
325		jQuery("#media-cta-box").hide();
326		jQuery("#involved-cta-box").hide();
327	});
328	jQuery(".resources-link").click(function () {
329		jQuery("#main-cta-box").hide();
330		jQuery("#about-cta-box").hide();
331		jQuery("#impact-cta-box").hide();
332		jQuery("#resources-cta-box").show();
333		jQuery("#media-cta-box").hide();
334		jQuery("#involved-cta-box").hide();
335	});
336	jQuery(".media-link").click(function () {
337		jQuery("#main-cta-box").hide();
338		jQuery("#about-cta-box").hide();
339		jQuery("#impact-cta-box").hide();
340		jQuery("#resources-cta-box").hide();
341		jQuery("#media-cta-box").show();
342		jQuery("#involved-cta-box").hide();
343	});
344	jQuery(".involved-link").click(function () {
345		jQuery("#main-cta-box").hide();
346		jQuery("#about-cta-box").hide();
347		jQuery("#impact-cta-box").hide();
348		jQuery("#resources-cta-box").hide();
349		jQuery("#media-cta-box").hide();
350		jQuery("#involved-cta-box").show();
351	});
352
353	/* ------------------------------------
354       SMOOTH SCROLL TO SECTION ON CLICK
355    ------------------------------------ */
356	jQuery(document).on("click", 'a[href^="#"]', function (e) {
357		var id = jQuery(this).attr("href");
358		var $id = $(id);
359
360		if ($id.length === 0) return;
361
362		e.preventDefault();
363
364		var pos = $id.offset().top;
365
366		jQuery("html, body").animate({ scrollTop: pos - 100 }, 500);
367	});
368
369	/* ------------------------------------
370       ADD ACTIVE CLASS ON SCROLL
371    ------------------------------------ */
372	// var sections = $("section[id]"); // all sections that have ID
373	// var navLinks = $(".report-sidebar a[href^='#']"); // your sidebar links
374
375	// $(window).on("scroll", function () {
376	// 	var scrollPos = $(window).scrollTop() + 120; // offset so section title is visible
377
378	// 	sections.each(function () {
379	// 		var top = $(this).offset().top;
380	// 		var bottom = top + $(this).outerHeight();
381
382	// 		// Check if this section is in the viewport
383	// 		if (scrollPos >= top && scrollPos <= bottom) {
384	// 			var id = jQuery(this).attr("id");
385
386	// 			navLinks.removeClass("active");
387	// 			jQuery('.report-sidebar a[href="#' + id + '"]').addClass("active");
388	// 		}
389	// 	});
390	// });
391
392	//Staff Popup
393	jQuery(".staff-btn").click(function () {
394		var target = jQuery(this).data("toggle");
395		jQuery('[data-target="' + target + '"]').toggleClass("hide-modal");
396	});
397	jQuery(".close-btn").click(function () {
398		var target = jQuery(this).data("toggle");
399		jQuery(".staff-popup").addClass("hide-modal");
400	});
401
402	//Latest News Slider
403	jQuery(".latest-news-items").slick({
404		dots: false,
405		infinite: false,
406		slidesToShow: 3.5,
407		centerMode: false,
408		vertical: false,
409		responsive: [
410			{
411				breakpoint: 1279,
412				settings: {
413					slidesToShow: 2.5,
414				},
415			},
416			{
417				breakpoint: 991,
418				settings: {
419					slidesToShow: 1.5,
420				},
421			},
422			{
423				breakpoint: 767,
424				settings: {
425					slidesToShow: 1,
426				},
427			},
428		],
429	});
430
431	//Testimonials
432	jQuery(".testimonial-row").slick({
433		dots: true,
434		arrows: false,
435		infinite: false,
436		slidesToShow: 3,
437		//slidesToScroll: 3,
438		infinite: true,
439		cssEase: "linear",
440		autoplay: true,
441		autoplaySpeed: 4000,
442		centerMode: true,
443		centerPadding: "0px",
444		//initialSlide: 1,
445		responsive: [
446			{
447				breakpoint: 1279,
448				settings: {
449					slidesToShow: 2,
450				},
451			},
452			{
453				breakpoint: 991,
454				settings: {
455					slidesToShow: 2,
456				},
457			},
458			{
459				breakpoint: 767,
460				settings: {
461					slidesToShow: 1,
462				},
463			},
464		],
465	});
466
467	// Hide all content on load
468	//jQuery('.presidential-candidates .pc-container').hide();
469
470	// Toggle on trigger click
471	jQuery(document).on("click", ".presidential-candidates .pc-trigger", function () {
472		var section = jQuery(this).closest(".presidential-candidates");
473		var content = section.find(".pc-container");
474
475		section.toggleClass("active");
476		content.slideToggle();
477	});
478
479	//FAQs
480	//Set default open/close settings
481	jQuery(".faq-content").hide(); //Hide/close all containers
482	//jQuery('.faq-title:first').addClass('active').next().show(); //Add "active" class to first trigger, then show/open the immediate next container
483
484	// --- Animation Function ---
485	function animateBars($context) {
486		$context.find(".pb-progress-container").each(function (index) {
487			var $wrapper = $(this);
488			var $bar = $wrapper.find(".pb-progress-bar");
489			var targetPercent = $wrapper.data("target");
490
491			// 1. Reset width to 0 immediately (so it can grow again)
492			$bar.css("width", "0%");
493
494			// 2. Stop any running animations to prevent glitches
495			$bar.stop();
496
497			// 3. Stagger and Animate
498			setTimeout(function () {
499				$bar.animate(
500					{
501						width: targetPercent + "%",
502					},
503					{
504						duration: 1500,
505						easing: "swing",
506					},
507				);
508			}, index * 200); // 200ms delay between bars
509		});
510	}
511
512	jQuery(".faq-title").click(function () {
513		if (jQuery(this).next().is(":hidden")) {
514			//If immediate next container is closed...
515			jQuery(".faq-title").removeClass("active").next().slideUp(); //Remove all .faq-title classes and slide up the immediate next container
516			jQuery(this).toggleClass("active").next().slideDown(); //Add .faq-title class to clicked trigger and slide down the immediate next container
517
518			var $contentArea = $(this).next();
519
520			// Run animation strictly inside this content area
521			animateBars($contentArea);
522		}
523		var id = jQuery(this).attr("id");
524		if (id) {
525			if (history.pushState) {
526				history.pushState(null, null, "#" + id);
527			} else {
528				location.hash = "#" + id;
529			}
530		}
531		return false; //Prevent the browser jump to the link anchor
532	});
533
534	function openFaqFromHash() {
535		if (window.location.hash) {
536			var hash = window.location.hash;
537
538			var $faqTitle = jQuery(hash);
539
540			if ($faqTitle.length && $faqTitle.hasClass("faq-title")) {
541				// Open the target FAQ if it's closed
542				if ($faqTitle.next().is(":hidden")) {
543					// Close any other open FAQs
544					jQuery(".faq-title").not($faqTitle).removeClass("active").next().slideUp();
545					$faqTitle.addClass("active").next().slideDown();
546					animateBars($faqTitle.next());
547				}
548
549				// Scroll to the FAQ
550				setTimeout(function () {
551					jQuery("html, body").animate(
552						{
553							scrollTop: $faqTitle.offset().top - 100, // 100px offset for header
554						},
555						500,
556					);
557				}, 100);
558			}
559		}
560	}
561
562	// Run on page load
563	openFaqFromHash();
564
565	// Run on hash change
566	jQuery(window).on("hashchange", function () {
567		openFaqFromHash();
568	});
569	// --- Animation Function ---
570
571	//Side Pannel
572	jQuery(".floating-tab").click(function () {
573		jQuery(this).toggleClass("active");
574		jQuery(".side-pannel").toggleClass("active");
575		jQuery(".slde-panel-overlay").toggle();
576		return !1;
577	});
578	jQuery(".slide-panel-close").click(function () {
579		jQuery(".side-pannel").removeClass("active");
580		jQuery(".floating-tab").removeClass("active");
581		jQuery(".slde-panel-overlay").hide();
582		return !1;
583	});
584	jQuery(".slde-panel-overlay").click(function () {
585		jQuery(this).hide();
586		jQuery(".side-pannel").removeClass("active");
587		jQuery(".floating-tab").removeClass("active");
588		return !1;
589	});
590
591	//CountUp
592	// jQuery(".countup").each(function () {
593	// 	var size = jQuery(this).text().split(".")[1] ? jQuery(this).text().split(".")[1].length : 0;
594	// 	jQuery(this)
595	// 		.prop("Counter", 0)
596	// 		.animate(
597	// 			{
598	// 				Counter: jQuery(this).text(),
599	// 			},
600	// 			{
601	// 				duration: 2000,
602	// 				step: function (func) {
603	// 					jQuery(this).text(parseFloat(func).toFixed(size));
604	// 				},
605	// 			}
606	// 		);
607	// });
608
609	//voter-outreach
610	inView(".voter-outreach-col").once("enter", function (el) {
611		// Add the counter-start class
612		jQuery(el).addClass("counter-start");
613		// Initialize the counterUp plugin on the element
614		jQuery(".vo-number").counterUp({
615			delay: 10,
616			time: 2500,
617		});
618	});
619
620	//2024 election report
621	// inView(".efforts-col").once("enter", function (el) {
622	// 	// Add the counter-start class
623	// 	jQuery(el).addClass("counter-start");
624	// 	// Initialize the counterUp plugin on the element
625	// 	jQuery(".efforts-content strong").counterUp({
626	// 		delay: 10,
627	// 		time: 2500,
628	// 	});
629	// });
630
631	//Arizona Report Gradient BG Counter
632	inView(".rwgb-column").once("enter", function (el) {
633		// Add the counter-start class
634		jQuery(el).addClass("counter-start");
635		// Initialize the counterUp plugin on the element
636		jQuery(".ergb-count").counterUp({
637			delay: 10,
638			time: 2000,
639		});
640	});
641
642	//Four Column Icons
643	inView(".four-column-icon-item").once("enter", function (el) {
644		// Add the counter-start class
645		jQuery(el).addClass("counter-start");
646		// Initialize the counterUp plugin on the element
647		jQuery(".countup").counterUp({
648			delay: 10,
649			time: 2000,
650		});
651	});
652
653	//Latest News Slider
654	jQuery(".latest-video-row").slick({
655		dots: false,
656		arrows: true,
657		infinite: false,
658		slidesToShow: 3.7, // Show 4 full slides + half of the next one
659		slidesToScroll: 1,
660		centerMode: false, // Keep slides left-aligned
661		variableWidth: false,
662		prevArrow: '<button type="button" class="slick-prev"><i class="fa-solid fa-chevron-left"></i></button>',
663		nextArrow: '<button type="button" class="slick-next"><i class="far fa-angle-right"></i></button>',
664		responsive: [
665			{
666				breakpoint: 1279,
667				settings: {
668					slidesToShow: 3.5,
669				},
670			},
671			{
672				breakpoint: 991,
673				settings: {
674					slidesToShow: 2.5,
675				},
676			},
677			{
678				breakpoint: 767,
679				settings: {
680					slidesToShow: 1,
681					dots: true,
682					arrows: false,
683					autoplay: true,
684				},
685			},
686		],
687	});
688
689	//Latest News Slider
690	jQuery(".ib-row-area").slick({
691		dots: true,
692		arrows: true,
693		infinite: true,
694		slidesToShow: 3, // Show 4 full slides + half of the next one
695		slidesToScroll: 1,
696		autoplay: true,
697		autoplaySpeed: 4000,
698		responsive: [
699			{
700				breakpoint: 991,
701				settings: {
702					slidesToShow: 2,
703				},
704			},
705			{
706				breakpoint: 767,
707				settings: "unslick",
708			},
709		],
710	});
711
712	//Fullscreen Swiper for News Page
713	jQuery(".news-pro-left").slick({
714		dots: true,
715		arrows: true,
716		infinite: true,
717		autoplay: true,
718		autoplaySpeed: 4000,
719		fade: true,
720		slidesToShow: 1,
721		centerMode: false,
722		vertical: false,
723	});
724
725	//Sub Menu
726	jQuery(".mobile-burger-menu").click(function () {
727		jQuery(".acf-nav-menu").toggle();
728	});
729
730	//Story carousel
731	function initStoryCarousel() {
732		if (jQuery(window).width() >= 768) {
733			if (!jQuery(".story-carousel").hasClass("slick-initialized")) {
734				jQuery(".story-carousel").slick({
735					infinite: true,
736					dots: true,
737					arrows: true,
738					variableWidth: false,
739					slidesToShow: 4,
740					slidesToScroll: 4,
741					responsive: [
742						{
743							breakpoint: 1170,
744							settings: {
745								slidesToShow: 3,
746								slidesToScroll: 3,
747								arrows: false,
748								dots: true,
749							},
750						},
751						{
752							breakpoint: 1023,
753							settings: {
754								slidesToShow: 2,
755								slidesToScroll: 2,
756								arrows: false,
757								dots: true,
758							},
759						},
760						{
761							breakpoint: 767,
762							settings: "unslick",
763						},
764					],
765				});
766			}
767		} else {
768			// Destroy slick below 768px
769			if (jQuery(".story-carousel").hasClass("slick-initialized")) {
770				jQuery(".story-carousel").slick("unslick");
771			}
772		}
773	}
774
775	// Run on load
776	initStoryCarousel();
777
778	// Run on resize
779	jQuery(window).on("resize", initStoryCarousel);
780
781	// Function to handle tab clicks
782	function activateMapTab($tab, updateHash) {
783		var index = $tab.index();
784		var tabId = $tab.find("a").attr("data-tab-id"); // You can use this or data-title
785
786		if (updateHash && tabId) {
787			window.location.hash = tabId;
788		}
789
790		// Update content
791		jQuery("ul.mtabs li").removeClass("current");
792		$tab.addClass("current");
793
794		jQuery(".map-panes").hide();
795		jQuery(".map-panes").eq(index).show();
796
797		//render_td_title();
798
799		// Redraw map after a short delay
800		setTimeout(function () {
801			jQuery(".mapsvg-wrap-all:visible").each(function (i, el) {
802				var id = jQuery(el).attr("data-map-id");
803				MapSVG.getById(id).redraw();
804			});
805		}, 200);
806	}
807
808	function activateMapTabFromHash() {
809		var hash = window.location.hash.replace("#", "");
810
811		if (!hash) {
812			return;
813		}
814
815		try {
816			hash = decodeURIComponent(hash);
817		} catch (e) {}
818
819		var $tab = jQuery("ul.mtabs li").filter(function () {
820			return jQuery(this).find("a").attr("data-tab-id") === hash;
821		}).first();
822
823		if ($tab.length) {
824			if ($tab.hasClass("current") && jQuery(".map-panes").eq($tab.index()).is(":visible")) {
825				return;
826			}
827
828			activateMapTab($tab, false);
829		}
830	}
831
832	// Function to handle tab clicks
833	jQuery("ul.mtabs li").click(function () {
834		activateMapTab(jQuery(this), true);
835
836		return false;
837	});
838
839	activateMapTabFromHash();
840	jQuery(window).on("hashchange", activateMapTabFromHash);
841});
842
843//Preloader
844document.addEventListener("DOMContentLoaded", () => {
845	initCanvasLoaders();
846});
847
848function initCanvasLoaders() {
849	const containers = document.querySelectorAll(".canvas-loader-container");
850
851	// Stagger animation on scroll
852	const observer = new IntersectionObserver(
853		(entries) => {
854			entries.forEach((entry, index) => {
855				if (entry.isIntersecting) {
856					setTimeout(() => {
857						// Initialize the loader logic for this element
858						createResponsiveLoader(entry.target);
859					}, index * 200);
860					observer.unobserve(entry.target);
861				}
862			});
863		},
864		{ threshold: 0.2 },
865	);
866
867	containers.forEach((container) => observer.observe(container));
868}
869
870function createResponsiveLoader(container) {
871	const canvas = container.querySelector("canvas");
872	const textEl = container.querySelector(".canvas-loader-text");
873	const targetPercent = parseInt(container.dataset.percent, 10);
874	const color = container.dataset.color || "#3498db";
875	const trackColor = "#eeeeee";
876
877	let currentProgress = 0; // 0 to 1
878	let animationFrameId;
879	let startTime = null;
880	const duration = 1500;
881
882	// Function to Draw (Stateless drawing based on current dimensions)
883	function draw() {
884		// 1. Get current container dimensions
885		const rect = container.getBoundingClientRect();
886		const size = rect.width; // Because aspect-ratio is 1, width == height
887
888		// Avoid drawing if hidden or 0 size
889		if (size === 0) return;
890
891		// 2. Dynamic Settings
892		const lineWidth = size / 40; // Thickness scales with size
893		const fontSize = size / 5; // Font size scales with size
894
895		// 3. Handle High DPI (Retina)
896		const dpr = window.devicePixelRatio || 1;
897		canvas.width = size * dpr;
898		canvas.height = size * dpr;
899
900		// Important: Canvas CSS width/height must match container
901		// (Already handled by CSS width: 100%, height: 100%)
902
903		const ctx = canvas.getContext("2d");
904		ctx.scale(dpr, dpr);
905
906		// 4. Update Text Size
907		textEl.style.fontSize = `${fontSize}px`;
908		textEl.textContent = Math.round(targetPercent * currentProgress) + "%";
909
910		// 5. Draw Geometry
911		const centerX = size / 2;
912		const centerY = size / 2;
913		const radius = (size - lineWidth) / 2;
914
915		ctx.clearRect(0, 0, size, size);
916
917		// Track
918		ctx.beginPath();
919		ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
920		ctx.strokeStyle = trackColor;
921		ctx.lineWidth = lineWidth;
922		ctx.stroke();
923
924		// Progress Arc
925		const radians = ((targetPercent * currentProgress) / 100) * (2 * Math.PI);
926		if (radians > 0) {
927			ctx.beginPath();
928			ctx.arc(centerX, centerY, radius, 0, radians);
929			ctx.strokeStyle = color;
930			ctx.lineWidth = lineWidth;
931			ctx.lineCap = "round";
932			ctx.stroke();
933		}
934	}
935
936	// Animation Loop
937	function step(timestamp) {
938		if (!startTime) startTime = timestamp;
939		const elapsed = timestamp - startTime;
940		let rawProgress = elapsed / duration;
941
942		if (rawProgress > 1) rawProgress = 1;
943
944		// Ease Out Cubic
945		currentProgress = 1 - Math.pow(1 - rawProgress, 3);
946
947		draw();
948
949		if (rawProgress < 1) {
950			animationFrameId = requestAnimationFrame(step);
951		}
952	}
953
954	// Start Animation
955	animationFrameId = requestAnimationFrame(step);
956
957	// Responsive Handler: ResizeObserver
958	// This ensures if the div changes size (e.g. window resize),
959	// we redraw at the new correct resolution immediately.
960	const resizeObserver = new ResizeObserver(() => {
961		// We redraw using the *current* progress state.
962		// If animation is finished (progress=1), it keeps it crisp at 100%
963		// If animation is running, the loop handles it, but this ensures no flicker.
964		draw();
965	});
966
967	resizeObserver.observe(container);
968}

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.