PageSourceSearch

https://vell.ai/themes/default/assets/js/frontend.js

js vell.ai collected 2026-09-25 18:35:58 UTC 9,364 bytes, 309 lines download raw bytes

1(() => {
2	'use strict';
3
4	const dropdownMenus = document.querySelectorAll('.dropdown-menu');
5	const mobileNavTrigger = document.querySelector('.mobile-nav-trigger');
6	const siteNavContainer = document.querySelector('.site-nav-container');
7	const templatesShowMore = document.querySelector('.templates-show-more');
8	const filterTriggers = document.querySelectorAll('button[data-target]');
9	const toggleClassnameTriggers = document.querySelectorAll( '[data-lqd-toggle-class]' );
10	const frontendLocalNav = document.querySelector('#frontend-local-navbar');
11	const textRotators = document.querySelectorAll('.lqd-text-rotator');
12	const setAnchors = document.querySelectorAll('[data-set-anchor=true]');
13	let lastActiveTrigger = null;
14	let lastOpenedAccordion = null;
15
16	textRotators?.forEach(textRotator => {
17		const items = textRotator.querySelectorAll('.lqd-text-rotator-item');
18
19		if (!items.length) return;
20
21		const timeout = 2000;
22		let activeIndex = 0;
23
24		textRotator.style.width = `${
25			items[activeIndex].querySelector('span').clientWidth
26		}px`;
27
28		setInterval(() => {
29			// current item
30			items[activeIndex].classList.remove('lqd-is-active');
31
32			// now next item
33			activeIndex =
34				activeIndex === items.length - 1 ? 0 : activeIndex + 1;
35			textRotator.style.width = `${
36				items[activeIndex].querySelector('span').clientWidth
37			}px`;
38			items[activeIndex].classList.add('lqd-is-active');
39		}, timeout);
40	});
41
42	dropdownMenus.forEach(dd => {
43		if (document.body.classList.contains('navbar-shrinked')) {
44			dd.classList.remove('show');
45		}
46	});
47
48	document.addEventListener('click', ev => {
49		const { target } = ev;
50		dropdownMenus.forEach(dd => {
51			if (
52				!document.body.classList.contains('navbar-shrinked') &&
53				dd.closest('.primary-nav')
54			)
55				return;
56			const clickedOutside = !dd.parentElement.contains(target);
57			if (clickedOutside) {
58				dd.classList.remove('show');
59			}
60		});
61	});
62
63	templatesShowMore?.addEventListener('click', ev => {
64		ev.preventDefault();
65		const list = document.querySelector('.templates-cards');
66		const overlay = document.querySelector('.templates-cards-overlay');
67
68		list.style.overflow = 'visible';
69		list.style.maxHeight = 'none';
70
71		overlay.animate([ { opacity: 0 } ], {
72			duration: 650,
73			fill: 'forwards',
74			easing: 'ease-out',
75		});
76		const btnAnima = templatesShowMore.animate([ { opacity: 0 } ], {
77			duration: 650,
78			fill: 'forwards',
79			easing: 'ease-out',
80		});
81		btnAnima.onfinish = () => {
82			overlay.style.visibility = 'hidden';
83			templatesShowMore.style.visibility = 'hidden';
84		};
85	});
86
87	filterTriggers?.forEach(trigger => {
88		const targetId = trigger.getAttribute('data-target');
89		const targets = document.querySelectorAll(targetId);
90		const triggerType =
91			trigger.getAttribute('data-trigger-type') || 'toggle';
92
93		if (targets.length <= 0) {
94			return trigger.setAttribute('disabled', true);
95		}
96
97		trigger.addEventListener('click', ev => {
98			ev?.preventDefault();
99
100			trigger.classList.add('lqd-is-active');
101
102			if (triggerType === 'toggle') {
103				[ ...trigger.parentElement.children ]
104					.filter(c => c.getAttribute('data-target') !== targetId)
105					.forEach(c => c.classList.remove('lqd-is-active'));
106			} else if (triggerType === 'accordion') {
107				if (lastActiveTrigger) {
108					lastActiveTrigger.classList.remove('lqd-is-active');
109				}
110				if (lastActiveTrigger === trigger) {
111					lastActiveTrigger = null;
112				} else {
113					lastActiveTrigger = trigger;
114				}
115			}
116
117			targets?.forEach(t => {
118				t.style.display = 'block';
119				t.animate([ { opacity: 0 }, { opacity: 1 } ], {
120					duration: 650,
121					easing: 'cubic-bezier(.48,.81,.52,.99)',
122				});
123			});
124
125			if (triggerType === 'toggle') {
126				[ ...targets[0]?.parentElement?.children ]
127					?.filter(c =>
128						targetId.startsWith('.')
129							? !c.classList.contains(targetId.replace('.', ''))
130							: c.getAttribute('id') !== targetId.replace('#', '')
131					)
132					?.forEach(c => (c.style.display = 'none'));
133			} else if (triggerType === 'accordion') {
134				if (lastOpenedAccordion) {
135					lastOpenedAccordion.style.display = 'none';
136				}
137				if (lastOpenedAccordion === targets[0]) {
138					lastOpenedAccordion = null;
139				} else {
140					lastOpenedAccordion = targets[0];
141				}
142			}
143		});
144	});
145
146	toggleClassnameTriggers?.forEach(trigger => {
147		const target = trigger.getAttribute('data-lqd-toggle-target');
148		let targetEls = target ? document.querySelectorAll(target) : [];
149
150		trigger.addEventListener('click', ev => {
151			ev?.preventDefault();
152			trigger.classList.toggle('lqd-is-active');
153			targetEls.forEach(t => t.classList.toggle('lqd-is-active'));
154		});
155	});
156
157	if (frontendLocalNav?.querySelector('ul')) {
158		const scrollspy = VanillaScrollspy({ menu: frontendLocalNav.querySelector('ul') });
159		scrollspy.init();
160	}
161
162	mobileNavTrigger?.addEventListener('click', ev => {
163		ev.preventDefault();
164		mobileNavTrigger?.classList.toggle('lqd-is-active');
165		siteNavContainer?.classList.toggle('lqd-is-active');
166	});
167
168	siteNavContainer?.querySelectorAll('a')?.forEach(link => {
169		link.addEventListener('click', ev => {
170			mobileNavTrigger?.classList.remove('lqd-is-active');
171			siteNavContainer?.classList.remove('lqd-is-active');
172		});
173	});
174
175	document.addEventListener('click', ev => {
176		const clickedEl = ev.target;
177		const clipboardCopyButton = clickedEl.closest('.lqd-clipboard-copy');
178
179		// Close mobile nav when clicked outside
180		if (
181			siteNavContainer?.classList.contains('lqd-is-active') &&
182			!siteNavContainer?.contains(clickedEl) &&
183			!siteNavContainer !== clickedEl &&
184			!mobileNavTrigger?.contains(clickedEl) &&
185			!mobileNavTrigger !== clickedEl
186		) {
187			mobileNavTrigger?.classList.remove('lqd-is-active');
188			siteNavContainer?.classList.remove('lqd-is-active');
189		}
190
191		// Copy to clipboard
192		if (clipboardCopyButton) {
193			const settings = JSON.parse(
194				clipboardCopyButton.getAttribute('data-copy-options') || '{}'
195			);
196			let getContentFrom;
197
198			if (settings.contentIn) {
199				if (settings.contentIn.startsWith('<') && settings.content) {
200					const el = clipboardCopyButton.parentElement.closest(
201						settings.contentIn.replace('<', '')
202					);
203					getContentFrom = el.querySelector(settings.content);
204				}
205			} else {
206				getContentFrom = settings.content
207					? document.querySelector(settings.content)
208					: clipboardCopyButton.parentElement;
209			}
210
211			if (getContentFrom) {
212				const copyButtonsTemplate = document.querySelector('#copy-btns-template');
213				const wrapper = clipboardCopyButton.closest('.lqd-clipboard-copy-wrap');
214				let textContent = extractTextWithLinks(getContentFrom);
215
216				wrapper?.classList?.toggle('active');
217
218				copyToClipboard(textContent, 'text');
219
220				if ( wrapper && copyButtonsTemplate && !clipboardCopyButton.classList.contains('copy-buttons-appended') ) {
221					const copyButtons = copyButtonsTemplate.content.cloneNode(true);
222					const buttons = copyButtons.querySelectorAll('button');
223
224					wrapper.appendChild(copyButtons);
225					clipboardCopyButton.classList.add('copy-buttons-appended');
226
227					buttons.forEach(button => {
228						button.addEventListener('click', () => {
229							const copyType = button.getAttribute('data-copy-type');
230
231							if ( copyType === 'text' ) {
232								copyToClipboard(textContent, 'text');
233							} else if ( copyType === 'html' ) {
234								const unwrappedContent = getContentFrom.innerHTML
235									.replace(/<span[^>]*class="[^"]*animated-word[^"]*"[^>]*>(.*?)<\/span>/g, '$1')
236									.replace('[DONE]', '');
237								copyToClipboard(unwrappedContent, 'html');
238							} else if ( copyType === 'md' ) {
239								if ( typeof TurndownService !== 'undefined' ) {
240									const turndownService = new TurndownService();
241									const content = getContentFrom.innerHTML.replace('[DONE]', '');
242									copyToClipboard(turndownService.turndown(getContentFrom.innerHTML), 'md');
243								}
244							}
245						});
246					});
247				}
248			}
249		}
250	});
251
252	function copyToClipboard(content, contentType = 'text') {
253		if (typeof toastr === 'undefined') {
254			return;
255		}
256
257		navigator.clipboard.writeText(content).then(() => {
258			toastr.success(
259				magicai_localize && magicai_localize[`${contentType}_content_copied_to_clipboard`] ?
260					magicai_localize[`${contentType}_content_copied_to_clipboard`] :
261					'Content copied to clipboard' );
262		}).catch(err => {
263			toastr.error( magicai_localize?.copy_failed || 'Failed to copy content' );
264		});
265	}
266
267	function extractTextWithLinks(element) {
268		let result = '';
269		const walker = document.createTreeWalker(
270			element,
271			NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT,
272			null,
273			false
274		);
275
276		let node;
277		while ((node = walker.nextNode())) {
278			if (node.nodeType === Node.TEXT_NODE) {
279				result += node.nodeValue;
280			} else if (node.nodeType === Node.ELEMENT_NODE) {
281				const nodeStyles = window.getComputedStyle(node);
282
283				if (node.nodeName === 'A') {
284					result += `(${node.href})`;
285				} else if (node.nodeName === 'BR') {
286					result += '\n\n';
287				} else if (
288					nodeStyles.display === 'block' ||
289					nodeStyles.display === 'list-item'
290				) {
291					result += '\n';
292				}
293			}
294		}
295		return result.trim();
296	}
297
298	setAnchors.forEach(el => {
299		const elRect = el.getBoundingClientRect();
300		const isRtl = document.documentElement.getAttribute('dir') === 'rtl';
301
302		if (
303			(!isRtl && elRect.right > window.innerWidth) ||
304			(isRtl && elRect.left < 0)
305		) {
306			el.classList.add('anchor-end');
307		}
308	});
309})();

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.