PageSourceSearch

https://vargohub.com/wp-content/themes/vargo/assets/js/main.js?ver=1.8.23

js vargohub.com collected 2026-09-24 11:07:43 UTC 12,805 bytes, 286 lines download raw bytes

1(function () {
2	'use strict';
3
4	var reduceMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
5
6	document.addEventListener('DOMContentLoaded', function () {
7
8		/* ---- Scroll progress bar ------------------------------------- */
9		var bar = document.createElement('div');
10		bar.className = 'vargo-progress';
11		document.body.appendChild(bar);
12		var onProgress = function () {
13			var d = document.documentElement;
14			var max = d.scrollHeight - d.clientHeight;
15			bar.style.transform = 'scaleX(' + (max > 0 ? d.scrollTop / max : 0) + ')';
16		};
17		onProgress();
18		window.addEventListener('scroll', onProgress, { passive: true });
19
20		/* ---- Mobile drawer ----------------------------------------- */
21		var menuTrigger = document.getElementById('vargo-menu-trigger');
22		var menuOverlay = document.getElementById('vargo-menu-overlay');
23		var menuClose = document.getElementById('vargo-menu-close');
24		var menuBackdrop = document.getElementById('vargo-menu-backdrop');
25		function openMenu() {
26			menuOverlay.hidden = false;
27			if (menuBackdrop) menuBackdrop.hidden = false;
28			requestAnimationFrame(function () {
29				menuOverlay.classList.add('is-open');
30				if (menuBackdrop) menuBackdrop.classList.add('is-open');
31			});
32			menuTrigger.setAttribute('aria-expanded', 'true');
33			document.body.classList.add('vargo-menu-is-open');
34		}
35		function closeMenu() {
36			menuOverlay.classList.remove('is-open');
37			if (menuBackdrop) menuBackdrop.classList.remove('is-open');
38			menuTrigger.setAttribute('aria-expanded', 'false');
39			document.body.classList.remove('vargo-menu-is-open');
40			setTimeout(function () {
41				menuOverlay.hidden = true;
42				if (menuBackdrop) menuBackdrop.hidden = true;
43			}, 400);
44		}
45		if (menuTrigger && menuOverlay) {
46			menuTrigger.addEventListener('click', function () {
47				menuOverlay.classList.contains('is-open') ? closeMenu() : openMenu();
48			});
49			if (menuClose) menuClose.addEventListener('click', closeMenu);
50			if (menuBackdrop) menuBackdrop.addEventListener('click', closeMenu);
51			menuOverlay.querySelectorAll('.vargo-menu-overlay__list a').forEach(function (l) {
52				l.addEventListener('click', closeMenu);
53			});
54			document.addEventListener('keydown', function (e) {
55				if (e.key === 'Escape' && menuOverlay.classList.contains('is-open')) closeMenu();
56			});
57		}
58
59		/* ---- Header shadow on scroll ------------------------------ */
60		var header = document.getElementById('masthead');
61		if (header) {
62			var hs = function () { header.classList.toggle('is-scrolled', window.scrollY > 12); };
63			hs();
64			window.addEventListener('scroll', hs, { passive: true });
65		}
66
67		/* ---- Chat triggers -------------------------------------- */
68		document.addEventListener('click', function (e) {
69			var t = e.target && e.target.closest && e.target.closest('.vargo-open-chat, #vargo-open-chat-btn');
70			if (!t) return;
71			var homeChat = document.getElementById('vargo-agent-chat');
72			if (homeChat) {
73				e.preventDefault();
74				homeChat.scrollIntoView({ behavior: 'smooth', block: 'center' });
75				if (window.vargoAgentFocusChat) setTimeout(window.vargoAgentFocusChat, 350);
76			} else if (window.vargoAgentOpen) {
77				e.preventDefault();
78				window.vargoAgentOpen();
79			}
80		});
81
82		/* ---- Anchor scrolling with sticky-header offset --------
83		   Works for same-page links AND for landing on /page/#section
84		   from another page (menu items). scrollIntoView / native hash
85		   jumps ignore the sticky header, so we compute the offset. */
86		var headerEl = document.getElementById('masthead');
87		var scrollToEl = function (el, smooth) {
88			if (!el) return;
89			var offset = (headerEl ? headerEl.getBoundingClientRect().height : 72) + 18;
90			var y = el.getBoundingClientRect().top + window.pageYOffset - offset;
91			window.scrollTo({ top: Math.max(0, y), behavior: smooth ? 'smooth' : 'auto' });
92		};
93		var elForHash = function (hash) {
94			if (!hash || hash.length < 2) return null;
95			try { return document.querySelector(hash); } catch (e) { return null; }
96		};
97
98		// After clicking an item inside a hover mega-menu, force it closed
99		// (otherwise, since we preventDefault the navigation, the pointer is
100		// still over it / focus is retained and it stays open on the same page).
101		var dismissDropdown = function (link) {
102			var item = link.closest('.vargo-nav__item');
103			if (!item) return;
104			item.classList.add('is-dismissed');
105			try { link.blur(); } catch (e) {}
106			if (document.activeElement && document.activeElement.blur) document.activeElement.blur();
107			var reopen = function () { item.classList.remove('is-dismissed');
107 item.removeEventListener('mouseleave', reopen); };
108			item.addEventListener('mouseleave', reopen);
109			setTimeout(reopen, 2500);
110		};
111
112		document.querySelectorAll('a[href*="#"]').forEach(function (link) {
113			link.addEventListener('click', function (e) {
114				var raw = link.getAttribute('href') || '';
115				if (raw.indexOf('#') === -1) return;
116				var u;
117				try { u = new URL(raw, location.href); } catch (err) { return; }
118				var hash = u.hash;
119				if (!hash || hash === '#') return;
120				var inDropdown = !!link.closest('.vargo-dropdown');
121				// same page only when origin + path match exactly
122				var samePage = u.origin === location.origin && u.pathname === location.pathname;
123				if (!samePage) {
124					if (inDropdown) dismissDropdown(link); // will close on navigate anyway; harmless
125					return;
126				}
127				// Tab links: let the tabs' own hashchange handler switch + scroll.
128				if (/^#tab-/.test(hash)) {
129					e.preventDefault();
130					if (location.hash === hash) {
131						window.dispatchEvent(new Event('hashchange'));
132					} else {
133						location.hash = hash;
134					}
135					if (inDropdown) dismissDropdown(link);
136					return;
137				}
138				var el = elForHash(hash);
139				if (el) {
140					e.preventDefault();
141					if (history.replaceState) { history.replaceState(null, '', hash); }
142					scrollToEl(el, true);
143					if (inDropdown) dismissDropdown(link);
144				}
145			});
146		});
147
148		// Landed with a hash (e.g. from the menu): re-align after layout settles
149		// — Elementor content + images shift the page height after the browser's
150		// initial jump, which is why "it stays at the top".
151		if (location.hash && location.hash.length > 1) {
152			var target = elForHash(location.hash);
153			if (target) {
154				var realign = function () { scrollToEl(elForHash(location.hash), false); };
155				requestAnimationFrame(realign);
156				setTimeout(realign, 250);
157				setTimeout(realign, 700);
158				window.addEventListener('load', function () { setTimeout(realign, 120); });
159			}
160		}
161
162		/* Logo carousel (client list) and the stat counters are now real
163		 * Elementor widgets ("Image Carousel" / "Counter") — their autoplay
164		 * and count-up animation are handled by Elementor's own frontend.js,
165		 * no custom code needed here any more. */
166
167		/* ---- Pricing monthly / annual toggle ----------------- */
168		var toggle = document.querySelector('.vargo-price-toggle');
169		var sw = toggle && toggle.querySelector('.vargo-sw, .sw, button');
170		if (toggle && sw) {
171			if (!sw.querySelector('.vargo-sw__dot')) {
172				var dot = document.createElement('span');
173				dot.className = 'vargo-sw__dot';
174				sw.appendChild(dot);
175			}
176			var labels = toggle.querySelectorAll('span[data-t]');
177			var apply = function (annual) {
178				toggle.classList.toggle('is-annual', annual);
179				sw.setAttribute('aria-checked', annual ? 'true' : 'false');
180				labels.forEach(function (s) { s.classList.toggle('on', s.dataset.t === (annual ? 'a' : 'm')); });
181				document.querySelectorAll('.vargo-plan__price[data-m]').forEach(function (p) {
182					p.querySelector('.amt').textContent = annual ? p.dataset.a : p.dataset.m;
183					var per = p.querySelector('.per');
184					if (per) per.textContent = annual ? (p.dataset.perA || '/ año') : (p.dataset.perM || '/ mes');
185				});
186				document.querySelectorAll('.vargo-plan__bill[data-bill-m]').forEach(function (b) {
187					b.textContent = annual ? b.dataset.billA : b.dataset.billM;
188				});
189			};
190			sw.addEventListener('click', function () { apply(!toggle.classList.contains('is-annual')); });
191			apply(false);
192		}
193
194		/* ---- Tabs (Plataforma) — native Elementor "Tabs" widget, kept
195		 * deep-linkable via #tab-<slug> (mega-menu + home bento links to it).
196		 * Order here must match the $groups order in vargo_demo_page_plataforma().
197		 * Elementor renders a desktop AND a mobile title per tab (both share
198		 * the .elementor-tab-title class + a 1-indexed data-tab), so matching
199		 * is done by data-tab value, picking whichever copy is visible. */
200		var VARGO_TAB_SLUGS = ['ventas', 'operacion', 'finanzas', 'comunicacion', 'automatizacion'];
201		document.querySelectorAll('.vargo-tabs').forEach(function (tabs) {
202			var allTitles = Array.prototype.slice.call(tabs.querySelectorAll('.elementor-tab-title[data-tab]'));
203			if (!allTitles.length) return;
204
205			allTitles.forEach(function (t) {
206				t.addEventListener('click', function () {
207					var slug = VARGO_TAB_SLUGS[parseInt(t.getAttribute('data-tab'), 10) - 1];
208					if (slug && history.replaceState) history.replaceState(null, '', '#tab-' + slug);
209				});
210			});
211
212			var fromHash = function (scroll) {
213				var m = (location.hash || '').match(/^#tab-([a-z-]+)$/);
214				if (!m) return;
215				var idx = VARGO_TAB_SLUGS.indexOf(m[1]);
216				if (idx < 0) return;
217				var candidates = allTitles.filter(function (t) { return t.getAttribute('data-tab') === String(idx + 1); });
218				var target = candidates.filter(function (t) { return t.offsetParent !== null; })[0] || candidates[0];
219				if (target && !target.classList.contains('elementor-active')) target.click();
220				if (scroll) tabs.scrollIntoView({ behavior: 'smooth', block: 'center' });
221			};
222			// Elementor's own tabs.js binds its click handler after its frontend
223			// init cycle, which may run after this script — retry the initial
224			// hash check a few times instead of a single early call.
225			fromHash(true);
226			[60, 200, 500, 1000].forEach(function (ms) { setTimeout(function () { fromHash(true); }, ms); });
227			window.addEventListener('hashchange', function () { fromHash(true); });
228		});
229
230		/* ---- Blog: copy-link share button ------------------- */
231		document.querySelectorAll('.vargo-share__copy').forEach(function (btn) {
232			btn.addEventListener('click', function () {
233				var wrap = btn.closest('.vargo-share');
234				var url = (wrap && wrap.getAttribute('data-url')) || location.href;
235				var done = function () {
236					btn.classList.add('is-done');
237					setTimeout(function () { btn.classList.remove('is-done'); }, 1800);
238				};
239				if (navigator.clipboard && navigator.clipboard.writeText) {
240					navigator.clipboard.writeText(url).then(done, done);
241				} else {
242					var t = document.createElement('textarea');
243					t.value = url; document.body.appendChild(t); t.select();
244					try { document.execCommand('copy'); } catch (e) {}
245					document.body.removeChild(t); done();
246				}
247			});
248		});
249
250		/* ---- Sticky sub-nav scroll-spy ---------------------- */
251		var subnav = document.querySelector('.vargo-subnav');
252		if (subnav && 'IntersectionObserver' in window) {
253			var links = Array.prototype.slice.call(subnav.querySelectorAll('a'));
254			var targets = links.map(function (a) { return document.querySelector(a.getAttribute('href')); }).filter(Boolean);
255			var so = new IntersectionObserver(function (es) {
256				es.forEach(function (e) {
257					if (e.isIntersecting) {
258						links.forEach(function (l) { l.classList.remove('is-active'); });
259						var m = links.filter(function (l) { return l.getAttribute('href') === '#' + e.target.id; })[0];
260						if (m) m.classList.add('is-active');
261					}
262				});
263			}, { rootMargin: '-45% 0px -50% 0px' });
264			targets.forEach(function (t) { so.observe(t); });
265		}
266
267		/* ---- Scroll-reveal (progressive, with failsafe) ----- */
268		if (!reduceMotion && 'IntersectionObserver' in window) {
269			var rt = Array.prototype.slice.call(document.querySelectorAll(
270				'.elementor-widget-icon-box, .elementor-widget-image-box, .elementor-widget-testimonial, .elementor-accordion, .elementor-widget-counter, .vargo-post-card, .vargo-contact-form-wrap, .vargo-hero__media, .vargo-split__media .elementor-widget-image, .vargo-plan__card, .vargo-tabs__card'
271			));
272			var reveal = function (n) { n.classList.add('is-visible'); };
273			var ro = new IntersectionObserver(function (es) {
274				es.forEach(function (e) { if (e.isIntersecting) { reveal(e.target); ro.unobserve(e.target); } });
275			}, { threshold: 0.08, rootMargin: '0px 0px -40px 0px' });
276			var vh = window.innerHeight || 800;
277			rt.forEach(function (n, i) {
278				if (n.getBoundingClientRect().top < vh - 40) { n.classList.add('vargo-reveal', 'is-visible'); return; }
279				n.classList.add('vargo-reveal');
280				n.style.transitionDelay = (i % 3) * 70 + 'ms';
281				ro.observe(n);
282			});
283			setTimeout(function () { rt.forEach(reveal); }, 2600);
284		}
285	});
286})();

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.