PageSourceSearch

https://www.cityofplummer.org/repository/designs/parts/scripts/global_resp.js

js cityofplummer.org collected 2026-10-05 23:30:01 UTC 114,203 bytes, 2,769 lines download raw bytes

1// Responsive specific javascript
2var lastsize = 2000;
3
4$(window).on('load', function () {
5	resized();
6	accordion();
7	loadWeather();
8	loadAirQuality();
9	// clipImages();
10	loadDocumentManager();
11	loadPrivacyOverlay();
12});
13
14$(window).resize(function () {
15	if ((Math.abs(lastsize - $(window).width())) > 100) {
16		lastsize = $(window).width();
17		resized();
18	}
19});
20
21$(document).ready(function () {
22	const COOKIE_NAME = "ASP_NET_IFRAME_SESSION";
23	// used for iframe resizing
24	if (document.getElementById("massForm")) {
25		window.addEventListener(
26			"message",
27			function (event) {
28				let iframe = document.getElementById("massForm");
29				let { height } = JSON.parse(event.data);
30				if (height && height > 0 && iframe) {
31					iframe.height = height + "px";
32				}
33			},
34			false
35		);
36	}
37});
38
39function loadPrivacyOverlay() {
40	if (privacyOverlayEnabled === "True") {
41		if (document.cookie.indexOf("privacyAccepted=true") === -1) {
42			if (privacyOverlayText === undefined || privacyOverlayText.length === 0) {
43				privacyOverlayText = "We respect your privacy.";
44			}
45			if (privacyOverlayText2 === undefined || privacyOverlayText2.length === 0) {
46				privacyOverlayText2 = "By continuing, you agree to our use of your information as described in our Privacy Policy. We only collect what is needed to provide our services.";
47			}
48			$('body').append('<div class="privacyOverlay"><div class="privacyBox"><p> </p></div><div class="privacyBox"><button class="acceptCookies">Accept</button></div></div>');
49			$('.privacyOverlay .privacyBox p').html('<h4>' + privacyOverlayText + '</h4>' + privacyOverlayText2);
50			if (privacyOverlayLink.length > 0) {
51				$('.privacyOverlay .privacyBox p').append('<span class="privacyPolicyLink"><a href="' + privacyOverlayLink + '" target="_blank">Privacy Policy</a></span>');
52			}
53			$('.acceptCookies').click(function () {
54				$('.privacyOverlay').hide();
55				document.cookie = "privacyAccepted=true; path=/; max-age=" + 60 * 60 * 24 * 365;
56			});
57		}
58	}
59	else {
60		if (document.cookie.indexOf("privacyAccepted") !== -1)
61			document.cookie = "privacyAccepted=; -1";
62	}
63}
64
65function resized() {
66	if ($(window).width() > 900) {
67		// reset the boxes, then size them according to the biggest for each group.
68		$('.mainContent .responsivebox').css('height', 'auto');
69		var sec = $('.mainContent section.box').has('.responsivebox')
70		sec.each(function (index) {
71			Match.classMatch($(this).find('.responsivebox'));
72		});
73	}
74	else {
75		$('.B_LIST .responsivebox').css('height', 'auto');
76	}
77}
78
79function accordion() {
80	$('.accordion article h2.accordionheader').addClass('closed');
81	$('.accordion article h2.accordionheader').click(function () {
82		if ($(this).hasClass('closed')) {
83			$(this).parent().parent().find('.description').slideUp(500);
84			$(this).parent().parent().find('h2.accordionheader.open').removeClass('open').addClass('closed');
85			$(this).parent().parent().find('h2.accordionheader button').children('i.fa-minus-square-o').removeClass('fa-minus-square-o').addClass('fa-plus-square-o');
86			$(this).find('i').removeClass('fa-plus-square-o').addClass('fa-minus-square-o');
87			$(this).attr("aria-expanded", "true");
88			$(this).siblings('.description').slideDown(500).attr("aria-hidden", "false");
89			$(this).removeClass('closed').addClass('open');
90		}
91		else {
92			$(this).attr("aria-expanded", "false");
93			$(this).siblings('.description').slideUp(500).attr("aria-hidden", "true");
94			$(this).removeClass('open').addClass('closed');
95			$(this).find('i').removeClass('fa-minus-square-o').addClass('fa-plus-square-o');
96		}
97	});
98	$('.accordion .accordionall').click(function () {
99		if ($(this).html() == "Expand All") {
100			$(this).parent().find('article h2.accordionheader').attr("aria-expanded", "true").removeClass('closed').addClass('open');
101			$(this).parent().find('article .description').slideDown(500).attr("aria-hidden", "false");
102			$(this).parent().find('article h2.accordionheader button').children('i.fa-plus-square-o').removeClass('fa-plus-square-o').addClass('fa-minus-square-o');
103			$(this).html('Collapse All');
104		}
105		else {
106			$(this).parent().find('article h2.accordionheader').attr("aria-expanded", "false").removeClass('open').addClass('closed');
107			$(this).parent().find('article .description').slideUp(500).attr("aria-hidden", "true");
108			$(this).parent().find('article h2.accordionheader button').children('i.fa-minus-square-o').removeClass('fa-minus-square-o').addClass('fa-plus-square-o');
109			$(this).html('Expand All');
110		}
111	});
112	$('section .accordion').each(function (index) {
113		$(this).find('h2.accordionheader').first().click();
114	});
115}
116
117function loadWeather() {
118	var url = "/govofficeservices/api/weather/GetWeather.ashx";
119	if ($('.openweather').length) {
120		$.getJSON(url, { format: "json" })
121			.done(function (data) {
122					let currenttemp = Math.round(data.main.temp * 10) / 10;
123					let currentcond = data.weather[0].description;
124					let currentlink = 'https://openweathermap.org/city/' + data.id;
125					//$('.openweather').append('<a href="' + currentlink + '" target="_blank" aria-label="opens weather in a new tab">' + currenttemp + '°&nbsp;&nbsp;' + currentcond + '</a>');
126					$('.openweather').append('<a href="#" role="button">' + currenttemp + '°&nbsp;&nbsp;' + currentcond + '</a>');
127			});
128	}
129}
130
131function loadAirQuality() {
132	var url = "/govofficeservices/api/weather/GetAirQuality.ashx";
133	if ($('.airquality').length) {
134		$.getJSON(url, { format: "json" })
135			.done(function (data) {
136			let aqi = data.list[0].main.aqi;
137			let q = 'Good';
138			switch (aqi) {
139				case 2:	q = 'Fair';	break;
140				case 3:	q = 'Moderate';	break;
141				case 4:	q = 'Poor';	break;
142				case 5:	q = 'Very Poor'; break;
143				default: break;
144			}
145			$('.airquality').append(q);
146		});
147	}
148}
149
150function clipImages() {
151	var clipImage = $('div.image.circle > img');
152	var offset = 0;
153	clipImage.each(function (index) {
154		if ($(this).width() > $(this).height()) {
155			$(this).css('clip-path', 'circle(' + (($(this).height() / 2) + 'px at 50% 50%)'));
156		}
157		else {
158			$(this).css('clip-path', 'circle(' + (($(this).width() / 2) + 'px at 50% 50%)'));
159		}
160	});
161	clipImage = $('div.image.square > img');
162	clipImage.each(function (index) {
163		if ($(this).width() > $(this).height()) {
164			offset = ($(this).width() - $(this).height()) / 2;
165			$(this).css('clip-path', 'inset(0 ' + offset + 'px 0 ' + offset + 'px)');
166		}
167		else {
168			offset = ($(this).height() - $(this).width()) / 2;
169			$(this).css('clip-path', 'inset(' + offset + 'px 0 ' + offset + 'px 0)');
170		}
171	});
172}
173
174let map;
175function initMap() {
176	let locations = document.getElementsByClassName("hidden location");
177	let addresses = document.getElementsByClassName("hidden address");
178	let loc = '';
179	let first = '';
180	// Get the first location with values to center the map
181	for (let i = 0; i < locations.length; i++) {
182		loc = locations[i].innerHTML;
183		if (loc.length > 0) {
184			first = loc;
185			break;
186		}
187	}
188	if (first.length > 0) {
189		let c = first.split(",");
190		let myLatLng = { lat: parseFloat(c[0]), lng: parseFloat(c[1]) };
191		//let myLatLng = new google.maps.LatLng(locations[0].innerHTML);
192		map = new google.maps.Map(document.getElementById('dirmap'), {
193			center: myLatLng,
194			zoom: 12
195		});
196	}
197
198	var markers = new Array();
199	var infoWindow = new google.maps.InfoWindow();
200	for (let i = 0; i < locations.length; i++) {
201		let loc = locations[i].innerHTML;
202		let addr = addresses[i].innerHTML;
203		if (loc.length > 0) {
204			let c = loc.split(",");
205			if (c.length > 2) {
206				let myLatLng = { lat: parseFloat(c[0]), lng: parseFloat(c[1]) };
207				let cont = '<div class="info"><h3>' + c[2] + '</h3><div><p>' + addr + '</p></div></div>';
208				const marker = new google.maps.Marker({
209					position: myLatLng,
210					map: map
211				});
212				markers[i] = marker;
213				(function (marker) {
214					google.maps.event.addListener(marker, "click", function (e) {
215						if (infoWindow) {
216							infoWindow.close();
217						}
218						infoWindow.setContent(cont);
219						infoWindow.open(map, marker);
220					});
221				})(marker);
222			}
223		}
224	}
225	$('.showonmap').click(function () {
226		let id = $(this).index('.showonmap');
227		//console.log(j);
228		google.maps.event.trigger(markers[id], 'click');
229		document.getElementById('dirmap').scrollIntoView({ behavior: 'smooth', block: 'center' });
230	});
231}
232
233$(document).ready(function() {
234	if ($('.namesearch').length) {
235		$('.namesearchbox i').click(function () {
236			if ($(this).hasClass('fa-magnifying-glass')) {
237				searchtitles();
238			}
239			else {
240				resetsearchtitles();
241			}
242		});
243		$(document).on('keyup', function(e) {
244			if (e.key == "Enter") searchtitles();
245			if (e.key == "Escape") resetsearchtitles();
246		});
247	}
248});
249
250function searchtitles() {
251	let filter = $('.namesearch').val().toLowerCase();
252	$('h2').each(function () {
253		if ($(this).text().toLowerCase().indexOf(filter) > -1) {
254			$(this).closest('article').addClass('found');
255		}
256		else {
257			$(this).closest('article').addClass('filtered');
258		}
259	});
260	$('i.fa-magnifying-glass').addClass('fa-circle-xmark').removeClass('fa-magnifying-glass');
261}
262
263function resetsearchtitles() {
264	$('i.fa-circle-xmark').addClass('fa-magnifying-glass').removeClass('fa-circle-xmark');
265	$('.filtered').removeClass('filtered');
266	$('.found').removeClass('found');
267}
268
269// document manager
270function loadDocumentManager() {
271	if ($('.docmanfolder').length > 0) {
272		$('.docmanContainer').each(function () {
273			let c = $(this).attr('id');
274			let f = c;
275			$(this).attr('currentfolder', f);
276			GetCurrentFolder(c, f);
277		});
278		rebindDocMan();
279		$('#docmanSearchButton').click(function () {
280			let c = $(this).closest('.docmanContainer').attr('id');
281			let f = $(this).closest('.docmanContainer').attr('currentfolder');
282			DocSearch(c, f);
283		});
284		$('.docmanSearch i.fa-magnifying-glass').click(function () {
285			let c = $(this).closest('.docmanContainer').attr('id');
286			let f = $(this).closest('.docmanContainer').attr('currentfolder');
287			DocSearch(c, f);
288		});
289		$('.docsearch').on('keyup', function (e) {
290			if (e.key == "Enter") {
291				let c = $(this).closest('.docmanContainer').attr('id');
292				let f = $(this).closest('.docmanContainer').attr('currentfolder');
293				DocSearch(c, f);
294			}
295		});
296	}
297}
298
299function DocSearch(c, f) {
300	if ($('.docmanContainer#' + c + ' .docsearch').val().length > 0) {
301		$('.docmanContainer#' + c + ' .subfolders').html('');
302		let s = $('.docmanContainer#' + c).attr('sec');
303		let k = $('.docmanContainer#' + c + ' .docsearch').val();
304		// k = k.replace(/'/g, '');
305		// k = k.replace(/-/g, '');
306		// k = k.replace(/"/g, '');
307		$.ajax({
308			type: "GET",
309			url: "/admin/dm/Public/SearchFolder/?folderid=" + f + "&sec=" + s + "&search=" + k ,
310			cache: false,
311			async: false,
312			contentType: "application/json; charset=utf-8",
313			dataType: "json",
314			success: function (data) {
315				let primary = '.docmanContainer#' + c;
316				if ($(primary + ' div.breadcrumb > div:contains("Search Results")').length == 0) {
317					$(primary + ' div.breadcrumb > div').append(' > <span id="s' + data.folderId + '">Search Results</span>');
318				}
319				$(primary + ' .docmanfolder .subfolders').append('<div class="headings"><div><i class="fa-regular fa-arrow-up"></i>Name</div><div>Last Modified Date</div><div><i class="fa-regular fa-arrow-right"></i></div></div>');
320
321				$.each(data, function (i, file) {
322					let df = String(file.fileDate);
323					df = df.substring(0, df.indexOf('T'));
324					if (df.startsWith('1900')) df = '';
325
326
327					let fn = file.fileName;
328					if (file.FileTitle != null && file.FileTitle.length > 0) fn = file.FileTitle;
329
330					$(primary + ' .docmanfolder .subfolders').append('<div class="file" filename="' + fn + '" id="' + file.fileId + '"><div>' + GetFileIcon(file.fileType) + fn + '</div><div>' + df + '</div><div><i class="fa-solid fa-download"></i></div></div>');
331				});
332				rebindDocMan();
333			},
334			failure: function (data) { console.log('failure: ' + JSON.stringify(data)); },
335			error: function (data) { console.log('error: ' + JSON.stringify(data)); }
336		});
337		$('.docmanContainer#' + c + ' .docsearch').val('');
338	}
339}
340
341function rebindDocMan() {
342	$('.docmanfolder .folder').unbind('click').on('click', function (e) {
343		let c = $(this).closest('.docmanContainer').attr('id');
344		let f = $(this).attr('id');
345		GetCurrentFolder(c, f);
346	});
347	$('.docmanfolder .file').unbind('click').on('click', function (e) {
348		let clickedfileid = $(this).attr('id');
349		let clickedfilename = $(this).attr('filename');
350		window.open('/uploads/dm/' + clickedfileid + '/' + encodeForPath(clickedfilename), '_blank', 'noreferrer');
351	});
352	$('.docmanContainer .breadcrumb > div > span').unbind('click').on('click', function (e) {
353		let c = $(this).closest('.docmanContainer').attr('id');
354		let f = $(this).attr('id').substring(1);
355		$(this).nextAll().remove();
356		GetCurrentFolder(c, f);
357	})
358}
359
360function GetCurrentFolder(c, f) {
361	$('.docmanContainer#' + c + ' .subfolders').html('');
362	let primary = '.docmanContainer#' + c;
363	let s = $(primary).attr('sec');
364	$(primary).attr('currentfolder', f);
365	$.ajax({
366		type: "GET",
367		url: "/admin/dm/Public/GetFolderById/?folderid=" + f + "&sec=" + s,
368		cache: false,
369		async: false,
370		contentType: "application/json; charset=utf-8",
371		dataType: "json",
372		success: function (data) {
373			if (c == f)
374				$(primary + ' div.breadcrumb > div').html('<span id="s' + data.folderId + '">' + data.folderName + '</span>');
375			else {
376				$active = $(primary + ' div.breadcrumb > div').find('#s' + f)
377				if ($active.length == 0)
378					$(primary + ' div.breadcrumb > div').append(' > <span id="s' + data.folderId + '">' + data.folderName + '</span>');
379			}
380			$(primary + ' .docmanfolder .subfolders').append('<div class="headings"><div><i class="fa-regular fa-arrow-up"></i>Name</div><div>Last Modified Date</div><div><i class="fa-regular fa-arrow-right"></i></div></div>');
381
382			$.each(data.dmfolders, function (i, subfolder) {
383				let d = String(subfolder.lastModified);
384				d = d.substring(0, d.indexOf('T'));
385				d = '';
386				$(primary + ' .docmanfolder .subfolders').append('<div class="folder" id="' + subfolder.folderId + '"><div><i class="fa-solid fa-folder"></i>' + subfolder.folderName + '</div><div>' + d + '</div><div><i class="fa-regular fa-arrow-right"></i></div></div>');
387			});
388			$.each(data.dmfiles, function (i, file) {
389				let df = String(file.fileDate);
390				df = df.substring(0, df.indexOf('T'));
391
392				let fn = file.fileName;
393				if (file.FileTitle != null && file.FileTitle.length > 0) fn = file.FileTitle;
394				$(primary + ' .docmanfolder .subfolders').append('<div class="file" filename="' + fn + '" id="' + file.fileId + '"><div>' + GetFileIcon(file.fileType) + fn + '</div><div>' + df + '</div><div><i class="fa-solid fa-download"></i></div></div>');
395			});
396			rebindDocMan();
397		},
398		failure: function (data) { console.log('failure: ' + JSON.stringify(data)); },
399		error: function (data) {
400			console.log('error: ' + JSON.stringify(data));
401			if (data.status == 401) {
402				$('div.breadcrumb').html('You will need to authenticate to see this content.');
403				$('div.searchContainer').hide();
404
405			}
406		}
407	});
408}
409
410function GetFileIcon(filetype) {
411	switch (filetype.toLowerCase()) {
412		case 'jpeg':
413		case 'jpg':
414		case 'png':
415		case 'gif':
416			return '<i class="fa-solid fa-file-image"></i>';
417		case 'doc':
418		case 'docx':
419			return '<i class="fa-solid fa-file-word"></i>';
420		case 'xls':
421		case 'xlsx':
422			return '<i class="fa-solid fa-file-excel"></i>';
423		case 'pdf':
424			return '<i class="fa-solid fa-file-pdf"></i>';
425		case 'zip':
426		case '7z':
427			return '<i class="fa-solid fa-file-zipper"></i>';
428		case 'avi':
429		case 'm4v':
430		case 'mp4':
431		case 'mov':
432		case 'mkv':
433			return '<i class="fa-solid fa-file-video"></i>';
434		case 'mp3':
435		case 'ogg':
436		case 'wav':
437			return '<i class="fa-solid fa-file-audio"></i>';
438		default:
439			return '<i class="fa-solid fa-file"></i>';
440	}
441}
442
443function taxcalc(containerId) {
444	// Attach event listener to the input field
445	document.getElementById(containerId).querySelector('.propValue').addEventListener('input', handleInputChange);
446
447	// Attach event listener to the radio buttons
448	var ratiosContainer = document.getElementById(containerId).querySelector('.Ratios');
449	ratiosContainer.addEventListener('change', handleRatioChange);
450
451
452	// Attach event listener to the dropdown
453	var jurisdictionDropdown = document.getElementById(containerId).querySelector('.jurisdictionDropdown');
454	jurisdictionDropdown.addEventListener('change', handleDropdownChange);
455
456	// Attach event listener to the base rate dropdown
457	var baseRatecont = document.getElementById(containerId).querySelector('.baseRatecont');
458	baseRatecont.addEventListener('change', handleBaseRateChange);
459}
460
461function hideElements(arraykey) {
462	var taxcontainer = document.querySelector(`#${arraykey}.tax-calculator`);
463	var dropdown = taxcontainer.querySelector(".jurisdictionDropdown");
464	var jursCont = taxcontainer.querySelector(".jurisdictionDropdownCont");
465	var middleCont = taxcontainer.querySelector(".middleCont");
466	var baseFactorBody = taxcontainer.querySelector(".baseFactorBody");
467	var baseFactor = taxcontainer.querySelector(".baseFactor");
468	var baserate = taxcontainer.querySelector(".baseRates");
469	var baseRatecont = taxcontainer.querySelector(".baseRatecont");
470	var creditTableContainer = taxcontainer.querySelector(".credits");
471	var tableCont = taxcontainer.querySelector(".taxes");
472	var ratiocontainer = taxcontainer.querySelector(".radioOptions");
473	var millcontainer = taxcontainer.querySelector(".millLeviesBody");
474	var creditBody = taxcontainer.querySelector(".millLeviesCREDITBody");
475	var juryCreditBody = taxcontainer.querySelector(".juryCreditBody");
476	var juryBody = taxcontainer.querySelector(".juryBody");
477	var CalcdAssessmentVal = taxcontainer.querySelector('.CalcdAssessmentVal');
478	var ratioCreditBody = taxcontainer.querySelector(".ratioCreditBody");
479	var ratioBody = taxcontainer.querySelector(".ratioBody");
480
481	var ratioArray = ratioArrays[arraykey] || [];
482	var levyArray = levyArrays[arraykey] || [];
483	var jurLevyArray = jurLevyArrays[arraykey] || [];
484
485	if (ratioArray.length === 0) {
486		ratiocontainer.style.display = 'none';
487	}
488
489	if (levyArray.length === 0 && millcontainer.childElementCount === 0 && ratioBody.childElementCount === 0 && juryBody.childElementCount === 0) {
490		tableCont.style.display = 'none';
491	} else {
492		tableCont.style.display = 'inline-block';
493	}
494
495	if (jurLevyArray.length === 0 && baseFactorBody.childElementCount === 0) {
496		middleCont.style.display = 'none';
497	} else {
498		middleCont.style.display = 'inline-block';
499	}
500
501	if (jurLevyArray.length === 0) {
502		jursCont.style.display = 'none';
503	} else {
504		dropdown.style.display = 'inline-block';
505	}
506
507	if (baseFactorBody.childElementCount != 0) {
508		baseFactor.style.display = 'inline-block';
509	} else {
510		baseFactor.style.display = 'none';
511	}
512
513	if (baserate.childElementCount === 0) {
514		baseRatecont.style.display = 'none';
515	} else {
516		baseRatecont.style.display = 'inline-block';
517	}
518
519	if (creditBody.childElementCount === 0 && ratioCreditBody.childElementCount === 0 && juryCreditBody.childElementCount === 0) {
520		creditTableContainer.style.display = 'none';
521	} else {
522		creditTableContainer.style.display = 'inline-block';
523	}
524
525}
526
527
528
529function calculate(arraykey) {
530	var taxcontainer = document.querySelector(`#${arraykey}.tax-calculator`);
531	const calcResultElements = taxcontainer.querySelectorAll('.millLevy');
532	const basefactorElement = taxcontainer.querySelector('.basefactor');
533	const baseRateElement = getSelectedBaseRate(arraykey);
534	var assessedValue = getAssessedValue(arraykey);
535	if (ratioArrays[arraykey].length === 0) {
536		assessedValue = taxcontainer.querySelector('.propValue').value;
537		//assessedValue = parseFloat(assessedValue.replace(/,/g, ''));
538	}
539
540	let total = 0;
541	//use function to removed comma from input
542	//console.log("assessed value: ", assessedValue );
543	calcResultElements.forEach(function (element) {
544		const value = parseFloat(element.textContent);
545		if (!isNaN(value) && value != 0) {
546			total += value;
547		}
548	});
549
550	total = total * assessedValue;
551	if (basefactorElement && total != 0) {
552		total = total * parseFloat(basefactorElement.textContent);
553	}
554	total = total.toFixed(2) / 1000;
555	if (baseRateElement ) {
556		total += (baseRateElement);
557	}
558	if (total === 0 && assessedValue) {
559		total = parseFloat(assessedValue);
560	}
561	if (total != 0 && !isNaN(total)) {
562		let roundedNum = Math.round(total * 100) / 100; //for 2 decimal places
563		total = roundedNum;
564
565	}
566	// Display total
567	const resultDiv = taxcontainer.querySelector('.results');
568	resultDiv.textContent = "$" + total.toFixed(2);
569}
570
571function displayassessedVal(assessedVal, arraykey) {
572	var taxcontainer = document.querySelector(`#${arraykey}.tax-calculator`);
573	var assessmentvalCont = taxcontainer.querySelector(".assessedValue");
574	if (assessmentvalCont) {
575		assessmentvalCont.innerHTML = ''; // Clear existing content
576		// Create a new div element for assessed value
577		var assessValDiv = document.createElement('div');
578		assessValDiv.className = 'calcdval';
579		assessValDiv.innerHTML = '<span class="valSpan">' + assessedVal.toFixed(2) + '</span>';
580		// Clear the container's contents
581		assessmentvalCont.innerHTML = '';
582
583		// Append the new assessed value div to the container
584		assessmentvalCont.appendChild(assessValDiv);
585	}
586}
587function displayRatios(arraykey) {
588	var containerElement = document.querySelector("#" + arraykey);
589	if (!containerElement) {
590		console.error("Container element not found.");
591		return;
592	}
593
594	var currArray = ratioArrays[arraykey];
595	if (!currArray) {
596		//console.error("Ratios array not found.");
597		return;
598	}
599	var ratiosContainer = containerElement.querySelector('.Ratios');
600	if (!ratiosContainer) {
601		//console.error("Ratios container not found.");
602		return;
603	}
604
605	var select = containerElement.querySelector('.ratioDropdown');
606
607	select.className = 'ratioSelect';
608
609	// Populate options from currArray
610	currArray.forEach(function (item) {
611		var option = document.createElement('option');
612		option.value = item.guid;
613		option.textContent = item.title + ": " + item.percent + "%";
614		select.appendChild(option);
615	});
616}
617
618function displayMillLevies() {
619	var containerElement = document.querySelector("#" + uniqueID);
620	if (!containerElement) {
621		console.error("Container element not found.");
622		return;
623	}
624	var currArray = levyArrays[uniqueID];
625	if (!currArray) {
626		console.error("Levy array not found.");
627		return;
628	}
629	var millLeviesContainer = containerElement.querySelector('.leviesTableContainer');
630	if (!millLeviesContainer) {
631		console.error("Mill levies container not found.");
632		return;
633	}
634
635	// Populate table rows with array data
636	currArray.forEach(function (item) {
637		// Create new table row
638		var row = document.createElement('tr');
639		// Create and populate cells
640		var titleCell = document.createElement('td');
641		titleCell.textContent = item.title;
642		var numberCell = document.createElement('td');
643		numberCell.className = 'millLevy';
644		numberCell.textContent = item.number;
645		var calcCell = document.createElement('td');
646		calcCell.className = 'calcResult';
647		calcCell.textContent = '0.00';
648		// Append cells to row
649		row.appendChild(titleCell);
650		row.appendChild(numberCell);
651		row.appendChild(calcCell);
652		// Append row to the appropriate table body
653		if (item.number >= 0) {
654			var body = millLeviesContainer.querySelector(".millLeviesBody");
655			body.appendChild(row);
656		} else {
657			var creditbody = millLeviesContainer.querySelector(".millLeviesCREDITBody");
658			if (!creditbody) {
659				creditbody = document.createElement('tbody');
660				creditbody.className = 'millLeviesCREDITBody';
661				millLeviesContainer.appendChild(creditbody);
662			}
663			creditbody.appendChild(row);
664		}
665	});
666}
667
668function displayRatioLevies(extraLevyArray, arraykey) {
669	var taxcontainer = document.querySelector(`#${arraykey}.tax-calculator`);
670	var body = taxcontainer.querySelector(".ratioBody");
671	// Clear existing content in millLeviesBody
672	body.innerHTML = '';
673	var creditbody = taxcontainer.querySelector(".ratioCreditBody");
674	// Clear existing content in millLeviesCREDITBody
675	creditbody.innerHTML = '';
676
677	// Populate table rows with array data
678	extraLevyArray.forEach(function (item) {
679		//if item number is greater than or equal to 0, add to table
680		if (item.number >= 0) {
681			var row = body.insertRow();
682			var titleCell = row.insertCell();
683			titleCell.textContent = item.name;
684			var numberCell = row.insertCell();
685			numberCell.className = 'millLevy';
686			numberCell.textContent = item.number;
687			var calcCell = row.insertCell();
688			calcCell.className = 'calcResult';
689			var calcResult = calcMillLevy(item.number, arraykey);
690			calcCell.textContent = calcResult;
691		} else {
692			var row = creditbody.insertRow();
693			var titleCell = row.insertCell();
694			titleCell.textContent = item.name;
695			var numberCell = row.insertCell();
696			numberCell.className = 'millLevy';
697			numberCell.textContent = item.number;
698			var calcCell = row.insertCell();
699			calcCell.className = 'calcResult';
700			var calcResult = calcMillLevy(item.number, arraykey);
701			calcCell.textContent = calcResult;
702		}
703	});
704
705	hideElements(arraykey);
706}
707
708function displayBaseRate(array, containerclass, uniqueId) {
709	var container = document.querySelector(`#${uniqueId} .${containerclass}`);
710	if (container) {
711		container.innerHTML = ''; // Clear existing content
712
713		// Create radio button options
714		array.forEach(function (item, index) {
715			var radioInput = document.createElement('input');
716			radioInput.type = 'radio';
717			radioInput.name = 'baseRate_' + uniqueId;
718			radioInput.value = item.number; // change the value attribute based data
719			var label = document.createElement('label');
720			label.appendChild(radioInput);
721			label.appendChild(document.createTextNode(item.name + ": $" + item.number)); // adjust the text content based data
722			container.appendChild(label);
723
724			// Check the first radio button option
725			if (index === 0) {
726				radioInput.checked = true;
727			}
728		});
729	}
730}
731
732
733function displayBaseFactor(Ratio, arraykey) {
734	var taxcontainer = document.querySelector(`#${arraykey}.tax-calculator`);
735	var middleCont = taxcontainer.querySelector(".middleCont");
736	var baseFactor = taxcontainer.querySelector(".baseFactor");
737	var body = taxcontainer.querySelector(".baseFactorBody");
738
739	// If Ratio has a factor, display the base factor table
740	if (Ratio.hasfactor === '1') {
741		if (middleCont && middleCont.length > 0) {
742			middleCont.style.display = 'inline-block';
743		}
744		body.innerHTML = '';
745		// Populate table rows with array data
746		var row = body.insertRow();
747		var nameCell = row.insertCell();
748		nameCell.textContent = Ratio.factorLabel;
749		var numberCell = row.insertCell();
750		numberCell.className = 'basefactor';
751		numberCell.textContent = Ratio.factor;
752	}
753	else {
754		// Clear existing content in baseFactorBody
755		body.innerHTML = '';
756		hideElements(arraykey);
757	}
758}
759
760function displayJuryLevies(extraLevyArray,arraykey) {
761	var taxcontainer = document.querySelector(`#${arraykey}.tax-calculator`);
762	var body = taxcontainer.querySelector(".juryBody");
763	// Clear existing content in millLeviesBody
764	body.innerHTML = '';
765	var creditbody = taxcontainer.querySelector(".juryCreditBody");
766	// Clear existing content in millLeviesCREDITBody
767	creditbody.innerHTML = '';
768
769	// Populate table rows with array data
770	extraLevyArray.forEach(function (item) {
771		//if item number is greater than or equal to 0, add to table
772		if (item.number >= 0) {
773			var row = body.insertRow();
774			var titleCell = row.insertCell();
775			titleCell.textContent = item.name;
776			var numberCell = row.insertCell();
777			numberCell.className = 'millLevy';
778			numberCell.textContent = item.number;
779			var calcCell = row.insertCell();
780			calcCell.className = 'calcResult';
781			var calcResult = calcMillLevy(item.number, arraykey);
782			calcCell.textContent = calcResult;
783		} else {
784			var row = creditbody.insertRow();
785			var titleCell = row.insertCell();
786			titleCell.textContent = item.name;
787			var numberCell = row.insertCell();
788			numberCell.className = 'millLevy';
789			numberCell.textContent = item.number;
790			var calcCell = row.insertCell();
791			calcCell.className = 'calcResult';
792			var calcResult = calcMillLevy(item.number, arraykey);
793			calcCell.textContent = calcResult;
794		}
795	});
796
797	hideElements(arraykey);
798}
799
800// Function to update Ratio specific Levies based on the selected Ratio
801function updateRatioLevies(ratioguid, arraykey) {
802	var ratioArray = ratioArrays[arraykey];
803	// Find the selected jurisdiction in the array
804	var selectedRatio = ratioArray.find(function (ratio) {
805		return ratio.guid === ratioguid;
806	});
807
808	if (selectedRatio) {
809		// Check if selectedJurisdiction has the ExtraLevy property
810		if (selectedRatio.ExtraLevy) {
811			//get array of ExtraLevy for the selected jurisdiction
812			var extraLevyArray = selectedRatio.ExtraLevy;
813			// Call the displayJuryLevies function with the correct array name
814			displayRatioLevies(extraLevyArray, arraykey);
815		} else {
816			console.log("Selected ratio does not have ExtraLevy property");
817		}
818	} else {
819		console.error("Selected ratio not found in the array");
820	}
821}
822
823// Function to update Jurisdiction specific Levies based on the selected jurisdiction
824function updateBaseRate(ratioguid, arraykey) {
825	var ratioArray = ratioArrays[arraykey];
826	var desiredRatio = ratioArray.find(function (ratio) {
827		return ratio.guid === ratioguid;
828	});
829
830	// Check if the desired ratio exists
831	if (desiredRatio) {
832		// Access the BaseRate array of the desired ratio
833		var baseRateArray = desiredRatio.BaseRate;
834		displayBaseRate(baseRateArray, 'baseRates', arraykey);
835	} else {
836		console.error("Desired ratio not found.");
837	}
838}
839
840function updateMillLevies(arraykey) {
841	var levyArray = levyArrays[arraykey];
842	var taxcontainer = document.querySelector(`#${arraykey}.tax-calculator`);
843	var body = taxcontainer.querySelector(".millLeviesBody");
844	// Clear existing content in millLeviesBody
845	body.innerHTML = '';
846
847	var creditbody = taxcontainer.querySelector(".millLeviesCREDITBody");
848	// Clear existing content in millLeviesCREDITBody
849	creditbody.innerHTML = '';
850
851	// Populate table rows with array data
852	levyArray.forEach(function (item) {
853		//if item number is greater than or equal to 0, add to table
854		if (item.number >= 0) {
855			var row = body.insertRow();
856			var titleCell = row.insertCell();
857			titleCell.textContent = item.title;
858			var numberCell = row.insertCell();
859			numberCell.className = 'millLevy';
860			numberCell.textContent = item.number;
861			var calcCell = row.insertCell();
862			calcCell.className = 'calcResult';
863			var calcResult = calcMillLevy(item.number, arraykey);
864			calcCell.textContent = calcResult;
865		} else {
866			var row = creditbody.insertRow();
867			var titleCell = row.insertCell();
868			titleCell.textContent = item.title;
869			var numberCell = row.insertCell();
870			numberCell.className = 'millLevy';
871			numberCell.textContent = item.number;
872			var calcCell = row.insertCell();
873			calcCell.className = 'calcResult';
874			var calcResult = calcMillLevy(item.number, arraykey);
875			calcCell.textContent = calcResult;
876		}
877	});
878}
879
880// Function to update Jurisdiction specific Levies based on the selected jurisdiction
881function updateJuryLevies(jurisdictionguid, arraykey) {
882	var jurylevyarray = jurLevyArrays[arraykey];
883	// Find the selected jurisdiction in the array
884	var selectedJurisdiction = jurylevyarray.find(function (jurisdiction) {
885		return jurisdiction.guid === jurisdictionguid;
886	});
887
888	if (selectedJurisdiction) {
889		// Check if selectedJurisdiction has the ExtraLevy property
890		if (selectedJurisdiction.ExtraLevy) {
891			//get array of ExtraLevy for the selected jurisdiction
892			var extraLevyArray = selectedJurisdiction.ExtraLevy;
893			// Call the displayJuryLevies function with the correct array name
894			displayJuryLevies(extraLevyArray, arraykey);
895		} else {
896			console.error("Selected jurisdiction does not have ExtraLevy property");
897		}
898	} else {
899		console.error("Selected jurisdiction not found in the array");
900	}
901}
902
903function updateBaseFactor(ratioguid, arraykey) {
904	var ratioArray = ratioArrays[arraykey];
905	var selectedRatio = ratioArray.find(function (ratio) {
906		return ratio.guid === ratioguid;
907	});
908	displayBaseFactor(selectedRatio, arraykey);
909}
910
911//function to handle input change event
912function handleInputChange(event) {
913	var propvalueInput = event.target.value;
914	propvalueInput = parseFloat(propvalueInput.replace(/,/g, ''));
915	var container = event.target.closest('.tax-calculator');
916	var arraykey = container.id;
917	var selectedRatioguid = getSelectedRatio(arraykey);
918	//if the selected ratio and assessed value are not null
919	if (selectedRatioguid) {
920		calculateAssessedValue(selectedRatioguid, arraykey, propvalueInput);
921		updateRatioLevies(selectedRatioguid, arraykey);
922
923	}
924	updateMillLevies(arraykey);
925
926	if (getJurisdiction(arraykey)) {
927		updateJuryLevies(getJurisdiction(arraykey), arraykey);
928	}
929	hideElements(arraykey);
930
931	calculate(arraykey);
932}
933
934function handleRatioChange(event) {
935	var selectedRatioguid = event.target.value;
936	var container = event.target.closest('.tax-calculator');
937	var arraykey = container.id;
938	var propertyvalue = getPropValue(arraykey);
939	if (selectedRatioguid != "null") {
940		updateBaseRate(selectedRatioguid, arraykey);
941		updateBaseFactor(selectedRatioguid, arraykey);
942
943		if (propertyvalue) {
944			propertyvalue = (propertyvalue.replace(/,/g, ''));
945			calculateAssessedValue(selectedRatioguid, arraykey, propertyvalue);
946			updateMillLevies(arraykey);
947					updateRatioLevies(selectedRatioguid, arraykey);
948
949		}
950		if (getJurisdiction(arraykey)) {
951			updateJuryLevies(getJurisdiction(arraykey), arraykey);
952		}
953
954	hideElements(arraykey);
955	calculate(arraykey);}
956}
957
958// Function to handle base rate radio change event
959function handleBaseRateChange(event) {
960	var container = event.target.closest('.tax-calculator');
961	var arraykey = container.id;
962	calculate(arraykey);
963	hideElements(arraykey);
964
965}
966// Function to handle dropdown change event
967function handleDropdownChange(event) {
968	var jurisdistionguid = event.target.value;
969	var container = event.target.closest('.tax-calculator');
970	var arraykey = container.id;
971
972	updateMillLevies(arraykey);
973	//var selectedJurisdiction = document.getElementsByClassName('jurisdictionDropdown').value;
974
975	if (getJurisdiction(arraykey)) {
976		updateJuryLevies(getJurisdiction(arraykey), arraykey);
977	}
978	hideElements(arraykey);
979	calculate(arraykey);
980}
981
982//function to get assessed value
983function getAssessedValue(arraykey) {
984	var assessedValue = null;
985	var taxcontainer = document.querySelector(`#${arraykey}.tax-calculator`);
986	try {
987		// Get all elements with the class name 'valSpan'
988		var element = taxcontainer.querySelector('.valSpan');
989		var value = element.textContent;
990		// If the value is not null or empty, parse it to a float
991		if (value) {
992			assessedValue = parseFloat(value).toFixed(2);
993		}
994
995	} catch (error) {
996		//console.error("Error in getAssessedValue:", error);
997	}
998return assessedValue;
999}
1000
1001//get input value
1002function getPropValue(arraykey) {
1003	var taxcontainer = document.querySelector(`#${arraykey}.tax-calculator`);
1004	try {
1005		var propValue = taxcontainer.querySelector('.propValue').value;
1006		//propValue = parseFloat(propValue.replace(/,/g, ''));
1007		return propValue;
1008	} catch { }
1009}
1010
1011function getSelectedRatio(arraykey) {
1012	var taxcontainer = document.querySelector(`#${arraykey}.tax-calculator`);
1013	try {
1014		// Get the dropdown element
1015		var dropdown = taxcontainer.querySelector('.ratioSelect');
1016		// Get the selected value
1017		var selectedRatio = dropdown.value;
1018		//if selectRatio has no value, return null
1019		if (selectedRatio === "null") {
1020            return null;
1021        }
1022		return selectedRatio;
1023	} catch (error) {
1024		// Handle any potential errors here
1025		console.error("Error in getSelectedRatio:", error);
1026		return null; // Return null if an error occurs
1027	}
1028}
1029
1030function getSelectedBaseRate(arraykey) {
1031	var taxcontainer = document
1031.querySelector(`#${arraykey}.tax-calculator`);
1032	var selectedbaseRateInput = taxcontainer.querySelector('input[name="baseRate_' + arraykey + '"]:checked');
1033	if (selectedbaseRateInput) {
1034		return parseFloat(selectedbaseRateInput.getAttribute('value'));
1035	} else {
1036		return null;
1037	}
1038}
1039
1040//function to get selected jurisdiction
1041function getJurisdiction(arraykey) {
1042	var taxcontainer = document.querySelector(`#${arraykey}.tax-calculator`);
1043	try {
1044		// Get the dropdown element
1045		var dropdown = taxcontainer.querySelector('.jurisdictionDropdown');
1046		// Get the selected value
1047		var selectedJurisdiction = dropdown.value;
1048		return selectedJurisdiction;
1049	} catch (error) {
1050		// Handle any potential errors here
1051		console.error("Error in getJurisdiction:", error);
1052		return null; // Return null if an error occurs
1053	}
1054}
1055
1056function calculateAssessedValue(ratioguid, arraykey, propvalueInput) {
1057	var ratioArray = ratioArrays[arraykey];
1058	var selectedRatio = ratioArray.find(function (ratio) {
1059		return ratio.guid === ratioguid;
1060	});
1061	var assessedValue = propvalueInput * ((selectedRatio.percent) / 100);
1062	displayassessedVal(assessedValue, arraykey);
1063}
1064
1065//calc the mill levy * assessed value
1066function calcMillLevy(millLevy, arraykey) {
1067	var assessedValue = getAssessedValue(arraykey);
1068	if (ratioArrays[arraykey].length === 0) {
1069		assessedValue = document.querySelector(`#${arraykey}.tax-calculator .propValue`).value;
1070	}
1071	var tax = millLevy * assessedValue / 1000;
1072	//make tax two decimal places
1073	tax = tax.toFixed(2);
1074	return tax;
1075}
1076
1077window.addEventListener('load', initializeTaxCalculators);
1078
1079function initializeTaxCalculators() {
1080	var calculatorContainers = document.querySelectorAll('.tax-calculator');
1081	calculatorContainers.forEach(function (container) {
1082		var containerId = container.id;
1083		uniqueID = containerId;
1084		taxcalc(containerId);
1085		displayRatios(containerId);
1086		displayMillLevies();
1087		hideElements(containerId);
1088	});
1089}
1090var ratioArrays = {};
1091var levyArrays = {};
1092var jurLevyArrays = {};
1093var uniqueID = '';
1094
1095//vehicle tax ***********************************
1096//initialize listeners
1097function taxcalcVehicle(containerId) {
1098	// Attach event listener to the input field
1099	document.getElementById(containerId).querySelector('.vehicleValue').addEventListener('input', handleVehicleInputChange);
1100	// Attach event listener to the radio buttons
1101	var ratiosContainer = document.getElementById(containerId).querySelector('.VehicleRatios');
1102	ratiosContainer.addEventListener('change', handleVehicleRatioChange);
1103}
1104
1105function displayRatiosVehicle(arraykey) {
1106	var taxcontainer = document.querySelector(`#${arraykey}.vehicletax-calculator`);
1107	if (!taxcontainer) {
1108		console.error("Container element not found.");
1109		return;
1110	}
1111
1112	var currArray = vehicleRatioArrays[arraykey];
1113	if (!currArray) {
1114		console.error("Ratios array not found.");
1115		return;
1116	}
1117	var ratiosContainer = taxcontainer.querySelector('.VehicleRatios');
1118	if (!ratiosContainer) {
1119		console.error("Ratios container not found.");
1120		return;
1121	}
1122	var select = taxcontainer.querySelector('.ratioDropdown');
1123
1124	select.className = 'ratioSelect';
1125
1126	// Populate options from currArray
1127	currArray.forEach(function (item) {
1128		var option = document.createElement('option');
1129		option.value = item.guid;
1130		option.textContent = item.title + ": " + item.percent + "%";
1131		select.appendChild(option);
1132	});
1133}
1134
1135function getSelectedRatioVehicle(arraykey) {
1136	var taxcontainer = document.querySelector(`#${arraykey}.vehicletax-calculator`);
1137	try {
1138		// Get the dropdown element
1139		var dropdown = taxcontainer.querySelector('.ratioSelect');
1140		// Get the selected value
1141		var selectedRatio = dropdown.value;
1142		//if selectRatio has no value, return null
1143		if (selectedRatio === "null") {
1144			return null;
1145		}
1146		return selectedRatio;
1147	} catch (error) {
1148		// Handle any potential errors here
1149		console.error("Error in getSelectedRatioVehicle:", error);
1150		return null; // Return null if an error occurs
1151	}
1152}
1153
1154function handleVehicleRatioChange(event) {
1155	var arraykey = event.target.closest('.vehicletax-calculator').id;
1156	var selectedRatioguid = getSelectedRatioVehicle(arraykey);
1157	if (selectedRatioguid != null) {
1158		calculateAssessedVehicleValue(selectedRatioguid, arraykey);
1159	}
1160}
1161
1162function handleVehicleInputChange(event) {
1163	var vehilcevalueInput = event.target.value;
1164	var container = event.target.closest('.vehicletax-calculator');
1165	var arraykey = container.id;
1166	var selectedRatioguid = getSelectedRatioVehicle(arraykey);
1167	//if the selected ratio and assessed value are not null
1168	if (selectedRatioguid ) {
1169		calculateAssessedVehicleValue(selectedRatioguid, arraykey);
1170	}
1171}
1172
1173function displayassessedValVehicle(assessedVal, arraykey) {
1174	var taxcontainer = document.querySelector(`#${arraykey}.vehicletax-calculator`);
1175	var assessmentvalCont = taxcontainer.querySelector(".assessedValue");
1176
1177	if (assessmentvalCont) {
1178		assessmentvalCont.innerHTML = ''; // Clear existing content
1179		// Create a new div element for assessed value
1180		var assessValDiv = document.createElement('div');
1181		assessValDiv.className = 'calcdval';
1182		assessValDiv.innerHTML = '<span class="valSpan">' + assessedVal + '</span>';
1183		// Clear the container's contents
1184		assessmentvalCont.innerHTML = '';
1185		// Append the new assessed value div to the container
1186		assessmentvalCont.appendChild(assessValDiv);
1187	}
1188}
1189
1190function calculateAssessedVehicleValue(ratioguid, arraykey) {
1191	var taxcontainer = document.querySelector(`#${arraykey}.vehicletax-calculator`);
1192	var ratioArray = vehicleRatioArrays[arraykey];
1193	var selectedRatio = ratioArray.find(function (ratio) {
1194		return ratio.guid === ratioguid;
1195	});
1196	var vehicleValue = taxcontainer.querySelector('.vehicleValue').value;
1197	var assessedValue = vehicleValue * ((selectedRatio.percent) / 100);
1198	displayassessedValVehicle(assessedValue.toFixed(2), arraykey);
1199}
1200
1201window.addEventListener('load', vehicleinitializeTaxCalculators);
1202
1203function vehicleinitializeTaxCalculators() {
1204	var calculatorContainers = document.querySelectorAll('.vehicletax-calculator');
1205	calculatorContainers.forEach(function (container) {
1206		var containerId = container.id;
1207		arraykey = containerId;
1208		taxcalcVehicle(containerId);
1209		displayRatiosVehicle(arraykey);
1210	});
1211}
1212
1213var vehicleRatioArrays = {};
1214
1215function SetupCalendar(calendarId, sectionId) {
1216	let date=new Date();
1217	let year=date.getFullYear();
1218	let month = date.getMonth();
1219	let calId = '#' + calendarId;
1220	let cal = document.querySelector(calId);
1221	let prenexIcons=cal.querySelectorAll(".calendar-nav");
1222	let today = cal.querySelector(".today");
1223	let viewicons = $('.calendar-container.g' + sectionId + ' .prolistswap');
1224	let procallist = $('.calendar-container.g' + sectionId + ' .calendar-prolist');
1225	let procalendar = $('.calendar-container.g' + sectionId + ' .calendar-body');
1226
1227	// function to generate the calendar
1228	GetCategories(sectionId);
1229	UpdateCalendar(calId, year, month, sectionId);
1230	let callist = $('.procalendar.g' + sectionId + ' .calendar-list');
1231	if (callist.length > 0) {
1232		GetUpcomingEvents(sectionId);
1233	}
1234
1235	prenexIcons.forEach(icon=> {
1236		icon.addEventListener('click', ()=> {
1237			month=icon.classList.contains("calendar-prev") ? month - 1 : month + 1;
1238			if (month < 0 || month > 11) {
1239				date=new Date(year, month, new Date().getDate());
1240				year=date.getFullYear();
1241				month=date.getMonth();
1242			}
1243			else {
1244				date=new Date();
1245			}
1246			UpdateCalendar(calId, year, month, sectionId);
1247			$('.procalendar.g' + sectionId + ' .categories').val('all');
1248		});
1249	});
1250	today.addEventListener("click", ()=> {
1251		date=new Date();
1252		year=date.getFullYear();
1253		month=date.getMonth();
1254		UpdateCalendar(calId, year, month, sectionId);
1255		$('.procalendar.g' + sectionId + ' .categories').val('all');
1256	});
1257	if (viewicons.length > 0) {
1258		viewicons.click( function () {
1259			$(this).parent().find('.active').removeClass('active');
1260			$(this).addClass('active');
1261			if ($(this).hasClass('calview')) {
1262				procallist.hide();
1263				procalendar.show();
1264			}
1265			else {
1266				procalendar.hide();
1267				procallist.show();
1268			}
1269		});
1270	}
1271}
1272
1273function GetCategories(sectionId) {
1274	$.ajax({
1275		type: "GET",
1276		method: 'GET',
1277		url: "/admin/dm/Item/GetCategoriesForSectionId/?sectionId=" + sectionId + "&IsPublic=true",
1278		cache: false,
1279		async: false,
1280		contentType: "application/json; charset=utf-8",
1281		dataType: "json",
1282		data: '',
1283		processData: false,
1284		timeout: 300000,
1285		crossDomain: true,
1286		success: function (data) {
1287			$('.procalendar.g' + sectionId + ' .thecats').html(JSON.stringify(data));
1288
1289			$.each(data, function (i, p) {
1290				if (p.Title != 'None') {
1291					$('.procalendar.g' + sectionId + ' .categories').append($('<option>', {
1292						value: p.CategoryId,
1293						icon: p.FaIcon,
1294						text: p.Title
1295					}));
1296				}
1297			});
1298			$('.procalendar.g' + sectionId + ' .categories').change(function () {
1299				let cat = $(this).val();
1300				if (cat != 'all') {
1301					$('.procalendar.g' + sectionId + ' .calendar-dates li a').each(function () {
1302						if ($(this).attr('category') != cat) { $(this).hide(); }
1303						else { $(this).show(); }
1304					});
1305					$('.procalendar.g' + sectionId + ' .eventlistitem').each(function () {
1306						if ($(this).attr('category') != cat) { $(this).addClass('hide'); }
1307						else { $(this).removeClass('hide'); }
1308					});
1309					$('.g' + sectionId + ' .calendar-dates li button.more').hide();
1310					$('.g' + sectionId + ' .calendar-dates li div.hide.moreevents').show();
1311			}
1312				else {
1313					$('.procalendar.g' + sectionId + ' .calendar-dates li a').each(function () {
1314						$(this).show();
1315					});
1316					$('.procalendar.g' + sectionId + ' .eventlistitem').each(function () {
1317						$(this).removeClass('hide');
1318					});
1319					$('.g' + sectionId + ' .calendar-dates li button.more').show();
1320					$('.g' + sectionId + ' .calendar-dates li div.hide.moreevents').hide();
1321				}
1322			});
1323		},
1324		failure: function (data) { console.log('failure: ' + JSON.stringify(data)); },
1325		error: function (data) {
1326			console.log('error: ' + JSON.stringify(data));
1327		}
1328	});
1329}
1330
1331function GetUpcomingEvents(sectionId) {
1332	let displayNum = $('.calendar-container.g' + sectionId).attr('displaynum');
1333	if (displayNum == undefined) { displayNum = $('.procalendar.g' + sectionId).attr('displaynum'); }
1334	if (displayNum == undefined) { displayNum = 5; }
1335	if (displayNum == 0) { displayNum = 5; }
1336	// if (displayNum > 10) { displayNum = 10; }
1337
1338	$.ajax({
1339		type: "GET",
1340		method: 'GET',
1341		url: "/admin/dm/Item/GetUpcomingEvents/?sectionId=" + sectionId + "&numberofitems=" + displayNum,
1342		cache: false,
1343		async: false,
1344		contentType: "application/json; charset=utf-8",
1345		dataType: "json",
1346		data: '',
1347		processData: false,
1348		timeout: 300000,
1349		crossDomain: true,
1350		success: function (data) {
1351			let callist = $('.procalendar.g' + sectionId + ' .calendar-list');
1352			if ($(`.procalendar.g${sectionId}.HA_ProCurrent`).length == 0) {
1353				callist.append('<h2>Current Events</h2>');
1354			}
1355			callist.append('<div class="eventlistdialogs"></div>');
1356			let listdialogs = $('.procalendar.g' + sectionId + ' .eventlistdialogs');
1357
1358			let socialSharing = $('.calendar-container.g' + sectionId).attr('socialsharing');
1359			let enableFacebook = $('.calendar-container.g' + sectionId).attr('enablefacebook');
1360			let enableX = $('.calendar-container.g' + sectionId).attr('enablex');
1361			let enableCopy = $('.calendar-container.g' + sectionId).attr('enablecopy');
1362			let cats = JSON.parse($('.procalendar.g' + sectionId + ' .thecats').html());
1363
1364			$.each(data, function (i, p) {
1365				let title = ''; let summary = ''; let description = ''; let dstart = ''; let dend = '';
1366				let location = ''; let lname = ''; let directions = ''; let contactinfo = ''; let categoryid = ''; let latlng = '';
1367				let links = ''; let images = ''; let documents = ''; let audios = ''; let videos = '';
1368				let start = null; let end = null;
1369
1370				$.each(p.XmlData.Data.Property, function (j, q) {
1371					if (q["@name"] == 'title') title = q["@value"];
1372					if (q["@name"] == 'summary') summary = q["@value"];
1373					if (q["@name"] == 'description') description = q["@value"];
1374					if (q["@name"] == 'displayStartTime') dstart = q["@value"];
1375					if (q["@name"] == 'displayEndTime') dend = q["@value"];
1376					if (q["@name"] == 'name' && q["@value"].length > 0) lname = q["@value"] + '<br/>';
1377					if (q["@name"] == 'address1' && q["@value"].length > 0) location = q["@value"] + '<br/>';
1378					if (q["@name"] == 'address2' && q["@value"].length > 0) location += q["@value"] + '<br/>';
1379					if (q["@name"] == 'city' && q["@value"].length > 0) location += q["@value"] + ', ';
1380					if (q["@name"] == 'state' && q["@value"].length > 0) location += q["@value"] + '  ';
1381					if (q["@name"] == 'zip' && q["@value"].length > 0) location += q["@value"];
1382					if (q["@name"] == 'latlng' && q["@value"].length > 0) latlng += q["@value"];
1383					if (q["@name"] == 'directions' && q["@value"].length > 0) directions = q["@value"];
1384					if (q["@name"] == 'contactInfo' && q["@value"].length > 0) contactinfo = q["@value"];
1385					if (q["@name"] == 'category') categoryid = q["@value"];
1386					if (q["@name"] == 'start') start = new Date(q["@value"]);
1387					if (q["@name"] == 'finish') end = new Date(q["@value"]);
1388				});
1389				let startdate = start.getDate();
1390				let displaystartdate = start.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });
1391				let displayenddate = end.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });
1392				let boxmonth = start.toLocaleString('en-US', { month: 'short' });
1393				let boxday = startdate;
1394				let starttime = start.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
1395				let endtime = end.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
1396				let displaydatetimestart = start.toLocaleString('en-US', { month: 'long', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true });
1397				let displaydatetimeend = end.toLocaleString('en-US', { month: 'long', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true });
1398
1399				$.each(p.XmlData.Data, function (key, val) {
1400					if (key == 'RelatedLinks' && val != null) {
1401						$.each(val, function (j, q) {
1402							if (Array.isArray(q)) {
1403								$.each(q, function (k, r) {
1404									if (r["@intermediatepage"] == 'true')
1405										links += '<a href="/intermediate.asp?link=' + r["@url"] + '" ';
1406									else
1407										links += '<a href="' + r["@url"] + '" ';
1408									if (r["@targetNewWindow"] == 'true')
1409										links += 'target="_blank"';
1410									links += '><i class="fa-solid fa-link"></i>' + r["@title"] + '</a><br/>';
1411								});
1412							}
1413							else {
1414								if (q["@intermediatepage"] == 'true')
1415									links += '<a href="/intermediate.asp?link=' + q["@url"] + '" ';
1416								else
1417									links += '<a href="' + q["@url"] + '" ';
1418								if (q["@targetNewWindow"] == 'true')
1419									links += 'target="_blank"';
1420								links += '><i class="fa-solid fa-link"></i>' + q["@title"] + '</a><br/>';
1421							}
1422						});
1423					}
1424					if (key == 'Images' && val != null) {
1425						let count = 0;
1426						$.each(val, function (j, q) {
1427							count++;
1428							if (Array.isArray(q)) {
1429								if (q[0]["@cropFilename"] != null)
1430									images += '<div class="image left"><img src="/vertical/sites/{' + p.SiteId + '}/uploads/' + q[0]["@cropFilename"] + '" alt="' + q[0]["@alt"] + '" /></div>';
1431								else if (q[0]["@displayFilename"] != null)
1432									images += '<div class="image left"><img src="/vertical/sites/{' + p.SiteId + '}/uploads/' + q[0]["@displayFilename"] + '" alt="' + q[0]["@alt"] + '" /></div>';
1433							}
1434							else {
1435								if (q["@cropFilename"] != null)
1436									images += '<div class="image left"><img src="/vertical/sites/{' + p.SiteId + '}/uploads/' + q["@cropFilename"] + '" alt="' + q["@alt"] + '" /></div>';
1437								else if (q["@displayFilename"] != null)
1438									images += '<div class="image left"><img src="/vertical/sites/{' + p.SiteId + '}/uploads/' + q["@displayFilename"] + '" alt="' + q["@alt"] + '" /></div>';
1439							}
1440						});
1441					}
1442					if (key == 'Documents' && val != null) {
1443						$.each(val, function (j, q) {
1444							if (Array.isArray(q)) {
1445								$.each(q, function (k, r) {
1446									if (r["@dmDoc"] == 'True') {
1447										let displayName = r["@title"] == "" ? r["@filename"] : r["@title"];
1448										documents += '<a target="_blank" href="/uploads/dm/' + r["@dmFileId"] + "/" + encodeForPath(displayName) + '"><i class="fa-solid fa-file"></i>' + displayName + '</a><br/>';
1449									} else {
1450										documents += '<a href="/vertical/sites/{' + p.SiteId + '}/uploads/' + r["@filename"] + '"><i class="fa-solid fa-file"></i>' + r["@title"] + '</a><br/>';
1451									}
1452								});
1453							}
1454							else {
1455								if (q["@dmDoc"] == 'True') {
1456									let displayName = q["@title"] == "" ? q["@filename"] : q["@title"];
1457									documents += '<a target="_blank" href="/uploads/dm/' + q["@dmFileId"] + "/" + encodeForPath(displayName) + '"><i class="fa-solid fa-file"></i>' + displayName + '</a><br/>';
1458								} else {
1459									documents += '<a href="/vertical/sites/{' + p.SiteId + '}/uploads/' + q["@filename"] + '"><i class="fa-solid fa-file"></i>' + q["@title"] + '</a><br/>';
1460								}
1461							}
1462						});
1463					}
1464					if (key == 'Audios' && val != null) {
1465						$.each(val, function (j, q) {
1466							if (Array.isArray(q)) {
1467								$.each(q, function (k, r) {
1468									audios += '<a href="/vertical/sites/{' + p.SiteId + '}/uploads/' + r["@filename"] + '"><i class="fa-solid fa-volume"></i>' + r["@title"] + '</a><br/>';
1469								});
1470							}
1471							else {
1472								audios += '<a href="/vertical/sites/{' + p.SiteId + '}/uploads/' + q["@filename"] + '" ><i class="fa-solid fa-volume"></i>' + q["@title"] + '</a><br/>';
1473							}
1474						});
1475					}
1476					if (key == 'Videos' && val != null) {
1477						$.each(val, function (j, q) {
1478							if (Array.isArray(q)) {
1479								$.each(q, function (k, r) {
1480									videos += '<a href="/vertical/sites/{' + p.SiteId + '}/uploads/' + r["@filename"] + '" ><i class="fa-solid fa-video"></i>' + r["@title"] + '</a><br/>';
1481								});
1482							}
1483							else {
1484								videos += '<a href="/vertical/sites/{' + p.SiteId + '}/uploads/' + q["@filename"] + '" ><i class="fa-solid fa-video"></i>' + q["@title"] + '</a><br/>';
1485							}
1486						});
1487					}
1488				});
1489
1490				let catclass = ''; let caticon = ''; let catname = '';
1491				$.each(cats, function (cati, catp) {
1492					if (catp.CategoryId == categoryid) {
1493						catclass = catp.CssClass;
1494						caticon = catp.FaIcon;
1495						catname = catp.Title;
1496					}
1497				});
1498
1499				if ($(`.procalendar.g${sectionId}.HA_ProCurrent`).length > 0) {
1500					callist.append(`<div class="eventlistitem" did="ld${p.DEId}" category="${categoryid}"></div>`)
1501					let callistitem = $(`.procalendar.g${sectionId} .eventlistitem[did="ld${p.DEId}"]`);
1502					if ($(`.procalendar.g${sectionId}.HA_ProCurrent`).attr('faicon') != undefined) {
1503						if ($(`.procalendar.g${sectionId}.HA_ProCurrent`).attr('faicon').length > 0) {
1504							let faicon = $(`.procalendar.g${sectionId}.HA_ProCurrent`).attr('faicon');
1505							callistitem.append(`<div class="boxicon"><i class="fa-solid ${faicon}"></i></div>`);
1506						} else {
1507							callistitem.append(`<div class="boxicon ${catclass}"><i class="fa-solid ${caticon}"></i></div>`);
1508						}
1509					} else {
1510						callistitem.append(`<div class="boxicon ${catclass}"><i class="fa-solid ${caticon}"></i></div>`);
1511					}
1512					callistitem.append('<div class="rightside"></div>');
1513					let callistitemright = $(`.procalendar.g${sectionId} .eventlistitem[did="ld${p.DEId}"] .rightside`);
1514					callistitemright.append(`<h3><a href="javascript:;" class="event" did="ld${p.DEId}">${title}</a></h3>`);
1515					if (dstart != 'none' && dstart != 'time') {
1516						callistitemright.append('<div class="eventdate" did="ld' + p.DEId + '">' + displaystartdate + '</div> ');
1517					}
1518					if (dstart != 'none' && dstart != 'date') {
1519						callistitemright.append('<div class="eventtime" did="ld' + p.DEId + '">' + starttime + '</div>');
1520					}
1521				}
1522				else {
1523					callist.append('<div class="eventlistitem" did="ld' + p.DEId + '" category="' + categoryid + '"></div>')
1524					let callistitem = $('.procalendar.g' + sectionId + ' .eventlistitem[did="ld' + p.DEId + '"]');
1525					callistitem.append('<div class="boxdate"><div class="month">' + boxmonth + '</div><div class="day">' + boxday + '</div></div>');
1526					callistitem.append('<div class="rightside"></div>');
1527					let callistitemright = $('.procalendar.g' + sectionId + ' .eventlistitem[did="ld' + p.DEId + '"] .rightside');
1528					callistitemright.append('<h3><a href="javascript:;" class="event" did="ld' + p.DEId + '">' + title + '</a></h3>');
1529					if (dstart != 'none' && dstart != 'date') {
1530						// callistitemright.append('<div class="eventdate" did="ld' + p.DEId + '">' + displaystartdate + '</div> ');
1531						callistitemright.append('<div class="eventtime" did="ld' + p.DEId + '">' + starttime + '</div>');
1532					}
1533				}
1534
1535				// build dialogs
1536				listdialogs.append('<dialog id="ld' + p.DEId + '" class="event dialog"><button type="button" class="closedialog" aria-label="close dialog">Close <i class="fa-regular fa-x"></i></button></dialog>');
1537				let $ld = listdialogs.find('dialog#ld' + p.DEId);
1538				$ld.append('<div class="beforemap"></div>');
1539				let bmap = $ld.find('.beforemap');
1540
1541				bmap.append('<h2>' + title + '</h2>');
1542
1543				if (dstart != 'none' || dend != 'none') {
1544					bmap.append('<div class="eventtime"><span class="label"><i class="fa-solid fa-clock"></i> When: </span></div>');
1545					let dt = $ld.find('.eventtime');
1546					if (dstart == 'date') { dt.append(displaystartdate); }
1547					if (dstart == 'time') { dt.append(starttime); }
1548					if (dstart == 'full') { dt.append(displaydatetimestart); }
1549					if (dstart != 'none' && dend != 'none') { dt.append(' - '); }	// add a dash between start and end
1550					if (dend == 'date') { dt.append(displayenddate); }
1551					if (dend == 'time') { dt.append(endtime); }
1552					if (dend == 'full') { dt.append(displaydatetimeend); }
1553				}
1554
1555				if (images.length > 0 || description.length > 0 || (caticon != undefined && caticon.length > 0) || links.length > 0 || documents.length > 0 || audios.length > 0 || videos.length > 0) {
1556					bmap.append('<hr/>');
1557				}
1558
1559				if (caticon != undefined && caticon.length > 0) {
1560					bmap.append(`<div class="category ${catclass}"><span class="color"> </span><span class="label"><i class="fa-solid ${caticon}"></i> </span>${catname}</div>`);
1561				}
1562
1563				if (images.length > 0) {
1564					bmap.append('<div class="description">' + images + description + '</div>');
1565				}
1566				else {
1567					bmap.append('<div class="description">' + description + '</div>');
1568				}
1569				let desc = bmap.find('.description');
1570
1571				if (links.length > 0 || documents.length > 0 || audios.length > 0 || videos.length > 0) {
1572					//desc.append('<div class="attachments"><span class="label"><i class="fa-solid fa-paperclip"></i> Attachments:</span><br/>' + links + documents + audios + videos + '</div>');
1573					desc.append('<div class="attachments">' + links + documents + audios + videos + '</div>');
1574				}
1575
1576				if (contactinfo.length > 0 || location.length > 0) {
1577					bmap.append('<hr/>');
1578				}
1579
1580				bmap.append('<div class="sidebyside"></div>');
1581				let sbs = $ld.find('.sidebyside');
1582				if (contactinfo.length > 0) {
1583					sbs.append('<div class="contactinfo"><span class="label"><i class="fa-solid fa-user"></i> Contact:</span><br/>' + contactinfo + '</div>');
1584				}
1585				if (lname.length > 0 || location.length > 0) {
1586					sbs.append('<div class="location"><span class="label"><i class="fa-solid fa-location-dot"></i> Where: </span><br/>' + lname + location + '</div>');
1587					let loc = sbs.find('.location');
1588					if (latlng.length > 0)
1589						loc.append('<br/><a href="https://www.google.com/maps/dir/?api=1&destination=' + latlng + '" target="_blank" class="maplink">Get Directions</a>');
1590				}
1591
1592				if (latlng.length > 0) {
1593					$ld.append('<div class="mapcontainer"></div>');
1594					let $thismap = $('.procalendar.g' + sectionId + ' dialog#ld' + p.DEId + ' .mapcontainer');
1595					$thismap.append('<div id="lloc' + p.DEId + '" class="hidden coordinates">' + latlng + '</div>');
1596					$thismap.append('<div id="lmap' + p.DEId + '" class="map"></div>');
1597				}
1598
1599				$ld.append('<div class="aftermap"></div>');
1600				let amap = $ld.find('.aftermap');
1601
1602				if (directions.length > 0) {
1603					amap.append('<div class="directions"><span class="label"><i class="fa-solid fa-diamond-turn-right"></i> Directions:</span><br/>' + directions + '</div>');
1604				}
1605
1606				//zach
1607				if (socialSharing != null && socialSharing == "True") {
1608					var siteUrl = window.location.protocol + "//" + window.location.host;
1609					var apiUrl = siteUrl + "/govofficeservices/api/content/ics.ashx?event=" + p.DEId + "&section=" + sectionId;
1610					var eventUrl = (siteUrl + "/index.asp?SEC=" + sectionId + "&DE=" + p.DEId);
1611					//ICS button + sharing
1612
1613					amap.append(`<div class="eventics"><a href="${apiUrl}"><i class="fa-solid fa-calendar-plus"></i><span class="label">Save to your calendar</span></a></div>`);
1614
1615					if (enableFacebook != null && enableFacebook == "True") {
1616						amap.append(`<a class="modalBTNSocial facebook" href="https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(eventUrl)}" title="Share on Facebook" target="_blank"><i class="fab fa-facebook-f"></i>SHARE</a>`);
1617						amap.append(`&nbsp;`);
1618					}
1619					if (enableX != null && enableX == "True") {
1620						amap.append(`<a class="modalBTNSocial twitter" href="https://twitter.com/intent/tweet?url=${encodeURIComponent(eventUrl)}" title = "Post on X (formerly Twitter)" target = "_blank" > <i class="fab fa-x-twitter"></i>POST</a >`);
1621						amap.append(`&nbsp;`);
1622					}
1623					if (enableCopy != null && enableCopy == "True")
1624						amap.append(`<a class="modalBTNSocial copy" onclick="copyToClipboard('${eventUrl}', this, 'COPIED!')" title="Copy to your clipboard"><i class="fal fa-copy"></i>COPY</a>`);
1625				}
1626
1627			});
1628
1629			if (callist.find('.eventlistitem').length == 0) { callist.append('<h3>No Current Events</h3>'); }
1630
1631			// event handlers
1632			$('.g' + sectionId + ' .eventlistitem').on("click", "a.event", function () {
1633				let ddd = document.getElementById($(this).attr('did'));
1634				ddd.showModal();
1635				ddd.addEventListener("click", e => {
1636					const dialogDimensions = ddd.getBoundingClientRect()
1637					if (
1638						e.clientX < dialogDimensions.left || e.clientX > dialogDimensions.right ||
1639						e.clientY < dialogDimensions.top || e.clientY > dialogDimensions.bottom
1640					) {
1641						ddd.close()
1642					}
1643				});
1644				// display map here if it exists
1645				if (document.getElementById('lloc' + $(this).attr('did').substring(2)) != null) {
1646					let loc = document.getElementById('lloc' + $(this).attr('did').substring(2)).innerHTML;
1647					let mapid = 'lmap' + $(this).attr('did').substring(2);	// get the map id
1648					//alert(mapid + ' - ' + loc);
1649					if (loc.length > 0 && mapid != '<!---->') {
1650						document.getElementById(mapid).classList.add("shown");
1651						document.querySelector('.mapcoords').innerHTML = loc;
1652						document.querySelector('.mapid').innerHTML = mapid;
1653						initLocationMap();
1654					}
1655				}
1656			});
1657
1658			$('.eventlistdialogs').on("click", "button.closedialog", function () {
1659				dialogs = document.querySelectorAll('dialog');
1660				dialogs.forEach (function(d) {
1661					d.close();
1662				});
1663			});
1664
1665		},
1666		failure: function (data) { console.log('failure: ' + JSON.stringify(data)); },
1667		error: function (data) {
1668			console.log('error: ' + JSON.stringify(data));
1669		}
1670	});
1671}
1672
1673function UpdateCalendar(calId, year, month, sectionId) {
1674	let date = new Date();
1675	let cal = document.querySelector(calId);
1676	let grid = cal.querySelector('.calendar-dates');
1677	let currdate = cal.querySelector('.calendar-current-date');
1678	let months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
1679
1680	// get the first day of the month
1681	let dayone=new Date(year, month, 1).getDay();
1682	// get the last date of the month
1683	let lastdate=new Date(year, month + 1, 0).getDate();
1684	// get the day of the last date of the month
1685	let dayend=new Date(year, month, lastdate).getDay();
1686	// get the last date of the previous month
1687	let monthlastdate=new Date(year, month, 0).getDate();
1688	let newcal=""; // variable to store the generated calendar HTML
1689	// loop to add the last dates of the previous month
1690	for (let i=dayone; i > 0; i--) { newcal+=`<li class="inactive">${monthlastdate - i + 1}</li>`; }
1691	// loop to add the dates of the current month
1692	for (let i=1; i <=lastdate; i++) {
1693		// check if the current date is today
1694		let isToday = i === date.getDate() && month === new Date().getMonth() && year === new Date().getFullYear() ? " active" : "";
1695		let displaydate = new Date(year, month, i).toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });
1696		newcal+=`<li class="d${i}${isToday}"><div class="day">${i}</div><div class="daylist"><h3>${displaydate}</h3><button type="button" class="close">Close</button></div></li>`;
1697	}
1698	// loop to add the first dates of the next month
1699	for (let i = dayend; i < 6; i++) { newcal += `<li class="inactive">${i - dayend + 1}</li>`; }
1700	// update the text of the current date element with the formatted current month and year
1701	currdate.innerText=`${months[month]} ${year}`;
1702	// update the HTML of the dates element with the generated calendar
1703	grid.innerHTML = newcal;
1704
1705	$('.procalendar.g' + sectionId + ' .dialogs dialog').remove();
1706	GetMonthItems(sectionId, year, month + 1);
1707}
1708
1709/**
1710 * copyToClipboard - Copies text to clipboard and swaps the text on the element with the provided text.
1711 * @param {any} url - what to copy to clipboard, usually a URL
1712 * @param {any} element - the button/element that was clicked
1713 * @param {any} text - the text to swap with the element's innerHTML
1714 */
1715window.copyToClipboard = function (url, element, text) {
1716	navigator.clipboard.writeText(url)
1717		.then(() => {
1718			const originalContent = element.innerHTML;
1719			const originalColor = element.style.backgroundColor;
1720			element.textContent = text;
1721			element.style.backgroundColor = '#338445';
1722
1723			// Revert back to original content after a delay
1724			setTimeout(() => {
1725				element.innerHTML = originalContent;
1726				element.style.backgroundColor = originalColor;
1727			}, 3500);
1728
1729		}, function (err) {
1730			alert('Failed to copy text: ', err);
1731		});
1732};
1733
1734function GetMonthItems(sectionId, year, month) {
1735	let promo = '';
1736	let APIurl = '';
1737	if ($(`.procalendar.g${sectionId}.promo`).length > 0) {
1738		APIurl = "/admin/dm/Item/GetMonth/?sectionId=" + sectionId + "&Year=" + year + "&Month=" + month + "&IsPublic=true&IsPromo=true";
1739	} else {
1740		APIurl = "/admin/dm/Item/GetMonth/?sectionId=" + sectionId + "&Year=" + year + "&Month=" + month + "&IsPublic=true";
1741	}
1742	$.ajax({
1743		type: "GET",
1744		method: 'GET',
1745		url: APIurl,
1746		cache: false,
1747		async: false,
1748		contentType: "application/json; charset=utf-8",
1749		dataType: "json",
1750		data: '',
1751		processData: false,
1752		timeout: 300000,
1753		crossDomain: true,
1754		success: function (data) {
1755			// let callist = $('.procalendar.g' + sectionId + ' .calendar-list');
1756			let procallist = $('.procalendar.g' + sectionId + ' .calendar-prolist');
1757			let currentMonth = new Date().getMonth() + 1;
1758			let displayNum = $('.calendar-container.g' + sectionId).attr('displaynum');
1759			let showPastEvents = $('.calendar-container.g' + sectionId).attr('showpastevents');
1760			let socialSharing = $('.calendar-container.g' + sectionId).attr('socialsharing');
1761			let enableFacebook = $('.calendar-container.g' + sectionId).attr('enablefacebook');
1762			let enableX = $('.calendar-container.g' + sectionId).attr('enablex');
1763			let enableCopy = $('.calendar-container.g' + sectionId).attr('enablecopy');
1764			// callist.html('');
1765			procallist.html('');
1766			let cats = JSON.parse($('.procalendar.g' + sectionId + ' .thecats').html());
1767
1768			// put values into variables
1769			$.each(data, function (i, p) {
1770				let gd = new Date(p.GD);
1771				let adjustedEnd = new Date(p.EndDate);
1772				let title = ''; let summary = ''; let description = ''; let dstart = ''; let dend = '';
1773				let location = ''; let lname = ''; let directions = '';	let contactinfo = '';	let categoryid = ''; let latlng = '';
1774				let links = ''; let images = ''; let documents = ''; let audios = ''; let videos = '';
1775				let start = null; let end = null;
1776
1777				$.each(p.XmlData.Data.Property, function (j, q) {
1778					if (q["@name"] == 'title') title = q["@value"];
1779					if (q["@name"] == 'summary') summary = q["@value"];
1780					if (q["@name"] == 'description') description = q["@value"];
1781					if (q["@name"] == 'displayStartTime')	dstart = q["@value"];
1782					if (q["@name"] == 'displayEndTime')	dend = q["@value"];
1783					if (q["@name"] == 'name' && q["@value"].length > 0)	lname = q["@value"] + '<br/>';
1784					if (q["@name"] == 'address1' && q["@value"].length > 0)	location = q["@value"] + '<br/>';
1785					if (q["@name"] == 'address2' && q["@value"].length > 0)	location += q["@value"] + '<br/>';
1786					if (q["@name"] == 'city' && q["@value"].length > 0) location += q["@value"] + ', ';
1787					if (q["@name"] == 'state' && q["@value"].length > 0) location += q["@value"] + '  ';
1788					if (q["@name"] == 'zip' && q["@value"].length > 0) location += q["@value"];
1789					if (q["@name"] == 'latlng' && q["@value"].length > 0)	latlng += q["@value"];
1790					if (q["@name"] == 'directions' && q["@value"].length > 0)	directions = q["@value"];
1791					if (q["@name"] == 'contactInfo' && q["@value"].length > 0) contactinfo = q["@value"];
1792					if (q["@name"] == 'category')	categoryid = q["@value"];
1793					if (q["@name"] == 'start') start = new Date(q["@value"]);
1794					if (q["@name"] == 'finish') end = new Date(q["@value"]);
1795				});
1796				let startdate = start.getDate();
1797				let displaystartdate = start.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });
1798				let displayenddate = end.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });
1799				let boxmonth = start.toLocaleString('en-US', { month: 'short' });
1800				let boxday = startdate;
1801				let starttime = start.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
1802				let endtime = end.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
1803				let displaydatetimestart = start.toLocaleString('en-US', { month: 'long', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true });
1804				let displaydatetimeend = end.toLocaleString('en-US', { month: 'long', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true });
1805
1806				$.each(p.XmlData.Data, function (key, val) {
1807					if (key == 'RelatedLinks' && val != null) {
1808						$.each(val, function (j, q) {
1809							if (Array.isArray(q)) {
1810								$.each(q, function (k, r) {
1811									if (r["@intermediatepage"] == 'true')
1812										links += '<a href="/intermediate.asp?link=' + r["@url"] + '" ';
1813									else
1814										links += '<a href="' + r["@url"] + '" ';
1815									if (r["@targetNewWindow"] == 'true')
1816										links += 'target="_blank"';
1817									links += '><i class="fa-solid fa-link"></i>' + r["@title"] + '</a><br/>';
1818								});
1819							}
1820							else {
1821								if (q["@intermediatepage"] == 'true')
1822									links += '<a href="/intermediate.asp?link=' + q["@url"] + '" ';
1823								else
1824									links += '<a href="' + q["@url"] + '" ';
1825								if (q["@targetNewWindow"] == 'true')
1826									links += 'target="_blank"';
1827								links += '><i class="fa-solid fa-link"></i>' + q["@title"] + '</a><br/>';
1828							}
1829						});
1830					}
1831					if (key == 'Images' && val != null) {
1832						let count = 0;
1833						$.each(val, function (j, q) {
1834							count++;
1835							if (Array.isArray(q)) {
1836								if (q[0]["@cropFilename"] != null)
1837									images += '<div class="image left"><img src="/vertical/sites/{' + p.SiteId + '}/uploads/' + q[0]["@cropFilename"] + '" alt="' + q[0]["@alt"] + '" /></div>';
1838								else if (q[0]["@displayFilename"] != null)
1839									images += '<div class="image left"><img src="/vertical/sites/{' + p.SiteId + '}/uploads/' + q[0]["@displayFilename"] + '" alt="' + q[0]["@alt"] + '" /></div>';
1840							}
1841							else {
1842								if (q["@cropFilename"] != null)
1843									images += '<div class="image left"><img src="/vertical/sites/{' + p.SiteId + '}/uploads/' + q["@cropFilename"] + '" alt="' + q["@alt"] + '" /></div>';
1844								else if (q["@displayFilename"] != null)
1845									images += '<div class="image left"><img src="/vertical/sites/{' + p.SiteId + '}/uploads/' + q["@displayFilename"] + '" alt="' + q["@alt"] + '" /></div>';
1846							}
1847						});
1848					}
1849					if (key == 'Documents' && val != null) {
1850						$.each(val, function (j, q) {
1851							if (Array.isArray(q)) {
1852								$.each(q, function (k, r) {
1853									if (r["@dmDoc"] == 'True') {
1854										let displayName = r["@title"] == "" ? r["@filename"] : r["@title"];
1855										documents += '<a target="_blank" href="/uploads/dm/' + r["@dmFileId"] + "/" + encodeForPath(displayName) + '"><i class="fa-solid fa-file"></i>' + displayName + '</a><br/>';
1856									} else {
1857										documents += '<a href="/vertical/sites/{' + p.SiteId + '}/uploads/' + r["@filename"] + '"><i class="fa-solid fa-file"></i>' + r["@title"] + '</a><br/>';
1858									}
1859								});
1860							}
1861							else {
1862								if (q["@dmDoc"] == 'True') {
1863									let displayName = q["@title"] == "" ? q["@filename"] : q["@title"];
1864									documents += '<a target="_blank" href="/uploads/dm/' + q["@dmFileId"] + "/" + encodeForPath(displayName) + '"><i class="fa-solid fa-file"></i>' + displayName + '</a><br/>';
1865								} else {
1866									documents += '<a href="/vertical/sites/{' + p.SiteId + '}/uploads/' + q["@filename"] + '"><i class="fa-solid fa-file"></i>' + q["@title"] + '</a><br/>';
1867								}
1868							}
1869						});
1870					}
1871					if (key == 'Audios' && val != null) {
1872						$.each(val, function (j, q) {
1873							if (Array.isArray(q)) {
1874								$.each(q, function (k, r) {
1875									audios += '<a href="/vertical/sites/{' + p.SiteId + '}/uploads/' + r["@filename"] + '"><i class="fa-solid fa-volume"></i>' + r["@title"] + '</a><br/>';
1876								});
1877							}
1878							else {
1879								audios += '<a href="/vertical/sites/{' + p.SiteId + '}/uploads/' + q["@filename"] + '" ><i class="fa-solid fa-volume"></i>' + q["@title"] + '</a><br/>';
1880							}
1881						});
1882					}
1883					if (key == 'Videos' && val != null) {
1884						$.each(val, function (j, q) {
1885							if (Array.isArray(q)) {
1886								$.each(q, function (k, r) {
1887									videos += '<a href="/vertical/sites/{' + p.SiteId + '}/uploads/' + r["@filename"] + '" ><i class="fa-solid fa-video"></i>' + r["@title"] + '</a><br/>';
1888								});
1889							}
1890							else {
1891								videos += '<a href="/vertical/sites/{' + p.SiteId + '}/uploads/' + q["@filename"] + '" ><i class="fa-solid fa-video"></i>' + q["@title"] + '</a><br/>';
1892							}
1893						});
1894					}
1895				});
1896
1897				let catclass = ''; let caticon = ''; let catname = '';
1898				$.each(cats, function (cati, catp) {
1899					if (catp.CategoryId == categoryid) {
1900						catclass = catp.CssClass;
1901						caticon = catp.FaIcon;
1902						catname = catp.Title;
1903					}
1904				});
1905
1906				if (showPastEvents == 'True' || adjustedEnd > gd) {
1907					// add to calendar
1908					for (var d = start; d <= end; d.setDate(d.getDate() + 1)) {
1909						if (d.getMonth() + 1 == month) {
1910							let spandate = d.getDate();
1911
1912							// add to daylist first
1913							$(`.g${sectionId} .calendar-dates li.d${spandate} .daylist button.close`).before(`<a href="javascript:;" class="event ${catclass}" id="l_${p.DEId}" did="d${p.DEId}" title="${title}" category="${categoryid}" aria-label="${displaydatetimestart} ${title}"><div><span class="ctitle">${title}</span><div class="datetime"></div></div></a>`);
1914							if (dstart == 'time' || dstart == 'full')
1915								$(`.g${sectionId} .calendar-dates li.d${spandate} .daylist a#l_${p.DEId} div.datetime`).append(`${starttime}`);
1916							if ((dstart == 'time' || dstart == 'full') && (dend == 'time' || dend == 'full'))
1917								$(`.g${sectionId} .calendar-dates li.d${spandate} .daylist a#l_${p.DEId} div.datetime`).append(` - `);
1918							if (dend == 'time' || dend == 'full')
1919								$(`.g${sectionId} .calendar-dates li.d${spandate} .daylist a#l_${p.DEId} div.datetime`).append(`${endtime}`);
1920
1921							// add to calendar date
1922							if ($(`.g${sectionId} .calendar-dates li.d${spandate} > a`).length < 2) {
1923								if (dstart == 'none' || dstart == 'date')
1924									$(`.g${sectionId} .calendar-dates li.d${spandate}`).append(`<a href="javascript:;" class="event ${catclass}" did="d${p.DEId}" title="${title}" category="${categoryid}" aria-label="${displaydatetimestart} ${title}"><div><span class="ctitle">${title}</span></div></a>`);
1925								else
1926									$(`.g${sectionId} .calendar-dates li.d${spandate}`).append(`<a href="javascript:;" class="event ${catclass}" did="d${p.DEId}" title="${title} ${starttime}" category="${categoryid}" aria-label="${displaydatetimestart} ${title}"><div><span class="ctitle">${title}</span><span class="cstarttime">${starttime}</span></div></a>`);
1927							}
1928							else {
1929								// add show more button
1930								if ($(`.g${sectionId} .calendar-dates li.d${spandate} button.more`).length == 0)
1931									$(`.g${sectionId} .calendar-dates li.d${spandate}`).append(`<button class="more">Show More</button>`);
1932								if ($(`.g${sectionId} .calendar-dates li.d${spandate} div.hide.moreevents`).length == 0)
1933									$(`.g${sectionId} .calendar-dates li.d${spandate}`).append(`<div class="hide moreevents"></div>`);
1934								if (dstart == 'none' || dstart == 'date')
1935									$(`.g${sectionId} .calendar-dates li.d${spandate} div.hide.moreevents`).append(`<a href="javascript:;" class="event ${catclass}" did="d${p.DEId}" title="${title}" category="${categoryid}"><div><span class="ctitle">${title}</span></div></a>`);
1936								else
1937									$(`.g${sectionId} .calendar-dates li.d${spandate} div.hide.moreevents`).append(`<a href="javascript:;" class="event ${catclass}" did="d${p.DEId}" title="${title} ${starttime}" category="${categoryid}"><div><span class="ctitle">${title}</span><span class="cstarttime">${starttime}</span></div></a>`);
1938							}
1939							$(`.g${sectionId} .calendar-dates li.d${spandate} button.more`).click(function () {
1940								$(`.g${sectionId} .calendar-dates li.d${spandate} div.daylist`).addClass('show');
1941								let topOfOverlay = $(`.g${sectionId} .calendar-dates li.d${spandate} div.daylist > h3`);
1942								$('html, body').animate({ scrollTop: ($(topOfOverlay).offset().top - 60) }, 500);
1943								//$('.g' + sectionId + ' .calendar-dates li.d' + spandate + ' button.more').hide();
1944								//$('.g' + sectionId + ' .calendar-dates li.d' + spandate + ' div.hide.moreevents').show();
1945							});
1946							$(`.g${sectionId} .calendar-dates li button.close`).click(function () {
1947								$(`.g${sectionId} .calendar-dates li div.daylist`).removeClass('show');
1948							});
1949						}
1950					}
1951
1952					// build dialogs
1953					$(`.procalendar.g${sectionId} .dialogs`).append(`<dialog id="d${p.DEId}" class="event dialog"><button type="button" class="closedialog" aria-label="close dialog">Close <i class="fa-regular fa-x"></i></button></dialog>`);
1954					let $thisdialog = $(`.procalendar.g${sectionId} dialog#d${p.DEId}`);
1955
1956					$thisdialog.append('<div class="beforemap"></div>');
1957					let bmap = $thisdialog.find('.beforemap');
1958
1959					bmap.append('<h2>' + title + '</h2>');
1960
1961					if (dstart != 'none' || dend != 'none') {
1962						bmap.append('<div class="eventtime"><span class="label"><i class="fa-solid fa-clock"></i> When: </span></div>');
1963						let dt = bmap.find('.eventtime');
1964						if (dstart == 'date') { dt.append(displaystartdate); }
1965						if (dstart == 'time') { dt.append(starttime); }
1966						if (dstart == 'full') { dt.append(displaydatetimestart); }
1967						if (dstart != 'none' && dend != 'none') { dt.append(' - '); }	// add a dash between start and end
1968						if (dend == 'date') { dt.append(displayenddate); }
1969						if (dend == 'time') { dt.append(endtime); }
1970						if (dend == 'full') { dt.append(displaydatetimeend); }
1971					}
1972
1973					if (images.length > 0 || description.length > 0 || (caticon != undefined && caticon.length > 0) || links.length > 0 || documents.length > 0 || audios.length > 0 || videos.length > 0) {
1974						bmap.append('<hr/>');
1975					}
1976
1977					if (caticon != undefined && caticon.length > 0) {
1978						bmap.append(`<div class="category ${catclass}"><span class="color"> </span><span class="label"><i class="fa-solid ${caticon}"></i> </span>${catname}</div>`);
1979					}
1980
1981					if (images.length > 0) {
1982						bmap.append('<div class="description">' + images + description + '</div>');
1983					}
1984					else {
1985						bmap.append('<div class="description">' + description + '</div>');
1986					}
1987					let desc = bmap.find('.description');
1988
1989					if (links.length > 0 || documents.length > 0 || audios.length > 0 || videos.length > 0) {
1990						//desc.append('<div class="attachments"><span class="label"><i class="fa-solid fa-paperclip"></i> Attachments:</span><br/>' + links + documents + audios + videos + '</div>');
1991						desc.append('<div class="attachments">' + links + documents + audios + videos + '</div>');
1992					}
1993
1994					if (contactinfo.length > 0 || lname.length > 0 || location.length > 0) {
1995						bmap.append('<hr/>');
1996					}
1997
1998					bmap.append('<div class="sidebyside"></div>');
1999					let sbs = bmap.find('.sidebyside');
2000					if (contactinfo.length > 0) {
2001						sbs.append('<div class="contactinfo"><span class="label"><i class="fa-solid fa-user"></i> Contact:</span><br/>' + contactinfo + '</div>');
2002					}
2003					if (lname.length > 0 || location.length > 0) {
2004						sbs.append('<div class="location"><span class="label"><i class="fa-solid fa-location-dot"></i> Where: </span><br/>' + lname + location + '</div>');
2005						let loc = sbs.find('.location');
2006						if (latlng.length > 0)
2007							loc.append('<br/><a href="https://www.google.com/maps/dir/?api=1&destination=' + latlng + '" target="_blank" class="maplink">Get Directions</a>');
2008					}
2009
2010					if (latlng.length > 0) { //location.length > 0 &&
2011						$thisdialog.append('<div class="mapcontainer"></div>');
2012						let $thismap = $('.procalendar.g' + sectionId + ' dialog#d' + p.DEId + ' .mapcontainer');
2013						$('.mapcontainer').append('<div id="loc' + p.DEId + '" class="hidden coordinates">' + latlng + '</div>');
2014						$thismap.append('<div id="map' + p.DEId + '" class="map"></div>');
2015					}
2016
2017					$thisdialog.append('<div class="aftermap"></div>');
2018					let amap = $thisdialog.find('.aftermap');
2019
2020					if (directions.length > 0) {
2021						amap.append('<div class="directions"><span class="label"><i class="fa-solid fa-diamond-turn-right"></i> Directions:</span><br/>' + directions + '</div>');
2022					}
2023
2024					//zach
2025					if (socialSharing != null && socialSharing == "True") {
2026					var siteUrl = window.location.protocol + "//" + window.location.host;
2027					var apiUrl = siteUrl + "/govofficeservices/api/content/ics.ashx?event=" + p.DEId + "&section=" + sectionId;
2028					var eventUrl = (siteUrl + "/index.asp?SEC=" + sectionId + "&DE=" + p.DEId);
2029					//ICS button + sharing
2030					amap.append(`<div class="eventics"><a href="${apiUrl}"><i class="fa-solid fa-calendar-plus"></i><span class="label">Save to your calendar</span></a></div>`);
2031
2032						if (enableFacebook != null && enableFacebook == "True") {
2033							amap.append(`<a class="modalBTNSocial facebook" href="https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(eventUrl)}" title="Share on Facebook" target="_blank"><i class="fab fa-facebook-f"></i>SHARE</a>`);
2034							amap.append(`&nbsp;`);
2035						}
2036						if (enableX != null && enableX == "True") {
2037							amap.append(`<a class="modalBTNSocial twitter" href="https://twitter.com/intent/tweet?url=${encodeURIComponent(eventUrl)}" title = "Post on X (formerly Twitter)" target = "_blank" > <i class="fab fa-x-twitter"></i>POST</a >`);
2038							amap.append(`&nbsp;`);
2039						}
2040						if (enableCopy != null && enableCopy == "True")
2041							amap.append(`<a class="modalBTNSocial copy" onclick="copyToClipboard('${eventUrl}', this, 'COPIED!')" title="Copy to your clipboard"><i class="fal fa-copy"></i>COPY</a>`);
2042					}
2043				}
2044
2045				// pro calendar list (the big list of events with everything)
2046				if (procallist.length > 0) {
2047					if ((showPastEvents == 'True' || adjustedEnd > gd) && procallist.find('.eventlistitem').length < displayNum) {
2048						if (!$('.calendar-container.g' + sectionId + ' .calview').hasClass('active'))
2049							$('.calendar-container.g' + sectionId + ' .calendar-body').hide();
2050						procallist.append('<div class="eventlistitem" did="d' + p.DEId + '" category="' + categoryid + '"></div>')
2051						let procallistitem = $(`.procalendar.g${sectionId} .eventlistitem[did="d${p.DEId}"]`);
2052						procallistitem.append(`<div class="boxdate"><div class="month">${boxmonth}</div><div class="day">${boxday}</div></div>`);
2053						procallistitem.append('<div class="rightside"></div>');
2054						let procallistitemright = $(`.procalendar.g${sectionId} .eventlistitem[did="d${p.DEId}"] .rightside`);
2055						procallistitemright.append(`<h3><a href="/?SEC=${sectionId}&DE=${p.DEId}" class="event" did="d${p.DEId}">${title}</a></h3>`);
2056
2057						// date
2058						if (dstart != 'none' || dend != 'none') {
2059							procallistitemright.append('<div class="eventtime"><i class="fa-solid fa-clock"></i><span class="label">
2059When </span></div>');
2060							let dt = procallistitemright.find('.eventtime');
2061							if (dstart == 'date') { dt.append(displaystartdate); }
2062							if (dstart == 'time') { dt.append(starttime); }
2063							if (dstart == 'full') { dt.append(displaydatetimestart); }
2064							if (dstart != 'none' && dend != 'none') { dt.append(' - '); }	// add a dash between start and end
2065							if (dend == 'date') { dt.append(displayenddate); }
2066							if (dend == 'time') { dt.append(endtime); }
2067							if (dend == 'full') { dt.append(displaydatetimeend); }
2068						}
2069						// image
2070						procallistitemright.append(images);
2071						// category
2072						let cats = JSON.parse($('.procalendar.g' + sectionId + ' .thecats').html());
2073						$.each(cats, function (cati, catp) {
2074							if (catp.CategoryId == categoryid) {
2075								if (catp.Title != 'None') {
2076									procallistitemright.append('<div class="category ' + catclass + '"><i class="fa-solid ' + catp.FaIcon + '"></i><span class="category value">' + catp.Title + '</span></div>');
2077								}
2078							}
2079						});
2080						// summary
2081						if (summary.length > 0) {
2082							procallistitemright.append('<div class="summary">' + summary + '</div>');
2083						}
2084						// bottom three
2085						procallistitemright.append('<div class="bottomthree"></div>');
2086						let bottomthree = $('.procalendar.g' + sectionId + ' .eventlistitem[did="d' + p.DEId + '"] .bottomthree');
2087						// contact
2088						if (contactinfo.length > 0) {
2089							bottomthree.append('<div class="contactinfo"><i class="fa-solid fa-user"></i><span class="label">Contact</span><br/>' + contactinfo + '</div>');
2090						}
2091						// location
2092						if (location.length > 0) {
2093							bottomthree.append('<div class="location"><i class="fa-solid fa-location-dot"></i><span class="label">Location</span><br/>' + lname + location + '</div>');
2094						}
2095						// attachments
2096						if (documents.length > 0 || audios.length > 0 || videos.length > 0) {
2097							bottomthree.append('<div class="attachments"><i class="fa-regular fa-paperclip-vertical"></i><span class="label">Attachments</span><br/>' + documents + audios + videos + '</div>');
2098						}
2099					}
2100				}
2101			});
2102
2103			if (procallist.find('.eventlistitem').length == 0) { procallist.append('<h3>No Items to Display</h3>'); }
2104
2105			// bind events
2106			$('.g' + sectionId + ' .calendar-dates').on("click", "a.event", function () {
2107				let ddd = document.getElementById($(this).attr('did'));
2108				ddd.showModal();
2109				ddd.addEventListener("click", e => {
2110					const dialogDimensions = ddd.getBoundingClientRect()
2111					if (
2112						e.clientX < dialogDimensions.left || e.clientX > dialogDimensions.right ||
2113						e.clientY < dialogDimensions.top || e.clientY > dialogDimensions.bottom
2114					) {
2115						ddd.close()
2116					}
2117				});
2118				// display map here if it exists
2119				if (document.getElementById('loc' + $(this).attr('did').substring(1)) != null) {
2120					let loc = document.getElementById('loc' + $(this).attr('did').substring(1)).innerHTML;
2121					let mapid = 'map' + $(this).attr('did').substring(1);	// get the map id
2122					if (loc.length > 0 && mapid != '<!---->') {
2123						document.getElementById(mapid).classList.add("shown");
2124						//document.getElementById(mapid).scrollIntoView({ behavior: 'smooth', block: 'center' });
2125						document.querySelector('.mapcoords').innerHTML = loc;
2126						document.querySelector('.mapid').innerHTML = mapid;
2127						initLocationMap();
2128					}
2129				}
2130			});
2131
2132			$('.dialogs').on("click", "button.closedialog", function () {
2133				dialogs = document.querySelectorAll('dialog');
2134				dialogs.forEach (function(d) {
2135					d.close();
2136				});
2137			});
2138			$('.dialogs dialog').on("click", "button.readmore", function () {
2139				$(this).closest('dialog').find('.description').slideToggle();
2140				$(this).text($(this).text() == 'Read More' ? 'Read Less' : 'Read More');
2141			});
2142
2143		},
2144		failure: function (data) { console.log('failure: ' + JSON.stringify(data)); },
2145		error: function (data) {
2146			console.log('error: ' + JSON.stringify(data));
2147		}
2148	});
2149}
2150
2151function initLocationMap() {
2152	let loc = '';
2153	let mapid = '';
2154	loc = document.querySelector('.mapcoords')?.innerHTML;
2155	if (!loc || loc.length == 0) {
2156		return;
2157	}
2158	loc = loc.replace(/\s/g, '');	// remove any spaces
2159	loc = loc.replace(/\n/g, '');	// remove any newlines
2160	loc = loc.replace(/\r/g, '');	// remove any carriage returns
2161	loc = loc.replace(/\t/g, '');	// remove any tabs
2162	loc = loc.replace(/[{()}]/g, ''); // remove any parentheses and curly braces
2163	mapid = document.querySelector('.mapid').innerHTML;
2164
2165	if (loc.length > 0 && mapid != '<!---->') {
2166		let c = loc.split(",");
2167		let myLatLng = { lat: parseFloat(c[0]), lng: parseFloat(c[1]) };
2168		map = new google.maps.Map(document.getElementById(mapid), {
2169			center: myLatLng,
2170			zoom: 12,
2171			mapId: mapid
2172		});
2173		var marker = new google.maps.marker.AdvancedMarkerElement({
2174			position: myLatLng,
2175			map: map
2176		});
2177	}
2178}
2179
2180function encodeForPath(str) {
2181	// Remove all characters except letters, numbers, and spaces
2182	str = str.replace(/[^a-zA-Z0-9\. ]/g, '');
2183
2184	// Replace one or more spaces with underscores
2185	str = str.replace(/ +/g, '_');
2186
2187	return str;
2188}
2189
2190/**
2191 * searchDM - Searches the Document Manager for documents matching the search string
2192 * This function is called on the searchResults.xsl page after the load
2193 * @param {any} str - the term to search for in the Document Manager
2194 */
2195function searchDM(str) {
2196	$.ajax({
2197		type: "GET",
2198		method: 'GET',
2199		url: "/admin/dm/Public/SearchAllFolders/?search=" + str,
2200		cache: false,
2201		async: true,
2202		contentType: "application/json; charset=utf-8",
2203		dataType: "json",
2204		data: '',
2205		processData: false,
2206		timeout: 300000,
2207		crossDomain: true,
2208		success: function (data) {
2209			document.getElementById('searchtabDMdocs').innerHTML = 'Document Manager (' + data.length + ')';
2210
2211			if (data.length <= 0) {
2212				document.getElementById('dmSearchNote').innerHTML = 'No matching documents have been found.';
2213				return;
2214			}
2215
2216			let html = '';
2217			$.each(data, function (i, p) {
2218				let displayName = p["FileTitle"] ?? p["fileName"];
2219				html += '<div class="dmsearchitem">';
2220				html += '<a target="_blank" href="/uploads/dm/' + p["fileId"] + "/" + encodeForPath(displayName) + '"><i class="fa-solid fa-file"></i> ' + displayName + '</a><br/>';
2221				html += '</div>';
2222			});
2223			document.getElementById('dmSearchNote').innerHTML = html;
2224
2225		},
2226		failure: function (data) { console.log('failure: ' + JSON.stringify(data)); },
2227		error: function (data) {
2228			console.log('error: ' + JSON.stringify(data));
2229		}
2230	});
2231}
2232
2233function grabCemeteries(sectionId) {
2234
2235	let params = new URLSearchParams();
2236	if (sectionId) params.append('sectionID', sectionId);
2237
2238	$.ajax({
2239		type: "GET",
2240		url: "/admin/dm/Public/GetCemeteriesBySection?" + params.toString(),
2241		cache: false,
2242		async: true,
2243		contentType: "application/json; charset=utf-8",
2244		dataType: "json",
2245		timeout: 0,
2246		success: function (data) {
2247			// populate cemetery dropdown
2248			const cemeterySelect = document.querySelector('#sec-' + sectionId.replace(/[{}]/g, '') + ' select[name="cemetery"]');
2249
2250			if (cemeterySelect) {
2251				// Loop through options (skip index 0 which is "All Cemeteries")
2252				Array.from(cemeterySelect.options).forEach(function (option, index) {
2253					// Skip first option ("All Cemeteries")
2254					if (index === 0) return;
2255
2256					// Normalize both values for comparison (remove braces and convert to lowercase)
2257					const normalizedOptionValue = option.value.replace(/[{}]/g, '').toLowerCase();
2258					const normalizedApiValues = data.map(id => id.toLowerCase());
2259
2260					// Remove if not in the API response
2261					if (!normalizedApiValues.includes(normalizedOptionValue)) {
2262						option.remove();
2263					}
2264				});
2265
2266				if (Array.from(cemeterySelect.options).length <= 2) {
2267					// Only all cemeteries and one other option remains, disable the select
2268					const parentElement = cemeterySelect.parentElement;
2269					parentElement.remove();
2270				}
2271			}
2272		}
2273	});
2274}
2275
2276function searchCemetery(event) {
2277	event.preventDefault();
2278
2279	// Get form values
2280	const form = event.target;
2281	const sectionId = form.sectionId.value;
2282	const cemetery = form.cemetery?.value || '';
2283	const fname = form.fname.value.trim();
2284	const lname = form.lname.value.trim();
2285	const section = form.section?.value || '';
2286	const block = form.block?.value || '';
2287	const plot = form.plot?.value || '';
2288	const birthDate = form.birthDate?.value || '';
2289	const deathDate = form.deathDate?.value || '';
2290	const buriedDate = form.buriedDate?.value || '';
2291	
2292	// Build query parameters matching your CemeteryEntry model
2293	let params = new URLSearchParams();
2294	if (sectionId) params.append('sectionID', sectionId);
2295	if (cemetery) params.append('CemeteryID', cemetery);
2296	if (fname) params.append('FName', fname);
2297	if (lname) params.append('LName', lname);
2298	if (section) params.append('Section', section);
2299	if (block) params.append('Block', block);
2300	if (plot) params.append('Plot', plot);
2301	// Only add dates if they're valid
2302	if (birthDate && birthDate.length > 0) params.append('BirthDate', birthDate);
2303	if (deathDate && deathDate.length > 0) params.append('DeathDate', deathDate);
2304	if (buriedDate && buriedDate.length > 0) params.append('BuriedDate', buriedDate);
2305
2306	const pageSize = 50;
2307	params.append('pageSize', pageSize);
2308	let currentPage = 1; // start with first page
2309	params.append('pageNumber', currentPage);
2310
2311	getCemeteries(params, sectionId);
2312}
2313
2314function getCemeteries(params, sectionId, pagination=false) {
2315	// scroll up so its not too jarring
2316	const targetElement = document.querySelector('#sec-' + sectionId.replace(/[{}]/g, '') + ' #cemeteryFormButtons');
2317	targetElement.scrollIntoView({
2318		behavior: 'smooth',
2319		block: 'center',
2320		inline: 'nearest'
2321	});
2322
2323	$.ajax({
2324		type: "GET",
2325		url: "/admin/dm/Public/SearchCemeteryEntries/?" + params.toString(),
2326		cache: false,
2327		async: true,
2328		contentType: "application/json; charset=utf-8",
2329		dataType: "json",
2330		timeout: 0,
2331		beforeSend: function () {
2332			// Show loading indicator
2333			const resultsContainer = document.querySelector('#sec-' + sectionId.replace(/[{}]/g, '') + ' #cemeteryResults');
2334			const loadingDiv = document.createElement('div');
2335			loadingDiv.className = 'loading-cemetery';
2336			loadingDiv.innerHTML = '<div>Searching cemetery records...</div><div class="spinner" style="visibility: visible;"></div>';
2337			resultsContainer.insertBefore(loadingDiv, resultsContainer.firstChild);
2338			
2339			// if we are calling this from the pagination buttons, then leave them on screen
2340			if (!pagination) {
2341				const underCemteries = document.querySelector('#sec-' + sectionId.replace(/[{}]/g, '') + ' #underCemteries');
2342				underCemteries.style.visibility = 'hidden';
2343				
2344				const paginationContainer = document.querySelector('#sec-' + sectionId.replace(/[{}]/g, '') + ' #cemeteryPaginatorButtons');
2345				paginationContainer.style.visibility = 'hidden';
2346			}
2347		},
2348		success: function (data) {
2349			displayCemeteryResults(data, sectionId);
2350			dealWithPagination(data, params, sectionId)
2351		},
2352		failure: function (data) { 
2353			console.log('failure: ' + JSON.stringify(data)); 
2354		},
2355		error: function (data) {
2356			console.log('error: ' + JSON.stringify(data));
2357			alert('Error searching cemetery records. Please try again.');
2358		}
2359	});
2360}
2361
2362function dealWithPagination(data, params, sectionId) {
2363	const underCemteries = document.querySelector('#sec-' + sectionId.replace(/[{}]/g, '') + ' #underCemteries');
2364	if (data.TotalCount === 0) {
2365		return;
2366	}
2367	
2368	underCemteries.style.visibility = 'visible';
2369
2370	const paginationContainer = document.querySelector('#sec-' + sectionId.replace(/[{}]/g, '') + ' #cemeteryPaginatorButtons');
2371	const infoContainer = document.querySelector('#sec-' + sectionId.replace(/[{}]/g, '') + ' #cemeteryPaginationInfo');
2372	const pageInfoContainer = document.querySelector('#sec-' + sectionId.replace(/[{}]/g, '') + ' #cemeteryPageInfo');
2373	
2374	let first = ((data.PageNumber - 1) * data.PageSize) + 1;
2375	
2376	infoContainer.innerHTML = '<span>Displaying ' + first + ' to ' + (first + data.Entries.length - 1) + ' of '+ data.TotalCount + ' total records.</span>';
2377	pageInfoContainer.innerHTML = '<span>Page ' + data.PageNumber + ' of ' + data.TotalPages + '</span>';
2378
2379	if(data.TotalCount > data.Entries.length) {
2380		// display pagination buttons
2381		paginationContainer.style.visibility = 'visible';
2382	}
2383
2384	// add pagination params to original params
2385	const prevPageButton = document.querySelector('#sec-' + sectionId.replace(/[{}]/g, '') + ' #prevPageButton');
2386	const nextPageButton = document.querySelector('#sec-' + sectionId.replace(/[{}]/g, '') + ' #nextPageButton');
2387	const currentPageSelector = document.querySelector('#sec-' + sectionId.replace(/[{}]/g, '') + ' #currentPageSelector');
2388
2389	// Create page selector dropdown
2390	currentPageSelector.innerHTML = '';
2391	for (let i = 1; i <= data.TotalPages; i++) {
2392		currentPageSelector.innerHTML += '<option value="' + i + '"' + (i === data.PageNumber ? ' selected' : '') + '>' + i + '</option>';
2393	}
2394
2395	// on page selector change
2396	currentPageSelector.onchange = function() {
2397		let selectedPage = parseInt(this.value);
2398		params.set('pageNumber', selectedPage);
2399		getCemeteries(params, sectionId, true);
2400	}
2401
2402	// on prev page button click
2403	prevPageButton.onclick = function() {
2404		if (data.PageNumber > 1) {
2405			currentPageSelector.value = data.PageNumber - 1;
2406			currentPageSelector.dispatchEvent(new Event('change'));
2407		}
2408	}
2409
2410	// on next page button click	
2411	nextPageButton.onclick = function() {
2412		if (data.PageNumber < data.TotalPages) {
2413			currentPageSelector.value = data.PageNumber + 1;
2414			currentPageSelector.dispatchEvent(new Event('change'));
2415		}
2416	}
2417}
2418
2419function getCemeteryName(cemeteryId) {
2420    const cemeteryElement = document.querySelector(`#cemeteryData .cemetery-item[data-id="${cemeteryId}"]`);
2421    return cemeteryElement ? cemeteryElement.getAttribute('data-name') : cemeteryId;
2422}
2423
2424function displayCemeteryResults(data, sectionId) {
2425	const resultsContainer = document.querySelector('#sec-' + sectionId.replace(/[{}]/g, '') + ' #cemeteryResults');
2426	
2427	if (!resultsContainer) {
2428		console.error('Results container not found');
2429		return;
2430	}
2431	
2432	if (data.Entries.length === 0) {
2433		resultsContainer.innerHTML = '<div class="no-results"><h2>No Results Found</h2><p>No records match your search criteria.</p></div>';
2434		return;
2435	}
2436	
2437	// Build results HTML matching XSL structure
2438	let html = '<div class="cemetery-results">';
2439	
2440	data.Entries.forEach(function(cemeteryEntry) {
2441		html += '<article class="summaryDisplay">';
2442		html += '<hr /><br />';
2443		
2444		// Cemetery name
2445		html += '<div class="field cemeteryname">';
2446		html += '<h3 class="value"><i class="fa-solid fa-tombstone-blank" aria-hidden="true"></i>';
2447		html += getCemeteryName(cemeteryEntry.CemeteryID);
2448		html += '</h3></div>';
2449		
2450		// Person name
2451		html += '<div class="field inline position">';
2452		html += '<h4 class="value">Name: ' + (cemeteryEntry.LName || '') + ', ' + (cemeteryEntry.FName || '') + '</h4>';
2453		html += '</div>';
2454		
2455		// Details
2456		html += '<div class="cemetery-details">';
2457		
2458		// Left column
2459		html += '<div class="cemetery-column">';
2460		html += '<div class="cemetery-detail-item"><span class="cemetery-detail-label">Section:</span> ' + (cemeteryEntry.Section || '') + '</div>';
2461		html += '<div class="cemetery-detail-item"><span class="cemetery-detail-label">Block:</span> ' + (cemeteryEntry.Block || '') + '</div>';
2462		html += '<div class="cemetery-detail-item"><span class="cemetery-detail-label">Plot:</span> ' + (cemeteryEntry.Plot || '') + '</div>';
2463		html += '</div>';
2464		
2465		// Right column
2466		html += '<div class="cemetery-column">';
2467		html += '<div class="cemetery-detail-item"><span class="cemetery-detail-label">Date of Birth:</span> ' + formatDate(cemeteryEntry.BirthDate) + '</div>';
2468		html += '<div class="cemetery-detail-item"><span class="cemetery-detail-label">Date of Death:</span> ' + formatDate(cemeteryEntry.DeathDate) + '</div>';
2469		html += '<div class="cemetery-detail-item"><span class="cemetery-detail-label">Date Buried:</span> ' + formatDate(cemeteryEntry.BuriedDate) + '</div>';
2470		html += '</div>';
2471		
2472		html += '</div><br /></article>';
2473	});
2474	
2475	html += '</div>';
2476	
2477	resultsContainer.innerHTML = html;
2478}
2479
2480var accessibilityDefaults =
2481	[
2482		{ "id": "acc_logs", "value": false },
2483		{ "id": "acc2020_01", "value": false },
2484		{ "id": "acc2020_02", "value": false },
2485		{ "id": "acc_pdf", "value": true },
2486		{ "id": "acc_2022_slider", "value": false },
2487		{ "id": "acc_2022_h1", "value": false },
2488		{ "id": "acc_2022_social", "value": true }
2489	];
2490
2491/**
2492 * loadAccessibilityOptions - Reads AccessibilityOptions stamped into the page
2493 * by the XSL from /PAGE/SiteInfo/AccessibilityOptions/AOption and merges any
2494 * present values over the accessibilityDefaults array.
2495 */
2496function loadAccessibilityOptions() {
2497	console.log("loadAccessibilityOptions");
2498	if (typeof accessibilityOptions === 'undefined') return;
2499	accessibilityDefaults.forEach(function (item) {
2500		if (accessibilityOptions.hasOwnProperty(item.id)) {
2501			item.value = accessibilityOptions[item.id] === '1';
2502		}
2503	});
2504}
2505
2506/**
2507 * getAccessibilityOption - Helper to look up an accessibility option value by id.
2508 * @param {string} id - the option id to look up
2509 * @returns {boolean} the value, or false if not found
2510 */
2511function getAccessibilityOption(id) {
2512	var match = accessibilityDefaults.find(function (item) { return item.id === id; });
2513	return match ? match.value : false;
2514}
2515
2516document.addEventListener('DOMContentLoaded', function () {
2517	loadAccessibilityOptions();
2518
2519	var logs = getAccessibilityOption('acc_logs');
2520
2521	//rule: acc2020_01 //default: off // case: moves slider and quicklink locations.
2522	if (getAccessibilityOption('acc2020_01')) {
2523		try {
2524			if (logs) { console.log("Applying accessibility rule: acc2020_01"); }
2525			var allContainer = document.querySelector('.allContainer');
2526			var widthContainer = allContainer.querySelector(':scope > .widthContainer');
2527			var mainEl = widthContainer.querySelector('main.container.content');
2528			var slider = allContainer.querySelector(':scope > .slider_wrap1');
2529			var filmstrip = allContainer.querySelector(':scope > .filmstrip_wrap1');
2530			var contentBG = widthContainer.querySelector('.contentBG');
2531
2532			widthContainer.setAttribute('role', 'main');
2533			widthContainer.classList.add('acc2020_01');
2534			widthContainer.parentElement.classList.add('acc2020_01');
2535			mainEl.setAttribute('role', 'none');
2536
2537			if (slider) widthContainer.insertBefore(slider, contentBG);
2538			if (filmstrip) widthContainer.insertBefore(filmstrip, contentBG);
2539		} catch (e) { if (logs) { console.warn("acc2020_01 skipped:", e.message); } }
2540	}
2541
2542	if (getAccessibilityOption('acc2020_02')) {
2543		try {
2544			if (logs) { console.log("Applying accessibility rule: acc2020_02"); }
2545			var main = document.querySelector('main');
2546			var footer = document.querySelector('footer');
2547			//var cardContainer = document.querySelector('.card-container');
2548			var nogroup = document.querySelector('.nogroup.Below');
2549
2550			//if (cardContainer) main.appendChild(cardContainer);
2551			if (nogroup) main.appendChild(nogroup);
2552		} catch (e) { if (logs) { console.warn("acc2020_02 skipped:", e.message); } }
2553	}
2554
2555	//rule: acc_pdf default: on.
2556	if (getAccessibilityOption('acc_pdf')) {
2557		try {
2558			if (logs) { console.log("Applying accessibility rule: acc_pdf"); }
2559			var pdfLinks = document.querySelectorAll('a[href$=".pdf"], a[href$=".PDF"]');
2560			pdfLinks.forEach(function (link) {
2561				if (link.textContent.toLowerCase().includes('pdf') ||
2562					link.querySelector('.sr-only')) return;
2563
2564				var srText = document.createElement('span');
2565				srText.className = 'sr-only';
2566				srText.textContent = ', opens PDF document';
2567				link.appendChild(srText);
2568			});
2569		} catch (e) { if (logs) { console.warn("acc_pdf skipped:", e.message); } }
2570	}
2571
2572	// For 2022 - Present designs
2573	try {
2574		var mainEl = document.getElementById('mainContent');
2575		var container = mainEl.parentElement;
2576		var sliders = container.querySelectorAll(':scope > .nogroup.Top');
2577		var firstChild = mainEl.firstChild;
2578
2579		//rule: acc_2022_slider default: off
2580		if (getAccessibilityOption('acc_2022_slider')) {
2581			try {
2582				if (logs) { console.log("Applying accessibility rule: acc_2022_slider"); }
2583				sliders.forEach(function (el) {
2584					mainEl.insertBefore(el, firstChild);
2585				});
2586			} catch (e) { if (logs) { console.warn("acc_2022_slider skipped:", e.message); } }
2587		}
2588
2589		//rule: acc_2022_h1 default: off
2590		if (getAccessibilityOption('acc_2022_h1')) {
2591			try {
2592				if (logs) { console.log("Applying accessibility rule: acc_2022_h1"); }
2593				var main = document.querySelector('main#mainContent');
2594				if (main) {
2595					main.removeAttribute('aria-label');
2596					if (!main.querySelector('h1')) {
2597						var h1 = document.createElement('h1');
2598						h1.className = 'sr-only';
2599						h1.textContent = document.title || 'Main Content';
2600						main.insertBefore(h1, main.firstChild);
2601					}
2602				}
2603			} catch (e) { if (logs) { console.warn("acc_2022_h1 skipped:", e.message); } }
2604		}
2605	} catch (e) { if (logs) { console.warn("2022 design block skipped:", e.message); } }
2606
2607	// Alert bar - global, ok.
2608	try {
2609		var alert = document.querySelector('.nogroup.Alert');
2610		if (alert) {
2611			alert.setAttribute('role', 'region');
2612			alert.setAttribute('aria-label', 'Alerts');
2613		}
2614	} catch (e) { if (logs) { console.warn("Alert bar skipped:", e.message); } }
2615
2616	// Top navigation - global, ok.
2617	try {
2618		var topbar = document.querySelector('.topbar');
2619		if (topbar) {
2620			topbar.setAttribute('role', 'navigation');
2621			topbar.setAttribute('aria-label', 'Top navigation');
2622		}
2623	} catch (e) { if (logs) { console.warn("Top navigation skipped:", e.message); } }
2624
2625	// Banner - global, ok.
2626	try {
2627		var topbar2 = document.querySelector('.topbar2');
2628		if (topbar2) {
2629			topbar2.setAttribute('role', 'banner');
2630		}
2631	} catch (e) { if (logs) { console.warn("Banner skipped:", e.message); } }
2632
2633	// acc_2022_social default: on
2634	if (getAccessibilityOption('acc_2022_social')) {
2635		try {
2636			if (logs) { console.log("Applying accessibility rule: acc_2022_social"); }
2637			var social = document.querySelector('.socialFixed');
2638			if (social) social.setAttribute('aria-hidden', 'true');
2639		} catch (e) { if (logs) { console.warn("acc_2022_social skipped:", e.message); } }
2640	}
2641
2642	// global, ok.
2643	try {
2644		var innerimage = document.querySelector('.innerimage');
2645		if (innerimage) innerimage.setAttribute('aria-hidden', 'true');
2646	} catch (e) { if (logs) { console.warn("innerimage skipped:", e.message); } }
2647
2648	// Quicklinks - global, ok.
2649	try {
2650		var nogroupTops = document.querySelectorAll('.nogroup.Top');
2651		nogroupTops.forEach(function (el) {
2652			if (el.querySelector('.quicklinkscontainer')) {
2653				el.setAttribute('role', 'region');
2654				el.setAttribute('aria-label', 'Quick links');
2655				var linksOnly = el.querySelector('.owl-stage-outer');
2656				if (linksOnly) {
2657					linksOnly.setAttribute('role', 'navigation');
2658					linksOnly.setAttribute('aria-label', 'Quick links');
2659				}
2660			} else if (el.querySelector('.slidercontainer')) {
2661				el.setAttribute('role', 'region');
2662				el.setAttribute('aria-label', 'Image slidewhow');
2663			}
2664		});
2665	} catch (e) { if (logs) { console.warn("Quicklinks skipped:", e.message); } }
2666
2667	// Modern calendars - global, ok.
2668	try {
2669		var calTables = document.querySelectorAll('.calendarTable');
2670		calTables.forEach(function (table) {
2671			try {
2672				var monthName = table.closest('.calMonth')?.querySelector('.calMonthName')?.textContent || '';
2673				var year = table.closest('.calMonth')?.querySelector('.calYear')?.textContent || '';
2674
2675				var caption = document.createElement('caption');
2676				caption.className = 'sr-only';
2677				caption.textContent = 'Calendar for ' + monthName + ' ' + year;
2678				table.insertBefore(caption, table.firstChild);
2679
2680				table.setAttribute('role', 'grid');
2681				table.setAttribute('aria-label', 'Calendar for ' + monthName + ' ' + year);
2682
2683				var headerRow = table.querySelector('tr.calDayHeading');
2684				if (headerRow) {
2685					headerRow.setAttribute('role', 'row');
2686					headerRow.querySelectorAll('th').forEach(function (th) {
2687						th.setAttribute('role', 'columnheader');
2688					});
2689				}
2690
2691				table.querySelectorAll('tr:not(.calDayHeading)').forEach(function (row) {
2692					row.setAttribute('role', 'row');
2693					row.querySelectorAll('td').forEach(function (td) {
2694						td.setAttribute('role', 'gridcell');
2695					});
2696				});
2697			} catch (e) { if (logs) { console.warn("calendarTable row skipped:", e.message); } }
2698		});
2699	} catch (e) { if (logs) { console.warn("Modern calendars skipped:", e.message); } }
2700});
2701
2702
2703function formatDate(dateStr) {
2704	if (!dateStr) return '';
2705	
2706	const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
2707	const date = new Date(dateStr);
2708	
2709	return months[date.getMonth()] + ' ' + date.getDate() + ', ' + date.getFullYear();
2710}
2711
2712
2713
2714
2715// end accessibility
2716
2717
2718// CMS suggestion button: called from news_resp.xsl / event_calendar_resp.xsl
2719// with the page id and resolved API/app hosts. On click, POSTs to token-init,
2720// sets the iframe src, opens the modal once the iframe finishes loading.
2721function initSuggestionButton(pageId, apiHost, appHost) {
2722	var btn = document.querySelector('.suggest-btn[data-page-id="' + pageId + '"]');
2723	if (!btn || btn.getAttribute('data-suggest-bound') === '1') return;
2724	btn.setAttribute('data-suggest-bound', '1');
2725
2726	var container = btn.closest('.submitContent');
2727	if (!container) return;
2728	var modal = container.querySelector('.suggest-modal');
2729	var iframe = container.querySelector('.suggest-iframe');
2730	var cancelBtn = container.querySelector('.suggest-close-x');
2731	if (!modal || !iframe || !cancelBtn) return;
2732
2733	cancelBtn.addEventListener('click', function () { modal.close(); });
2734
2735	btn.addEventListener('click', function () {
2736		if (btn.classList.contains('is-loading')) return;
2737		btn.classList.add('is-loading');
2738		btn.disabled = true;
2739
2740		fetch(apiHost + '/v2/workflow/cms/embed/token-init', {
2741			method: 'POST',
2742			headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
2743			body: JSON.stringify({ pageId: pageId })
2744		})
2745		.then(function (res) {
2746			if (!res.ok) throw new Error('token-init ' + res.status);
2747			return res.json();
2748		})
2749		.then(function (payload) {
2750			var data = (payload && payload.data) ? payload.data : payload;
2751			if (!data || !data.embedToken) throw new Error('no embed token in response');
2752
2753			function onIframeLoad() {
2754				iframe.removeEventListener('load', onIframeLoad);
2755				btn.classList.remove('is-loading');
2756				btn.disabled = false;
2757				modal.showModal();
2758			}
2759			iframe.addEventListener('load', onIframeLoad);
2760			console.log("token-init data: ", data);
2761			iframe.src = appHost + '/app/cms/suggest?t=' + encodeURIComponent(data.embedToken);
2762		})
2763		.catch(function (err) {
2764			if (window.console) console.warn('[suggest] ' + err.message);
2765			btn.classList.remove('is-loading');
2766			btn.disabled = false;
2767		});
2768	});
2769}

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.