PageSourceSearch

https://www.ncwebcrafters.com/wp-content/plugins/ncwc-blocks-core/assets/js/frontend.js?ver=1783877577

js ncwebcrafters.com collected 2026-10-08 00:44:39 UTC 10,153 bytes, 339 lines download raw bytes

1
2(function () {
3	var prefersReducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
4
5	function parseNumber(value) {
6		var clean = String(value || '0').replace(/,/g, '').match(/-?\d+(\.\d+)?/);
7		return clean ? parseFloat(clean[0]) : 0;
8	}
9	function decimals(value) {
10		var match = String(value || '').match(/\.(\d+)/);
11		return match ? match[1].length : 0;
12	}
13	function formatNumber(value, places) {
14		return value.toLocaleString(undefined, { minimumFractionDigits: places, maximumFractionDigits: places });
15	}
16	function animateCounter(card) {
17		if (card.dataset.counted === 'true') return;
18		var valueEl = card.querySelector('.ncwc-stat-value');
19		if (!valueEl) return;
20		var rawTarget = card.getAttribute('data-target') || valueEl.textContent || '0';
21		var target = parseNumber(rawTarget);
22		var places = decimals(rawTarget);
23		var duration = Math.max(parseInt(card.getAttribute('data-duration') || '1200', 10), 1);
24		var startTime = null;
25		card.dataset.counted = 'true';
26
27		if (prefersReducedMotion) {
28			valueEl.textContent = formatNumber(target, places);
29			return;
30		}
31
32		function tick(timestamp) {
33			if (!startTime) startTime = timestamp;
34			var progress = Math.min((timestamp - startTime) / duration, 1);
35			var eased = 1 - Math.pow(1 - progress, 3);
36			valueEl.textContent = formatNumber(target * eased, places);
37			if (progress < 1) requestAnimationFrame(tick);
38			else valueEl.textContent = formatNumber(target, places);
39		}
40		requestAnimationFrame(tick);
41	}
42	function initCounters() {
43		var counters = document.querySelectorAll('.ncwc-stat-card.has-counter[data-counter="true"]');
44		if (!counters.length) return;
45		if (!('IntersectionObserver' in window)) {
46			counters.forEach(animateCounter);
47			return;
48		}
49		var observer = new IntersectionObserver(function (entries) {
50			entries.forEach(function (entry) {
51				if (entry.isIntersecting) {
52					animateCounter(entry.target);
53					observer.unobserve(entry.target);
54				}
55			});
56		}, { threshold: 0.35 });
57		counters.forEach(function (counter) { observer.observe(counter); });
58	}
59	if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', initCounters);
60	else initCounters();
61})();
62
63(function () {
64	var faqSections = document.querySelectorAll('.ncwc-faq-section[data-accordion="true"]');
65
66	if (!faqSections.length) return;
67
68	faqSections.forEach(function (section) {
69		section.addEventListener('toggle', function (event) {
70			var current = event.target;
71
72			if (!current || current.tagName !== 'DETAILS' || !current.open) return;
73			if (!current.classList.contains('ncwc-faq-item')) return;
74
75			Array.prototype.forEach.call(section.querySelectorAll('.ncwc-faq-item[open]'), function (item) {
76				if (item !== current) item.open = false;
77			});
78		}, true);
79	});
80})();
81
82(function () {
83	var compareBlocks = document.querySelectorAll('.ncwc-before-after-block');
84
85	if (!compareBlocks.length) return;
86
87	compareBlocks.forEach(function (block) {
88		var range = block.querySelector('.ncwc-ba-range');
89
90		if (!range) return;
91
92		function clamp(value) {
93			var number = parseFloat(value);
94
95			if (Number.isNaN(number)) number = 50;
96
97			return Math.max(0, Math.min(100, number));
98		}
99
100		function update(value) {
101			var position = clamp(value);
102
103			block.style.setProperty('--ncwc-ba-position', position + '%');
104			range.value = position;
105		}
106
107		range.addEventListener('input', function () {
108			update(range.value);
109		});
110
111		range.addEventListener('pointerdown', function () {
112			block.classList.add('is-dragging');
113		});
114
115		range.addEventListener('pointerup', function () {
116			block.classList.remove('is-dragging');
117		});
118
119		range.addEventListener('pointercancel', function () {
120			block.classList.remove('is-dragging');
121		});
122
123		range.addEventListener('keydown', function () {
124			window.requestAnimationFrame(function () { update(range.value); });
125		});
126
127		update(range.value || 50);
128	});
129})();
130
131(function () {
132	var reviewBlocks = document.querySelectorAll('.ncwc-google-reviews-block');
133
134	if (!reviewBlocks.length) return;
135
136	var prefersReducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
137
138	function initReviewBlock(block) {
139		var scroller = block.querySelector('.ncwc-google-reviews-track');
140		var prevButton = block.querySelector('.ncwc-google-reviews-control--prev');
141		var nextButton = block.querySelector('.ncwc-google-reviews-control--next');
142		var dragEnabled = block.classList.contains('has-drag');
143		var equalHeight = block.classList.contains('has-equal-height');
144		var autoScroll = block.getAttribute('data-auto-scroll') === 'true';
145		var autoSpeed = Math.max(parseInt(block.getAttribute('data-auto-scroll-speed') || '6', 10), 2) * 1000;
146		var autoTimer = null;
147
148		if (!scroller) return;
149
150		function getCards() {
151			return Array.prototype.slice.call(scroller.querySelectorAll('.ncwc-google-review-card'));
152		}
153
154		function equalizeCardHeights() {
155			var cards = getCards();
156
157			scroller.style.removeProperty('--ncwc-google-review-card-height');
158
159			if (!equalHeight || !cards.length) return;
160
161			var tallestCard = cards.reduce(function (maxHeight, card) {
162				return Math.max(maxHeight, card.offsetHeight);
163			}, 0);
164
165			if (tallestCard) {
166				scroller.style.setProperty('--ncwc-google-review-card-height', tallestCard + 'px');
167			}
168		}
169
170		function getPositions() {
171			var cards = getCards();
172			var firstOffset = cards[0] ? cards[0].offsetLeft : 0;
173
174			return cards.map(function (card) {
175				return card.offsetLeft - firstOffset;
176			});
177		}
178
179		function getCurrentIndex() {
180			var positions = getPositions();
181
182			if (!positions.length) return 0;
183
184			return positions.reduce(function (closestIndex, position, index) {
185				var closestDistance = Math.abs(positions[closestIndex] - scroller.scrollLeft);
186				var currentDistance = Math.abs(position - scroller.scrollLeft);
187
188				return currentDistance < closestDistance ? index : closestIndex;
189			}, 0);
190		}
191
192		function scrollToIndex(index) {
193			var positions = getPositions();
194
195			if (!positions.length) return;
196
197			var targetIndex = Math.max(0, Math.min(positions.length - 1, index));
198
199			scroller.scrollTo({
200				left: positions[targetIndex],
201				behavior: prefersReducedMotion ? 'auto' : 'smooth'
202			});
203		}
204
205		function updateControls() {
206			var maxScroll = Math.max(0, scroller.scrollWidth - scroller.clientWidth - 2);
207			var hasOverflow = maxScroll > 2;
208
209			if (!prevButton || !nextButton) return;
210
211			prevButton.hidden = !hasOverflow;
212			nextButton.hidden = !hasOverflow;
213			prevButton.disabled = !hasOverflow || scroller.scrollLeft <= 2;
214			nextButton.disabled = !hasOverflow || scroller.scrollLeft >= maxScroll;
215		}
216
217		function startAutoScroll() {
218			if (!autoScroll || prefersReducedMotion || autoTimer) return;
219
220			autoTimer = window.setInterval(function () {
221				var currentIndex = getCurrentIndex();
222				var nextIndex = currentIndex + 1;
223
224				if (nextIndex >= getPositions().length) nextIndex = 0;
225
226				scrollToIndex(nextIndex);
227			}, autoSpeed);
228		}
229
230		function stopAutoScroll() {
231			if (!autoTimer) return;
232
233			window.clearInterval(autoTimer);
234			autoTimer = null;
235		}
236
237		if (prevButton) prevButton.addEventListener('click', function () { scrollToIndex(getCurrentIndex() - 1); });
238		if (nextButton) nextButton.addEventListener('click', function () { scrollToIndex(getCurrentIndex() + 1); });
239
240		Array.prototype.slice.call(block.querySelectorAll('[data-google-review-toggle]')).forEac
240h(function (button) {
241			button.addEventListener('click', function () {
242				var card = button.closest('.ncwc-google-review-card');
243				var isExpanded;
244
245				if (!card) return;
246
247				isExpanded = card.classList.toggle('is-review-expanded');
248				button.setAttribute('aria-expanded', isExpanded ? 'true' : 'false');
249				button.textContent = isExpanded ? 'Show less' : 'Read full review';
250
251				equalizeCardHeights();
252				updateControls();
253			});
254		});
255
256		if (dragEnabled) {
257			var isPointerDown = false;
258			var hasDragged = false;
259			var pointerStartX = 0;
260			var scrollStartX = 0;
261
262			scroller.addEventListener('pointerdown', function (event) {
263				if (event.button !== 0 || event.target.closest('button, a, input, textarea, select, label')) return;
264
265				isPointerDown = true;
266				hasDragged = false;
267				pointerStartX = event.clientX;
268				scrollStartX = scroller.scrollLeft;
269				scroller.classList.add('is-dragging');
270				scroller.setPointerCapture(event.pointerId);
271				stopAutoScroll();
272			});
273
274			scroller.addEventListener('pointermove', function (event) {
275				var deltaX;
276
277				if (!isPointerDown) return;
278
279				deltaX = event.clientX - pointerStartX;
280
281				if (Math.abs(deltaX) > 4) hasDragged = true;
282
283				scroller.scrollLeft = scrollStartX - deltaX;
284			});
285
286			function finishDrag(event) {
287				if (!isPointerDown) return;
288
289				isPointerDown = false;
290				scroller.classList.remove('is-dragging');
291
292				if (scroller.hasPointerCapture(event.pointerId)) {
293					scroller.releasePointerCapture(event.pointerId);
294				}
295
296				if (hasDragged) scrollToIndex(getCurrentIndex());
297
298				startAutoScroll();
299			}
300
301			scroller.addEventListener('pointerup', finishDrag);
302			scroller.addEventListener('pointercancel', finishDrag);
303		}
304
305		scroller.addEventListener('scroll', function () { window.requestAnimationFrame(updateControls); }, { passive: true });
306		block.addEventListener('mouseenter', stopAutoScroll);
307		block.addEventListener('mouseleave', startAutoScroll);
308		block.addEventListener('focusin', stopAutoScroll);
309		block.addEventListener('focusout', startAutoScroll);
310		window.addEventListener('resize', function () {
311			equalizeCardHeights();
312			updateControls();
313		});
314
315		window.requestAnimationFrame(function () {
316			equalizeCardHeights();
317			updateControls();
318			startAutoScroll();
319		});
320	}
321
322	if (!('IntersectionObserver' in window)) {
323		reviewBlocks.forEach(initReviewBlock);
324		return;
325	}
326
327	var reviewObserver = new IntersectionObserver(function (entries) {
328		entries.forEach(function (entry) {
329			if (!entry.isIntersecting) return;
330
331			reviewObserver.unobserve(entry.target);
332			initReviewBlock(entry.target);
333		});
334	}, { rootMargin: '800px 0px' });
335
336	reviewBlocks.forEach(function (block) {
337		reviewObserver.observe(block);
338	});
339})();

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.