PageSourceSearch

https://tecidoskite.com.br/wp-content/themes/tecidoskite/assets/js…?ver=e7e10faf67b54c017dd06f033c6fbca0

js tecidoskite.com.br collected 2026-10-05 03:41:41 UTC 10,031 bytes, 269 lines download raw bytes

1/**
2 * main.js — Utilitários globais
3 * - prefers-reduced-motion flag no <html>
4 * - IntersectionObserver para seções da home (showcase, blog, categorias, instagram)
5 * - Header scroll via header.js / footer via footer.js (não duplicar aqui)
6 */
7
8( function () {
9	'use strict';
10
11	// ─── prefers-reduced-motion ───────────────────────────────────
12	if ( window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches ) {
13		document.documentElement.setAttribute( 'data-reduced-motion', 'true' );
14	}
15
16	// ─── Scroll reveal genérico ───────────────────────────────────
17	// Adiciona .is-visible quando o elemento entra na viewport.
18	// Header, footer e seus filhos têm observers próprios (header.js / footer.js).
19	const targets = [
20		{ selector: '.showcase',                   threshold: 0.15 },
21		{ selector: '.blog-section',               threshold: 0.15 },
22		{ selector: '.categories-section',         threshold: 0.15 },
23		{ selector: '.highlight',                  threshold: 0.15 },
24		{ selector: '.instagram-section',          threshold: 0.15 },
25		{ selector: '.video-institucional',        threshold: 0.15 },
26		{ selector: '.video-block',                threshold: 0.15 },
27		{ selector: '.archive__hero',              threshold: 0.15 },
28		{ selector: '.archive__content',           threshold: 0.10 },
29		{ selector: '.blog-single__hero',          threshold: 0.15 },
30		{ selector: '.blog-single__featured-img',  threshold: 0.15 },
31		{ selector: '.blog-single__article',       threshold: 0.10 },
32		{ selector: '.blog-single__related',       threshold: 0.15 },
33		{ selector: '.page-content',               threshold: 0.10 },
34		{ selector: '.header-hero',                threshold: 0.15 },
35		{ selector: '.produtos-grid',              threshold: 0 },
36		{ selector: '.produto-detail',             threshold: 0 },
37		{ selector: '.produtos-relacionados',      threshold: 0 },
38		{ selector: '.pillars__quote-wrap',        threshold: 0.15 },
39		{ selector: '.pillars__card',              threshold: 0.15 },
40		{ selector: '.material-card',              threshold: 0.12 },
41		{ selector: '.collection-card',            threshold: 0.12 },
42	];
43
44	targets.forEach( function ( item ) {
45		const els = document.querySelectorAll( item.selector );
46		if ( ! els.length ) return;
47
48		const observer = new IntersectionObserver(
49			function ( entries ) {
50				entries.forEach( function ( entry ) {
51					if ( entry.isIntersecting ) {
52						entry.target.classList.add( 'is-visible' );
53						observer.unobserve( entry.target );
54					}
55				} );
56			},
57			{ threshold: item.threshold }
58		);
59
60		els.forEach( function ( el ) { observer.observe( el ); } );
61	} );
62
63	// ─── Máscara de telefone — (99) 99999-9999 ───────────────────
64	function applyPhoneMask( input ) {
65		input.addEventListener( 'input', function () {
66			var digits = this.value.replace( /\D/g, '' ).slice( 0, 11 );
67			var masked = '';
68
69			if ( digits.length === 0 ) {
70				masked = '';
71			} else if ( digits.length <= 2 ) {
72				masked = '(' + digits;
73			} else if ( digits.length <= 7 ) {
74				masked = '(' + digits.slice( 0, 2 ) + ') ' + digits.slice( 2 );
75			} else {
76				masked = '(' + digits.slice( 0, 2 ) + ') ' + digits.slice( 2, 7 ) + '-' + digits.slice( 7 );
77			}
78
79			this.value = masked;
80		} );
81	}
82
83	document.querySelectorAll( 'input[type="tel"]' ).forEach( applyPhoneMask );
84
85	// ─── Máscara CPF — 999.999.999-99 ───────────────────────────
86	function applyCpfMask( input ) {
87		input.setAttribute( 'maxlength', '14' );
88		input.addEventListener( 'input', function () {
89			var d = this.value.replace( /\D/g, '' ).slice( 0, 11 );
90			var v = '';
91			if ( d.length <= 3 ) {
92				v = d;
93			} else if ( d.length <= 6 ) {
94				v = d.slice( 0, 3 ) + '.' + d.slice( 3 );
95			} else if ( d.length <= 9 ) {
96				v = d.slice( 0, 3 ) + '.' + d.slice( 3, 6 ) + '.' + d.slice( 6 );
97			} else {
98				v = d.slice( 0, 3 ) + '.' + d.slice( 3, 6 ) + '.' + d.slice( 6, 9 ) + '-' + d.slice( 9 );
99			}
100			this.value = v;
101		} );
102	}
103
104	// ─── Máscara CNPJ — 99.999.999/9999-99 ──────────────────────
105	function applyCnpjMask( input ) {
106		input.setAttribute( 'maxlength', '18' );
107		input.addEventListener( 'input', function () {
108			var d = this.value.replace( /\D/g, '' ).slice( 0, 14 );
109			var v = '';
110			if ( d.length <= 2 ) {
111				v = d;
112			} else if ( d.length <= 5 ) {
113				v = d.slice( 0, 2 ) + '.' + d.slice( 2 );
114			} else if ( d.length <= 8 ) {
115				v = d.slice( 0, 2 ) + '.' + d.slice( 2, 5 ) + '.' + d.slice( 5 );
116			} else if ( d.length <= 12 ) {
117				v = d.slice( 0, 2 ) + '.' + d.slice( 2, 5 ) + '.' + d.slice( 5, 8 ) + '/' + d.slice( 8 );
118			} else {
119				v = d.slice( 0, 2 ) + '.' + d.slice( 2, 5 ) + '.' + d.slice( 5, 8 ) + '/' + d.slice( 8, 12 ) + '-' + d.slice( 12 );
120			}
121			this.value = v;
122		} );
123	}
124
125	var SEL_CPF  = 'input[name="cpf"], input[name="CPF"], input[name*="_cpf"], input[data-mask="cpf"]';
126	var SEL_CNPJ = 'input[name="cnpj"], input[name="CNPJ"], input[name*="_cnpj"], input[data-mask="cnpj"]';
127
128	document.querySelectorAll( SEL_CPF  ).forEach( applyCpfMask  );
129	document.querySelectorAll( SEL_CNPJ ).forEach( applyCnpjMask );
130
131	// Aplica em inputs adicionados dinamicamente (ex: formulários carregados via AJAX)
132	var telObserver = new MutationObserver( function ( mutations ) {
133		mutations.forEach( function ( mutation ) {
134			mutation.addedNodes.forEach( function ( node ) {
135				if ( node.nodeType !== 1 ) return;
136
137				var tels = node.matches( 'input[type="tel"]' )
138					? [ node ]
139					: Array.from( node.querySelectorAll( 'input[type="tel"]' ) );
140				tels.forEach( applyPhoneMask );
141
142				var cpfs = node.matches( SEL_CPF )
143					? [ node ]
144					: Array.from( node.querySelectorAll( SEL_CPF ) );
145				cpfs.forEach( applyCpfMask );
146
147				var cnpjs = node.matches( SEL_CNPJ )
148					? [ node ]
149					: Array.from( node.querySelectorAll( SEL_CNPJ ) );
150				cnpjs.forEach( applyCnpjMask );
151			} );
152		} );
153	} );
154	telObserver.observe( document.body, { childList: true, subtree: true } );
155
156	// ─── Scroll suave no link "Ver mais" do hero de categoria ────
157	document.querySelectorAll( '.term-desc-excerpt__link[href^="#"]' ).forEach( function ( link ) {
158		link.addEventListener( 'click', function ( e ) {
159			var target = document.querySelector( link.getAttribute( 'href' ) );
160			if ( ! target ) return;
161			e.preventDefault();
162			target.scrollIntoView( { behavior: 'smooth', block: 'start' } );
163		} );
164	} );
165
166	// ─── Conteúdo expandível do produto ──────────────────────────
167	( function () {
168		var bodies = document.querySelectorAll( '.product-content-body' );
169
170		bodies.forEach( function ( body ) {
171			var inner = body.querySelector( '.product-content-body__inner' );
172			var btn   = body.querySelector( '[data-action="toggle"]' );
173			if ( ! inner || ! btn ) return;
174
175			// Se o conteúdo cabe sem rolar, remove a UI de expansão
176			if ( inner.scrollHeight <= inner.clientHeight + 4 ) {
177				inner.style.maxHeight = 'none';
178				var grad = inner.querySelector( '.product-content-body__gradient' );
179				if ( grad ) grad.remove();
180				var toggle = body.querySelector( '.product-content-body__toggle' );
181				if ( toggle ) toggle.remove();
182				return;
183			}
184
185			var labelMore = btn.dataset.labelMore || 'Ver mais ↓';
186			var labelLess = btn.dataset.labelLess || 'Ver menos ↑';
187
188			btn.addEventListener( 'click', function () {
189				var expanded = inner.classList.toggle( 'is-expanded' );
190				if ( expanded ) {
191					inner.style.setProperty( '--full-h', inner.scrollHeight + 'px' );
192					btn.textContent = labelLess;
193				} else {
194					btn.textContent = labelMore;
195					body.scrollIntoView( { behavior: 'smooth', block: 'start' } );
196				}
197			} );
198		} );
199	} )();
200
201	// ─── Índice automático da página (.page-sidebar__nav) ────────
202	// Varre os h2 dentro de .page-body, atribui IDs e monta o <ol>.
203	// Também destaca o item ativo conforme o scroll (IntersectionObserver).
204	( function () {
205		const nav  = document.querySelector( '.page-sidebar__nav' );
206		const body = document.querySelector( '.page-body' );
207		if ( ! nav || ! body ) return;
208
209		const headings = Array.from( body.querySelectorAll( 'h2' ) );
210		if ( ! headings.length ) {
211			nav.closest( '.page-sidebar' )?.remove();
212			return;
213		}
214
215		// Gera slug único a partir do texto
216		const slugify = ( text ) =>
217			text.toLowerCase()
218				.normalize( 'NFD' ).replace( /[\u0300-\u036f]/g, '' )
219				.replace( /[^a-z0-9]+/g, '-' )
220				.replace( /^-|-$/g, '' );
221
222		const used = new Set();
223		const pairs = headings.map( ( h ) => {
224			if ( ! h.id ) {
225				let slug = slugify( h.textContent );
226				let candidate = slug;
227				let i = 2;
228				while ( used.has( candidate ) ) candidate = slug + '-' + i++;
229				used.add( candidate );
230				h.id = candidate;
231			}
232			return { heading: h, id: h.id, label: h.textContent.trim() };
233		} );
234
235		// Constrói o <ol>
236		const ol = document.createElement( 'ol' );
237		pairs.forEach( ( { id, label } ) => {
238			const li = document.createElement( 'li' );
239			const a  = document.createElement( 'a' );
240			a.href        = '#' + id;
241			a.textContent = label;
242			a.dataset.toc = id;
243			li.appendChild( a );
244			ol.appendChild( li );
245		} );
246		nav.innerHTML = '';
247		nav.appendChild( ol );
248
249		// Destaque do item ativo via IntersectionObserver
250		const links = nav.querySelectorAll( 'a[data-toc]' );
251		const setActive = ( id ) => {
252			links.forEach( ( a ) => {
253				a.classList.toggle( 'is-active', a.dataset.toc === id );
254			} );
255		};
256
257		const io = new IntersectionObserver(
258			( entries ) => {
259				entries.forEach( ( entry ) => {
260					if ( entry.isIntersecting ) setActive( entry.target.id );
261				} );
262			},
263			{ rootMargin: '-20% 0px -70% 0px', threshold: 0 }
264		);
265
266		pairs.forEach( ( { heading } ) => io.observe( heading ) );
267	} )();
268
269} )();

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.