PageSourceSearch

https://www.sna.org.sg/wp-content/themes/hello-theme-child-master/theme/default/js/content.js?ver=1785916656

js sna.org.sg collected 2026-10-01 15:58:06 UTC 12,929 bytes, 467 lines download raw bytes

1var contentjs = {
2	init: function () {
3		this.directorsPopup.init();
4		this.directorsNav.init();
5		this.exclusiveEvents.init();
6		//this.stickyRegistration.init();
7		this.companySelected.init();
8		//this.initBillingFields.init();
9	},
10	directorsPopup: {
11		init: function () {
12			const popupId = 81;
13			let popupData = {};
14
15			function captureData($item) {
16				const $img = $item.find('.js-data-image img').first();
17				const $bio = $item.find('.js-data-bio').first();
18
19				popupData = {
20					title: $.trim($item.find('.js-data-title').first().text()),
21					position: $.trim($item.find('.js-data-position').first().text()),
22					bio: $.trim($bio.html() || ''),
23					image: {
24						src: $img.attr('src') || '',
25						srcset: $img.attr('srcset') || '',
26						sizes: $img.attr('sizes') || '',
27						alt: $img.attr('alt') || ''
28					}
29				};
30			}
31
32			function setImage($img, data) {
33				if (!data || !data.src || !$img.length) return;
34
35				$img.attr({ src: data.src, alt: data.alt || '' });
36				data.srcset ? $img.attr('srcset', data.srcset) : $img.removeAttr('srcset');
37				data.sizes ? $img.attr('sizes', data.sizes) : $img.removeAttr('sizes');
38			}
39
40			function injectData(attempt) {
41				attempt = attempt || 0;
42
43				const $popup = $('#elementor-popup-modal-' + popupId);
44
45				if (!$popup.length) {
46					if (attempt < 10) requestAnimationFrame(function () {
47						injectData(attempt + 1);
48					});
49					return;
50				}
51
52				const bio = popupData.bio || '';
53				const safeBio = bio.indexOf('<p') > -1 ? bio : '<p>' + bio + '</p>';
54
55				$popup.find('.js-popup-title p').first().text(popupData.title || '');
56				$popup.find('.js-popup-position p').first().text(popupData.position || '');
57				$popup.find('.js-popup-bio').first().html(safeBio);
58
59				setImage($popup.find('.js-popup-image img').first(), popupData.image);
60
61				requestAnimationFrame(function () {
62					$('body').removeClass('loop-popup-loading');
63				});
64			}
65
66			$(document)
67				.off('click.loopPopup')
68				.on('click.loopPopup', '.js-open-loop-popup', function (e) {
69				e.preventDefault();
70
71				$('body').addClass('loop-popup-loading');
72
73				captureData($(this).closest('.e-loop-item'));
74
75				if (window.elementorProFrontend && elementorProFrontend.modules && elementorProFrontend.modules.popup) {
76					elementorProFrontend.modules.popup.showPopup({ id: popupId });
77					injectData();
78				}
79			});
80
81			$('body').addClass('loop-popup-ready');
82		}
83	},
84	directorsNav: {
85		init: function () {
86			var $nav = $('#directors-nav');
87			if (!$nav.length) return;
88
89			var items = [];
90			var activeId = '';
91			var ticking = false;
92
93			function getVar(name) {
94				return parseFloat(getComputedStyle(document.documentElement).getPropertyValue(name)) || 0;
95			}
96
97			function updateNavHeight() {
98				document.documentElement.style.setProperty('--directors-nav-height', ($nav.outerHeight() || 0) + 'px');
99			}
100
101			function getOffset() {
102				return getVar('--sna-header-height') + getVar('--wpadminbar-offset') + getVar('--directors-nav-height');
103			}
104
105			if (!$nav.find('.directors-nav-toggle').length) {
106				$nav.prepend('<button type="button" class="directors-nav-toggle" aria-expanded="false"></button>');
107			}
108
109			var $toggle = $nav.find('.directors-nav-toggle');
110
111			function updateDropdownLabel() {
112				var text = $nav.find('.elementor-widget-heading.is-active a').first().text()
113				|| $nav.find('.elementor-widget-heading a').first().text()
114				|| 'Select section';
115
116				$toggle.text($.trim(text));
117			}
118
119			$nav.find('.elementor-widget-heading a[href^="#"]').each(function () {
120				var $link = $(this);
121				var id = ($link.attr('href') || '').slice(1);
122				var $target = $('#' + id);
123				var $widget = $link.closest('.elementor-widget-heading');
124
125				if ($target.length && $widget.length) {
126					items.push({ id: id, $target: $target, $widget: $widget });
127				}
128			});
129
130			if (!items.length) {
131				updateNavHeight();
132				updateDropdownLabel();
133				return;
134			}
135
136			function setActive(id) {
137				if (id === activeId) return;
138
139				activeId = id;
140
141				$.each(items, function (_, item) {
142					item.$widget.toggleClass('is-active', item.id === id);
143				});
144
145				updateDropdownLabel();
146			}
147
148			function update() {
149				var triggerY = getOffset() + window.innerHeight * 0.15;
150				var current = items[0];
151
152				updateNavHeight();
153
154				$.each(items, function (_, item) {
155					if (item.$target[0].getBoundingClientRect().top <= triggerY) {
156						current = item;
157					}
158				});
159
160				setActive(current.id);
161				ticking = false;
162			}
163
164			function requestUpdate() {
165				if (ticking) return;
166
167				ticking = true;
168				requestAnimationFrame(update);
169			}
170
171			$toggle.off('click.directorsNav').on('click.directorsNav', function () {
172				var open = !$nav.hasClass('is-open');
173
174				$nav.toggleClass('is-open', open);
175				$toggle.attr('aria-expanded', open);
176			});
177
178			$nav.off('click.directorsNavLinks').on('click.directorsNavLinks', '.elementor-widget-heading a[href^="#"]', function (e) {
179				var id = ($(this).attr('href') || '').slice(1);
180				var $target = $('#' + id);
181
182				if (!$target.length) return;
183
184				e.preventDefault();
185
186				$nav.removeClass('is-open');
187				$toggle.attr('aria-expanded', 'false');
188				setActive(id);
189
190				var offset = getOffset();
191				var targetTop = $target.offset().top;
192				var finalTop = Math.max(0, targetTop - offset);
193
194				console.log('[Directors Nav Offset]', {
195					header: getVar('--sna-header-height'),
196					adminBar: getVar('--wpadminbar-offset'),
197					directorsNav: getVar('--directors-nav-height'),
198					totalOffset: offset,
199					targetTop: targetTop,
200					finalScrollTop: finalTop
201				});
202
203				$('html, body').stop(true).animate({
204					scrollTop: finalTop
205				}, 500);
206			});
207
208			$(window)
209				.off('scroll.directorsNav resize.directorsNav load.directorsNav')
210				.on('scroll.directorsNav resize.directorsNav load.directorsNav', requestUpdate);
211
212			if ('ResizeObserver' in window) {
213				new ResizeObserver(requestUpdate).observe($nav[0]);
214			}
215
216			update();
217			updateDropdownLabel();
218		}
219	},
220	exclusiveEvents: {
221		init: function () {
222			if (!$('.sna-exclusive-events').length) return;
223
224			new Swiper('.sna-exclusive-events', {
225				slidesPerView: 1,
226				spaceBetween: 24,
227
228				pagination: {
229					el: '.sna-exclusive-events .swiper-pagination',
230					clickable: true,
231				},
232
233				breakpoints: {
234					480: {
235						slidesPerView: 1,
236					},
237					768: {
238						slidesPerView: 2,
239					},
240					1025: {
241						slidesPerView: 4,
242					}
243				}
244			});
245		}
246	},
247	stickyRegistration: {
248		init: function () {
249			var $sticky = $('.event-registration-section');
250			var $stopSection = $('.other-events-section');
251
252			if (!$sticky.length || !$stopSection.length) return;
253
254			var stickyOffset = 180;
255
256			function update() {
257				if (window.innerWidth <= 1024) {
258					$sticky.css({
259						position: '',
260						top: '',
261						left: '',
262						width: ''
263					});
264
265					return;
266				}
267
268				var scrollTop = $(window).scrollTop();
269				var stickyTop = $sticky.data('original-top');
270				var stickyHeight = $sticky.outerHeight();
271				var stopPoint = $stopSection.offset().top - stickyHeight - stickyOffset;
272
273				if (scrollTop < stickyTop - stickyOffset) {
274					$sticky.css({
275						position: 'relative',
276						top: '',
277						left: '',
278						width: ''
279					});
280
281					return;
282				}
283
284				// Reached stop section
285				if (scrollTop >= stopPoint) {
286					var topValue = $stopSection.offset().top - scrollTop - stickyHeight;
287					$sticky.css({
288						position: 'fixed',
289						top: topValue + 'px',
290						left: $sticky.data('original-left') + 'px',
291						width: $sticky.data('original-width') + 'px'
292					});
293
294					return;
295				}
296
297				$sticky.css({
298					position: 'fixed',
299					top: stickyOffset + 'px',
300					left: $sticky.data('original-left') + 'px',
301					width: $sticky.data('original-width') + 'px'
302				});
303			}
304
305			function calculateDimensions() {
306				if (window.innerWidth <= 1024) return;
307				if ($sticky.css('position') === 'fixed') {
308					$sticky.css({
309						position: 'relative',
310						top: '',
311						left: '',
312						width: ''
313					});
314				}
315
316				$sticky.data('original-top', $sticky.offset().top);
317				$sticky.data('original-left', $sticky.offset().left);
318				$sticky.data('original-width', $sticky.outerWidth());
319			}
320			calculateDimensions();
321			$(window).on(
322				'load resize',
323				function () {
324					calculateDimensions();
325					update();
326				}
327			);
328			$(window).on('scroll', update);
329		}
330	},
331	companySelected: {
332		init: function () {
333			var NOTICE_ID = 'sna-company-notice';
334
335			// Find a currently-checked radio whose option starts with "Company"
336			function companyRadio() {
337				return $('input[type="radio"]:checked').filter(function () {
338					var val   = this.value || '';
339					var label = $(this).closest('label').text() || '';
340					return /^\s*Company/i.test(val) || /^\s*Company/i.test(label);
341				});
342			}
343
344			function isCompanySelected() {
345				return companyRadio().length > 0;
346			}
347
348			// The "Continue" button(s) — matched by inner text so it survives class changes
349			function continueButtons() {
350				return $('.am-button, button').filter(function () {
351					return $.trim($(this).find('.am-button__inner').text() || $(this).text()) === 'Continue';
352				});
353			}
354
355			function noticeHtml() {
356				return '' +
357					'<div id="' + NOTICE_ID + '" style="margin:0 0 18px 0;padding:14px 16px;' +
358					'border:1px solid #E0B84C;background:#FFF8E6;border-radius:8px;' +
359					'color:#5A4A00;font-size:14px;line-height:1.55;">' +
360					'<strong>Company / Institution payment</strong><br>' +
361					'Please email <a href="mailto:[email protected]" ' +
362					'style="color:#1246D6;text-decoration:underline;">[email protected]</a> ' +
363					'with your registration details and billing information. ' +
364					'Our event manager will guide you through the next steps.' +
365					'</div>';
366			}
367
368			function setButtonState(disabled) {
369				continueButtons().each(function () {
370					var $b = $(this);
371					if (disabled) {
372						$b.prop('disabled', true).attr('aria-disabled', 'true')
373							.css({ 'opacity': '0.45', 'cursor': 'not-allowed', 'pointer-events': 'none' });
374					} else {
375						$b.prop('disabled', false).removeAttr('aria-disabled')
376							.css({ 'opacity': '', 'cursor': '', 'pointer-events': '' });
377					}
378				});
379			}
380
381			function sync() {
382				var company = isCompanySelected();
383				
384				if (company) {
385					if (!$('#' + NOTICE_ID).length) {
386						var $anchor = companyRadio().first()
387						.closest('.el-form-item, .am-custom-field, [class*="custom-field"]');
388						if (!$anchor.length) $anchor = companyRadio().first().closest('.el-radio-group');
389						if (!$anchor.length) $anchor = companyRadio().first().closest('label').parent();
390						$anchor.after(noticeHtml());
391					}
392				} else {
393					$('#' + NOTICE_ID).remove();
394				}
395
396				// Continue button
397				setButtonState(company);
398			}
399
400			// Hard block: stop the click reaching Vue while Company is selected.
401			// Capture phase = runs before Amelia's own handler.
402			document.addEventListener('click', function (e) {
403				if (!isCompanySelected()) return;
404				var btn = e.target.closest('.am-button, button');
405				if (!btn) return;
406				var label = (btn.querySelector('.am-button__inner') || btn).textContent || '';
407				if ($.trim(label) === 'Continue') {
408					e.preventDefault();
409					e.stopPropagation();
410				}
411			}, true);
412
413			// Amelia re-renders on step changes, so listen + poll to stay in sync
414			$(document).on('change', 'input[type="radio"]', sync);
415			setInterval(sync, 200);
416			sync();
417		}
418	},
419	initBillingFields: {
420		init: function () {
421			function initFeesCondition() {
422				var $feesField = $('#am-cf-17');
423
424				if (!$feesField.length) return;
425
426				var $institutionField = $('#am-cf-19');
427				var $billingNameField = $('#am-cf-20');
428				var $billingEmailField = $('#am-cf-21');
429
430				function toggleCompanyFields() {
431					var selectedValue = $feesField.find('input:checked').val();
432
433					if (selectedValue === 'Company (Please email [email protected] with your registration details and billing information.)') {
434						$institutionField.slideDown(100);
435						$billingNameField.slideDown(100);
436						$billingEmailField.slideDown(100);
437
438						$institutionField.find('input, textarea').val('');
439						$billingNameField.find('input, textarea').val('');
440						$billingEmailField.find('input, textarea').val('');
441					} else {
442						$institutionField.find('input').val('N/A').trigger('input').trigger('change');
443						$billingNameField.find('input').val('N/A').trigger('input').trigger('change');
444						$billingEmailField.find('input').val('N/A').trigger('input').trigger('change');
445
446						$institutionField.slideUp(100);
447						$billingNameField.slideUp(100);
448						$billingEmailField.slideUp(100);
449					}
450				}
451				$feesField.on('change', 'input[type="radio"]', toggleCompanyFields);
452				toggleCompanyFields();
453			}
454
455			var feesInterval = setInterval(function () {
456				if ($('#am-cf-17').length) {
457					clearInterval(feesInterval);
458					initFeesCondition();
459				}
460			}, 300);
461		}
462	},
463};
464
465jQuery(function ($) {
466	contentjs.init();
467});

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.