PageSourceSearch

https://providentcu.org/scripts/Site/script.js

js providentcu.org collected 2026-10-03 21:21:06 UTC 21,745 bytes, 569 lines download raw bytes

1const navContainer = document.getElementById('nav-container');
2/* ~~~~~~~~~~~~~~~~~~~~~~~~ image SPECIFIC FUNCTION ~~~~~~~~~~~~~~~~~~~~~~~~ */
3const lazyImages = document.getElementsByClassName('lazyload');
4const closeBtn = document.getElementsByClassName('close');
5const compareInput = document.getElementsByClassName('compare-content');
6const largeImages = document.getElementsByClassName("photo-container");
7const remember = document.getElementById("remember");
8const chatLink = document.getElementsByClassName("chat-link");
9function lazyLoad(e){
10    var imageBG;
11    if(e.getAttribute("data-src")) {
12        imageBG = e.getAttribute("data-src")
13        e.classList.remove('lazyload')
14    }
15    else if (e.getAttribute("data-src-sm") && window.innerWidth < 600) {
16        imageBG = e.getAttribute("data-src-sm");
17    }
18    else if (e.getAttribute("data-src-lrg")) {
19        imageBG = e.getAttribute("data-src-lrg")
20    }
21    e.style.backgroundImage = "url('" + imageBG + "')";
22}
23function imageLoop(){
24    for (i=0;i<lazyImages.length; i++){
25        if((window.pageYOffset - 200) <= lazyImages[i].offsetTop || lazyImages[i].offsetTop < 200 ){
26            lazyLoad(lazyImages[i])
27        }
28    }
29}
30window.addEventListener("resize", imageLoop)
31window.addEventListener("scroll", imageLoop)
32imageLoop();
33/* ~~~~~~~~~~~~~~~~~~~~~~~~ close box ~~~~~~~~~~~~~~~~~~~~~~~~ */
34function hideParent(e){
35	e.parentNode.classList.add('hide')
36}
37/* ~~~~~~~~~~~~~~~~~~~~~~~~ Holiday Hours and other downtime support for chat ~~~~~~~~~~~~~~~~~~~~~~~~ */
38var chatHours = document.createElement("div");
39chatHours.id = "chathours";
40chatHours.style.display = "none";
41chatHours.innerHTML = '<div id="preamble"><p>You may chat live with a Provident agent <b>Monday-Friday 7am - 7pm</b>, and <b>Saturday 9am-5pm</b>. Not available during holidays.</p><!-- This gets replaced by the JS --></div><h2>Holiday Schedule:</h2><table cellspacing="0" class="table" width="100%"><thead><th style="text-align:left">Date</th><th style="text-align:left">Holiday</th></thead><tbody></tbody></table>';
42document.body.appendChild(chatHours)
43/* ~~~~~~~~~~~~~~~~~~~~~~~~ Event Listeners ~~~~~~~~~~~~~~~~~~~~~~~~ */
44/*nav */
45if(remember) {
46	remember.addEventListener('click', function(event){
47		event.target.checked ? event.target.parentNode.classList.add('selected') : event.target.parentNode.classList.remove('selected')
48	})
49}
50document.addEventListener("DOMContentLoaded", function(){
51	function checkGliaLoaded(ChatLink) {
52		if(typeof sm != "undefined" && typeof sm.getApi != "undefined"){
53			upDateGliaChat(ChatLink)
54		} else {
55			setTimeout(checkGliaLoaded, 1000, ChatLink ) 
56		}
57	}
58
59	for(i=0; i<chatLink.length;i++) {
60		let ChatLink = chatLink[i];
61		if(typeof currentlyOpen != "undefined" && currentlyOpen) {
62			ChatLink.setAttribute('data-sm-show-media-selection-on', 'click');
63			checkGliaLoaded(ChatLink);		
64		} else {
65			ChatLink.addEventListener('click', function(event){
66				popDetails(document.getElementById('chathours').innerHTML, 'Chat is Closed', 650, 550, false, false); 
67				event.preventDefault();
68			})
69		}
70	}
71})
72function upDateGliaChat(ChatLink){
73	sm.getApi({version: 'v1'}).then(function(glia) {
74		glia.removeEventListener(glia.EVENTS.QUEUE_STATE_UPDATE, onQueueStateUpdateForChat)
75		function onQueueStateUpdateForChat(queueState) {
76		  if (queueState.state !== queueState.QUEUE_STATES.CAN_QUEUE) {
77			if(ChatLink) ChatLink.classList.add("disabled");
78		  } else {
79			if(ChatLink) ChatLink.classList.remove("disabled");
80		  }
81		}
82		glia.addEventListener(glia.EVENTS.QUEUE_STATE_UPDATE, onQueueStateUpdateForChat);
83	  });
84} 
85
86for (i=0;i<closeBtn.length;i++){
87	closeBtn[i].addEventListener("click", function(event) {
88		hideParent(event.target)
89		if(document.getElementsByClassName("slider-controls")[0]) {
90			document.getElementsByClassName("slider-controls")[0].focus();
91		}
92	});
93}
94
95imageLoop()
96/* ~~~~~~~~~~~~~~ alert script ~~~~~~~~~~~~~~~~~~~~~ */
97const downtimeAlert = document.getElementsByClassName("downtime")[0]
98const signOn = document.getElementById("action-links-container").getElementsByTagName("LI")[0]
99if(downtimeAlert) {
100	signOn.style.opacity = ".5"
101	signOn.getElementsByTagName("A")[0].style.cursor = "default"
102	signOn.getElementsByTagName("A")[0].setAttribute("href", "#")
103	document.getElementById("signon").style.display = "none"
104}
105/* ~~~~~~~~~~~~~~~~~~~~~~~~ NAV SPECIFIC FUNCTION ~~~~~~~~~~~~~~~~~~~~~~~~ */
106const nav = document.getElementsByTagName('nav')[0];
107const dropNav = document.getElementsByClassName('drop-nav');
108const topNav = document.getElementsByClassName('top-nav');
109const burgerMenu = document.getElementById("burger-menu");
110const mainNavContainer = document.getElementById("nav-link-container");
111const searchButton = document.getElementById('search-bar');
112const topNavWidget = document.getElementsByClassName('top-navigation-widget')[0];
113if(typeof focusableElements == "undefined"){
114	var focusableElements = ['a[href]', 'area[href]', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'button:not([disabled])', 'iframe', 'object', 'embed', '[contenteditable]', '[tabindex]:not
114([tabindex^="-"])'];
115}
116const focEl = document.querySelectorAll(focusableElements); // not used?
117function deselectTopNav(){
118	topNavWidget.classList.remove('expand');
119	document.getElementsByTagName('body')[0].classList.remove('expand');
120	for(i=0; i < topNav.length;i++){
121		topNav[i].querySelector('button').classList.remove('open');
122		topNav[i].querySelector('.drop-nav').classList.remove('open');
123		topNav[i].classList.remove('open');
124	}
125}
126function hightlightNav(event) {
127	deselectTopNav();
128	event.target.classList.add('open');
129	event.target.nextElementSibling.classList.add('open');
130	topNavWidget.classList.add('open');
131	topNavWidget.classList.add('expand');
132	document.getElementsByTagName('body')[0].classList.add('expand');
133	event.target.closest(".top-nav").classList.add('open');
134}
135/* ~~~~~~~~~~~~~~~~~~~~~~~~ search ~~~~~~~~~~~~~~~~~~~~~~~~ */
136const navSearch = document.getElementById('nav-search')
137const searchField = document.getElementById('SearchInput')
138function openSearch(e){
139	navSearch.classList.toggle('open');
140	if(navSearch.classList.contains('open')) setTimeout(function() { searchField.focus()}, 501);
141	e.preventDefault();
142}
143if(navSearch) navSearch.getElementsByTagName("A")[0].addEventListener('click', openSearch);
144/* ~~~~~~~~~~~~~~~~~~~~~~~~ dropdown ~~~~~~~~~~~~~~~~~~~~~~~~ */
145for (i=0; i< topNav.length;i++){
146	topNav[i].firstElementChild.addEventListener("click", function(event) {
147		if (event.target.tagName !="A"){
148			navContainer.classList.add('open');
149			burgerMenu.classList.add('open');
150			topNavWidget.classList.add('open');    
151			hightlightNav(event);
152		}
153	});
154}
155if(burgerMenu){
156	burgerMenu.addEventListener('click', function(event){
157		burgerMenu.classList.toggle('open');
158		navContainer.classList.toggle('open');
159		topNavWidget.classList.toggle('open');
160		if(burgerMenu.classList != "expand") {
161			topNavWidget.classList.remove('expand');
162			document.getElementsByTagName('body')[0].classList.remove('expand');	
163		}  
164	})
165}
166/* outside of the nav */
167Element.prototype.parents = function(selector){
168	// returns all ancestor elements, or optionally all that match the selector. If none, returns false
169	var elements = [];
170	var elem = this;
171	var useSelector = selector !== undefined;
172	while ((elem = elem.parentElement) !== null){
173		if(elem.nodeType !== Node.ELEMENT_NODE) continue;
174		if(!useSelector || elem.matches(selector)) elements.push(elem);
175	}
176	if(elements.length == 0) elements = false
177		return elements;
178	}
179Element.prototype.matchingParent = function(selector){
180	// returns first matching ancestor element (or parentElement if no selector). If none, returns false
181	var elem = this;
182	var useSelector = selector !== undefined;
183	while ((elem = elem.parentElement) !== null){
184		if(elem.nodeType !== Node.ELEMENT_NODE) continue;
185		if(!useSelector || elem.matches(selector)) return elem;
186	}
187	return false;
188}
189/* IE polyfill for matches() */
190if (!Element.prototype.matches) {
191	Element.prototype.matches = 
192		Element.prototype.matchesSelector || 
193		Element.prototype.mozMatchesSelector ||
194		Element.prototype.msMatchesSelector || 
195		Element.prototype.oMatchesSelector || 
196		Element.prototype.webkitMatchesSelector ||
197		function(s) {
198			var matches = (this.document || this.ownerDocument).querySelectorAll(s),
199				i = matches.length;
200			while (--i >= 0 && matches.item(i) !== this) {}
201			return i > -1;            
202		};
203}
204/* IE polyfill for forEach() */
205if (window.NodeList && !NodeList.prototype.forEach) {
206	NodeList.prototype.forEach = Array.prototype.forEach;
207}
208document.querySelectorAll(focusableElements).forEach(function(focEl){
209	let mq = window.matchMedia('(max-width: 768px)');
210	focEl.addEventListener("focus", function(event){
211		let inTopNav = (event.target).classList.contains("bar") || (event.target) == document.getElementById("nav-chat-link") || (event.target).matchingParent(".top-nav") || (event.target) == burgerMenu || (event.target).matchingParent('#burger-menu') || (event.target).matchingParent('.search-bar') || (event.target).matchingParent("iframe") || (event.target).matchingParent("#nav-search") || (mq.matches && (event.target).matchingParent("#action-links-container"));
212		let inSearch = (event.target).matchingParent("#nav-search");
213		if(!inTopNav){
214			deselectTopNav()
215			if(navContainer) {
216				navContainer.classList.remove("open");
217			}
218			if(burgerMenu) {
219				burgerMenu.classList.remove('open');
220			}
221			topNavWidget.classList.remove('open');    
222		}
223		if (navSearch && !inSearch){ /* search exists and the click element was the seach*/
224			if(event.target.classList.contains("focus-visible")) navSearch.classList.remove('open');
225	   	}
226	})
227})
228let scrollEventNav = window.onscroll=function(){
229	var initialY = window.pageYOffset;
230	var newY;
231	setTimeout(function () {
232		newY = window.pageYOffset
233		var scrollAmount = Math.abs(initialY - newY)
234		if(scrollAmount > 100) {
235			deselectTopNav()
236			burgerMenu.classList.remove('open');
237			navSearch.classList.remove("open")
238			if(navContainer) navContainer.classList.remove("open")
239		}
240	}
241	,500);
242}
243// function to change one type of element into another, while preserving contents and attributes (used in a11y.js, but probably useful elsewhere):
244function replaceElement(source, newType) {
245	const frag = document.createDocumentFragment();
246	while (source.firstChild) {
247	  frag.appendChild(source.firstChild);
248	}
249	const newElem = document.createElement(newType);
250	newElem.appendChild(frag);
251	
252	if (source.hasAttributes()) {
253		let attrs = source.attributes;
254		for(let i = attrs.length - 1; i >= 0; i--) {
255		  newElem.setAttribute( attrs[i].name, attrs[i].value );
256		}
257	}
258	source.parentNode.replaceChild(newElem, source);
259}
260/* ~~~~~~~~~~~~~~~~~~~~~~~~ comparison ~~~~~~~~~~~~~~~~~~~~~~~~ */
261let counter = 0;
262const allContainers = document.getElementsByClassName('compare');
263const allSelected = document.getElementsByClassName('compare-selected');
264const comparisonPopup = document.getElementById('comparisonButton');
265const tableWidget = document.getElementsByClassName('table-widget')[0]
266const legalWidget = document.getElementsByClassName('legal-widget')[0]
267function visibleButtons(state){
268	(state == true) ? comparisonPopup.removeAttribute("disabled") : comparisonPopup.setAttribute("disabled", "disabled");
269};
270//runs when a card is clicked
271function checkState(accountSelected) {
272	counter = 0
273	if(accountSelected.classList.contains('compare-selected')){
274		accountSelected.classList.remove("compare-selected");
275	}
276	else {
277		accountSelected.classList.add("compare-selected");
278	}
279	for(i=0; i<allContainers.length; i++){
280		if(allContainers[i].classList.contains('compare-selected')){
281			counter +=1
282		}
283	}
284	if(counter >1 && counter <= 3 ) {
285		visibleButtons(true)
286	}
287	else if (counter > 3){
288		accountSelected.classList.remove("compare-selected")
289		alert ("Our comparison page supports up to three cards at a time. To compare this card please uncheck one of your previous choices.")
290	}
291	else {
292		visibleButtons(false)
293	}
294}
295function displayCompTable(showArr,hideArr){
296	tableWidget.style.display = "block";
297	if(legalWidget) {
298		legalWidget.style.display = "block";
299
300	}
301	for(i=0; i <showArr.length;i++) {
302		let showCell = document.getElementsByClassName(showArr[i])
303		for(j=0;j<showCell.length;j++) {
304			showCell[j].style.display = "table-cell"
305		}
306	}
307	for(i=0; i <hideArr.length;i++) {
308		let hideCell = document.getElementsByClassName(hideArr[i])
309		for(j=0;j<hideCell.length;j++) {
310			hideCell[j].style.display = "none"
311		}
312	}
313}
314if(comparisonPopup) {
315	if(tableWidget) {
316		tableWidget.style.display = "none";
317	}
318	if(legalWidget) {
319		legalWidget.style.display = "none";
320	}
321	comparisonPopup.addEventListener('click', function (event){
322		if(!comparisonPopup.getAttribute("disabled")) {
323			let comparisonArr = []
324			let comparisonHide = []
325			for(i=0;i<allContainers.length;i++) {
326				if(allContainers[i].classList.contains("compare-selected")){
327					comparisonArr.push(allContainers[i].id)
328				}
329				else {
330					comparisonHide.push(allContainers[i].id)
331				}
332			}
333			displayCompTable(comparisonArr,comparisonHide)
334			comparisonArr = []
335			comparisonHide = []
336	
337		}
338	})
339}
340for(i = 0; i < allContainers.length; i++) {
341	allContainers[i].addEventListener("click", function(event){
342		checkState(event.target)
343	})
344	if(allContainers[i].classList.contains('compare-selected')) {
345		counter +=1;
346	}
347}
348if(counter > 1) {
349	visibleButtons(true)
350}
351/* ~~~~~~~~~~~~~~~~~~~~~~~~ accordion ~~~~~~~~~~~~~~~~~~~~~~~~ */
352Array.prototype.slice.call(document.querySelectorAll('.accordion')).forEach(function (accordion) {
353	// Allow for multiple accordion sections to be expanded at the same time
354	var allowMultiple = accordion.hasAttribute('data-allow-multiple');
355	// Allow for each toggle to both open and close individually
356	var allowToggle = (allowMultiple) ? allowMultiple : accordion.hasAttribute('data-allow-toggle');
357	// Create the array of toggle elements for the accordion group
358	var triggers = Array.prototype.slice.call(accordion.querySelectorAll('.accordion-trigger'));
359	var panels = Array.prototype.slice.call(accordion.querySelectorAll('.Accordion-panel'));
360	accordion.addEventListener('click', function (event) {
361		var target = event.target;
362		if (target.classList.contains('accordion-trigger')) {
363			// Check if the current toggle is expanded.
364			var isExpanded = target.getAttribute('aria-expanded') == 'true';
365			var active = accordion.querySelector('[aria-expanded="true"]');
366			// without allowMultiple, close the open accordion
367			if (!allowMultiple && active && active !== target) {
368			// Set the expanded state on the triggering element
369			active.setAttribute('aria-expanded', 'false');
370			// Hide the accordion sections, using aria-controls to specify the desired section
371			document.getElementById(active.getAttribute('aria-controls')).setAttribute('hidden', '');
372			// When toggling is not allowed, clean up disabled state
373			if (!allowToggle) {
374				active.removeAttribute('aria-disabled');
375			}
376		}
377		if (!isExpanded) {
378			// Set the expanded state on the triggering element
379			target.setAttribute('aria-expanded', 'true');
380			// Hide the accordion sections, using aria-controls to specify the desired section
381			document.getElementById(target.getAttribute('aria-controls')).removeAttribute('hidden');
382  
383			// If toggling is not allowed, set disabled state on trigger
384			if (!allowToggle) {
385				target.setAttribute('aria-disabled', 'true');
386			}
387		}
388		else if (allowToggle && isExpanded) {
389			// Set the expanded state on the triggering element
390			target.setAttribute('aria-expanded', 'false');
391			// Hide the accordion sections, using aria-controls to specify the desired section
392			document.getElementById(target.getAttribute('aria-controls')).setAttribute('hidden', '');
393		}
394
395		event.preventDefault();
396		}
397	});
398
399	// Bind keyboard behaviors on the main accordion container
400	accordion.addEventListener('keydown', function (event) {
401		var target = event.target;
402		var key = event.which.toString();
403
404		var isExpanded = target.getAttribute('aria-expanded') == 'true';
405		var allowToggle = (allowMultiple) ? allowMultiple : accordion.hasAttribute('data-allow-toggle');
406
407		// 33 = Page Up, 34 = Page Down
408		var ctrlModifier = (event.ctrlKey && key.match(/33|34/));
409
410		// Is this coming from an accordion header?
411		if (target.classList.contains('accordion-trigger')) {
412			// Up/ Down arrow and Control + Page Up/ Page Down keyboard operations
413			// 38 = Up, 40 = Down
414			if (key.match(/38|40/) || ctrlModifier) {
415				var index = triggers.indexOf(target);
416				var direction = (key.match(/34|40/)) ? 1 : -1;
417				var length = triggers.length;
418				var newIndex = (index + length + direction) % length;
419
420				triggers[newIndex].focus();
421
422				event.preventDefault();
423			}
424			else if (key.match(/35|36/)) {
425				// 35 = End, 36 = Home keyboard operations
426				switch (key) {
427				// Go to first accordion
428					case '36':
429						triggers[0].focus();
430						break;
431						// Go to last accordion
432					case '35':
433						triggers[triggers.length - 1].focus();
434					break;
435				}
436				event.preventDefault();
437			}
438		}
439	});
440
441	// These are used to style the accordion when one of the buttons has focus
442	accordion.querySelectorAll('.accordion-trigger').forEach(function (trigger) {
443
444		trigger.addEventListener('focus', function (event) {
445			accordion.classList.add('focus');
446		});
447
448		trigger.addEventListener('blur', function (event) {
449			accordion.classList.remove('focus');
450		});
451
452	});
453
454	// Minor setup: will set disabled state, via aria-disabled, to an
455	// expanded/ active accordion which is not allowed to be toggled close
456	if (!allowToggle) {
457		// Get the first expanded/ active accordion
458		var expanded = accordion.querySelector('[aria-expanded="true"]');
459
460		// If an expanded/ active accordion is found, disable
461		if (expanded) {
462			expanded.setAttribute('aria-disabled', 'true');
463		}
464	}
465});
466  //needed if there is more than 1 accordion on the page because the automatic numbering only accounts for a single accordion
467const accordions = document.getElementsByClassName("content-accordion");
468if (accordions[1]){
469	counter = 1
470	// find all of the accordion groups
471	for (i =0; i < accordions.length; i++) {
472
473		let accordionPanel = accordions[i].getElementsByClassName("Accordion-panel");
474		// find all of the dropdowns in the group
475		for ( j = 0; j < accordionPanel.length; j++) {
476			let newAttribute = "section" + counter
477			accordionPanel[j].id = newAttribute
478			accordionPanel[j].previousElementSibling.querySelector("button").setAttribute("aria-controls", newAttribute)
479			counter += 1
480		}
481	}
482}
483var touching = false;
484var delayClockUpdate = false;
485function getCookie(timeOut) {
486	var name = timeOut + "=";
487	var decodedCookie = decodeURIComponent(document.cookie);
488	var ca = decodedCookie.split(';');
489	for(var i = 0; i <ca.length; i++) {
490		var c = ca[i];
491		while (c.charAt(0) == ' ') {
492		c = c.substring(1);
493		}
494		if (c.indexOf(name) == 0) {
495		return c.substring(name.length, c.length);
496		}
497	}
498	return "";
499}
500let onlineBankingSignon = document.getElementsByClassName("ob-status")[0]
501let docBod = document.body
502
503const maxSeconds = 120;
504const nextTimeoutCookie = getCookie("nextTimeout");
505let nextTimeout, leftOnlineBanking;
506if(nextTimeoutCookie && nextTimeoutCookie != ""){
507	nextTimeout = nextTimeoutCookie.split("|")[0];
508	leftOnlineBanking = nextTimeoutCookie.split("|")[1];
509} else nextTimeout = "";
510
511let viewableTickCount = 0;
512
513function sessionCountdownTick() {
514	let onlineBankingNotice = document.getElementById("signed-in")
515	let onlineBankingTimer = document.getElementById("online-banking-timer")
516	let dt = new Date(); 
517	if (nextTimeout != "" && nextTimeout != "0" && onlineBankingNotice){		
518		let timeLeft = parseInt(nextTimeout) - dt.getTime();
519		let timeSinceLeavingOLB =  dt.getTime() - parseInt(leftOnlineBanking);
520		if (timeLeft > 0) {
521			onlineBankingSignon.setAttribute('href', 'https://accountmanager.providentcu.org/');
522
523			if (!leftOnlineBanking || (timeSinceLeavingOLB / 1000 ) >= maxSeconds){
524				docBod.classList.remove('signon-active')
525			} else {
526				docBod.classList.add('signon-active')		
527			}
528
529
530			var minutes = Math.floor(timeLeft / 1000 / 60);
531			var seconds = Math.floor(timeLeft / 1000 % 60);
532			if (minutes < 0) {
533				minutes = 0;
534			}
535			var displayString = minutes + ":";
536
537			if (seconds < 10) {
538				if (seconds < 0) {
539					seconds += "00";
540				}
541				else {
542					seconds = "0" + seconds;
543				}
544			}
545			displayString += seconds;
546			onlineBankingTimer.textContent = displayString
547			setTimeout("sessionCountdownTick()", 1000);	
548		}
549		else {
550			timeout()
551		}
552	}
553	else {
554		docBod.classList.remove('signon-active')		
555	}             
556}
557function timeout() {
558	var timer = document.getElementById('online-banking-timer')
559	timer.parentNode.innerHTML = "Your online banking session has expired.";
560	docBod.classList.add('expired')
561	document.cookie = "nextTimeout=; path=; SameSite=None; Secure; Domain=.providentcu.org"
562	onlineBankingSignon.setAttribute('href', 'https://accountmanager.providentcu.org/')
563	var messageBar = document.getElementById('signed-in')
564	messageBar.querySelector('a').setAttribute('href', 'https://accountmanager.providentcu.org/')
565
566}
567
568
569sessionCountdownTick()

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.