PageSourceSearch

https://www.lojaintt.com.br/common/js/utilsPeformance.js?v=1

js lojaintt.com.br collected 2026-09-26 10:09:37 UTC 12,353 bytes, 376 lines download raw bytes

1/** @type {Map<string, any>} Armazena os seletores e seus resultados. */
2const domCache = new Map();
3/** @type {number} Número máximo de entradas no cache antes de limpar automaticamente. */
4const MAX_CACHE_SIZE = 500;
5/** @type {Map<string, IntersectionObserver>} Cache para armazenar observers ativos. */
6const intersectionObserverCache = new Map();
7var utilsSpeed = {};
8
9
10const scopeIds = new WeakMap();
11let scopeIdCounter = 0;
12
13utilsSpeed.getScopeKey = function (scope) {
14  if (scope === document) return 'document';
15  if (!scopeIds.has(scope)) {
16    scopeIds.set(scope, `scope-${++scopeIdCounter}`);
17  }
18  return scopeIds.get(scope);
19}
20
21/**
22 * Busca um elemento no DOM e usa cache para evitar buscas repetidas.
23 *
24 * @param {string} selector - Seletor CSS do elemento a ser buscado.
25 * @param {ParentNode} [scope=document] - Elemento onde será feita a busca (default: document).
26 * @returns {Element|null} - Elemento encontrado ou null.
27 */
28utilsSpeed.getCachedElement = function (selector, scope = document) {
29	const scopeKey = utilsSpeed.getScopeKey(scope);
30	const cacheKey = `${scopeKey}:${selector}`;
31
32  if (domCache.has(cacheKey)) {
33    const cachedEl = domCache.get(cacheKey);
34
35    if (!cachedEl || !document.contains(cachedEl)) {
36      domCache.delete(cacheKey);
37    } else {
38      domCache.delete(cacheKey);
39      domCache.set(cacheKey, cachedEl);
40      return cachedEl;
41    }
42  }
43
44  const foundElement = scope.querySelector(selector);
45  if (!foundElement) return null;
46
47  if (domCache.size >= MAX_CACHE_SIZE) {
48    const firstKey = domCache.keys().next().value;
49    domCache.delete(firstKey);
50  }
51
52  domCache.set(cacheKey, foundElement);
53
54  return foundElement;
55};
56
57/**
58 * Busca vários elementos no DOM com cache para evitar buscas repetidas.
59 *
60 * @param {string} selector - Seletor CSS dos elementos a serem buscados.
61 * @param {ParentNode} [scope=document] - Elemento onde será feita a busca (default: document).
62 * @returns {NodeListOf<Element>} - Lista de elementos encontrados.
63 */
64utilsSpeed.getCachedElements = function (selector, scope = document) {
65  const scopeKey = utilsSpeed.getScopeKey(scope);
66  const cacheKey = `all:${scopeKey}:${selector}`;
67
68  if (domCache.has(cacheKey)) {
69    const nodeList = domCache.get(cacheKey);
70
71    if (nodeList && Array.from(nodeList).every(el => document.contains(el))) {
72      domCache.delete(cacheKey);
73      domCache.set(cacheKey, nodeList);
74      return nodeList;
75    } else {
76      domCache.delete(cacheKey);
77    }
78  }
79
80  const foundElements = scope.querySelectorAll(selector);
81  if (!foundElements.length) return [];
82
83  if (domCache.size >= MAX_CACHE_SIZE) {
84    const firstKey = domCache.keys().next().value;
85    domCache.delete(firstKey);
86  }
87
88  domCache.set(cacheKey, foundElements);
89
90  return foundElements;
91};
92
93
94// Função para verificar se elemento existe antes de usar
95utilsSpeed.safeQuerySelector = function (parent, selector) {
96	return parent ? parent.querySelector(selector) : null;
97}
98
99// Função para verificar se elementos existem antes de usar
100utilsSpeed.safeQuerySelectorAll = function (parent, selector) {
101	return parent ? parent.querySelectorAll(selector) : [];
102}
103
104/**
105 * Executa a função apenas após um tempo de inatividade (útil para resize, input).
106 * @param {Function} fn - Função a ser executada.
107 * @param {number} wait - Tempo de espera em ms.
108 * @returns {Function}
109 */
110utilsSpeed.safeDebounce = function (fn, wait = 100) {
111	let timeout;
112	return function (...args) {
113		clearTimeout(timeout);
114		timeout = setTimeout(() => fn.apply(this, args), wait);
115	};
116}
117
118/**
119 * Executa a função no máximo 1 vez por intervalo definido (útil para scroll, resize).
120 * @param {Function} fn - Função a ser executada.
121 * @param {number} limit - Intervalo mínimo entre execuções em ms.
122 * @returns {Function}
123 */
124utilsSpeed.safeThrottle = function (fn, limit = 100) {
125	let inThrottle = false;
126	return function (...args) {
127		if (!inThrottle) {
128			fn.apply(this, args);
129			inThrottle = true;
130			setTimeout(() => (inThrottle = false), limit);
131		}
132	};
133}
134
135/** 
136 * Cria um IntersectionObserver com opções padrão otimizadas.
137 * Ideal para lazy loading e ativações condicionais.
138 * 
139 * @param {IntersectionObserverCallback} callback - Função executada quando o alvo entra/saí da viewport.
140 * @param {IntersectionObserverInit} [options={}] - Configurações do observer.
141 * @returns {IntersectionObserver}
142 */
143utilsSpeed.createIntersectionObserver = function (callback, options = {}) {
144	const defaultOptions = {
145		root: null,
146		rootMargin: '50px',
147		threshold: 0.1
148	};
149	const finalOptions = { ...defaultOptions, ...options };
150	return new IntersectionObserver(callback, finalOptions);
151}
152
153/**
154 * Limpa e desconecta todos os observers salvos no cache.
155 * Ideal para uso em componentes que são destruídos dinamicamente.
156 */
157utilsSpeed.cleanupIntersectionObservers = function () {
158	intersectionObserverCache.forEach(observer => {
159		if (observer?.disconnect) {
160			observer.disconnect();
161		}
162	});
163	intersectionObserverCache.clear();
164}
165
166/**
167 * Adiciona um listener de scroll otimizado com throttle e listener passivo.
168 * Ajusta o throttle dinamicamente para dispositivos de baixa performance.
169 *
170 * @param {Element} element - Elemento DOM que receberá o listener de scroll.
171 * @param {Function} callback - Função a ser executada no evento scroll.
172 * @param {Object} [options] - Opções para customização.
173 * @param {boolean} [options.passive=true] - Define se o listener será passivo.
174 * @param {number} [options.throttle] - Intervalo em ms para throttle; padrão baseado em dispositivo.
175 *
176 * @returns {Function} Função para remover o listener de scroll adicionado.
177 */
178utilsSpeed.optimizeScrollEvent = function (element, callback, options = {}) {
179	const defaultOptions = {
180		passive: true,
181		throttle: utilsSpeed.isLowEndDevice() ? 150 : 100,
182	};
183	const finalOptions = { ...defaultOptions, ...options };
184	const throttledCallback = utilsSpeed.safeThrottle(callback, finalOptions.throttle);
185
186	element.addEventListener('scroll', throttledCallback, { passive: finalOptions.passive });
187
188	return () => {
189		element.removeEventListener('scroll', throttledCallback);
190	};
191}
192
193// Sistema de fila para atualizações de estilo para evitar reflows forçados
194const styleUpdateQueue = new Map();
195let styleUpdateScheduled = false;
196
197utilsSpeed.queueStyleUpdate = function (element, property, value) {
198  if (!element || !element.isConnected) {
199    return;
200  }
201
202  if (!styleUpdateQueue.has(element)) {
203    styleUpdateQueue.set(element, new Map());
204  }
205
206  const styles = styleUpdateQueue.get(element);
207  styles.set(property, value);
208
209  if (!styleUpdateScheduled) {
210    styleUpdateScheduled = true;
211
212    requestAnimationFrame(() => {
213      styleUpdateQueue.forEach((properties, element) => {
214        if (element && element.isConnected) {
215          properties.forEach((value, property) => {
216            element.style[property] = value;
217          });
218        }
219      });
220
221      styleUpdateQueue.clear();
222      styleUpdateScheduled = false;
223    });
224  }
225};
226
227
228/**
229 * Detecta se o dispositivo é considerado de baixa performance com base em critérios configuráveis.
230 *
231 * Útil para condicionar a execução de scripts pesados, ajustar tamanhos de chunks, desabilitar autoplay, etc.
232 *
233 * @param {Object} [thresholds] - Configurações personalizadas dos critérios.
234 * @param {number} [thresholds.maxCpuCores=2] - Número máximo de núcleos de CPU para considerar como fraco.
235 * @param {number} [thresholds.maxMemory=2] - Quantidade máxima de memória (em GB) para considerar como fraco.
236 * @param {boolean} [thresholds.requireMobile=true] - Se `true`, o dispositivo deve ser mobile para ser considerado fraco.
237 * @param {number} [thresholds.maxScreenWidth=480] - Largura máxima da tela (em pixels) para considerar como fraco.
238 * @param {number} [thresholds.minConditionsMet=3] - Número mínimo de critérios que precisam ser atendidos para considerar o dispositivo fraco.
239 * @returns {boolean} `true` se o dispositivo for considerado de baixa performance, `false` caso contrário.
240 */
241let _cachedLowEnd;
242utilsSpeed.isLowEndDevice = function (thresholds = {}) {
243  if (typeof _cachedLowEnd !== 'undefined') return _cachedLowEnd;
244
245  const DEFAULTS = {
246	maxCpuCores: 2,
247	maxMemory: 2,
248	requireMobile: true,
249	maxScreenWidth: 480,
250	minConditionsMet: 3
251  };
252  const config = { ...DEFAULTS, ...thresholds };
253
254  try {
255	const deviceSpecs = {
256	  cpuCores: navigator.hardwareConcurrency || 2,
257	  memory: navigator.deviceMemory || 1,
258	  isMobile: /android|iphone|ipad|ipod/.test(navigator.userAgent.toLowerCase()),
259	  screenWidth: window.screen.width || 0
260	};
261
262	const metConditions = [
263	  deviceSpecs.cpuCores <= config.maxCpuCores,
264	  deviceSpecs.memory <= config.maxMemory,
265	  !config.requireMobile || deviceSpecs.isMobile,
266	  deviceSpecs.screenWidth <= config.maxScreenWidth
267	].filter(Boolean).length;
268
269	_cachedLowEnd = metConditions >= config.minConditionsMet;
270	return _cachedLowEnd;
271  } catch {
272	_cachedLowEnd = true;
273	return true;
274  }
275}
276
277// Função para otimizar batch size baseado no dispositivo
278utilsSpeed.getOptimizedBatchSize = function () {
279	return utilsSpeed.isLowEndDevice() ? 1 : 2;
280}
281
282// Função para otimizar delay baseado no dispositivo
283utilsSpeed.getOptimizedDelay = function () {
284	return utilsSpeed.isLowEndDevice() ? 50 : 30;
285}
286
287/**
288 * Executa uma lista de tarefas em lotes para evitar long tasks e travamentos na UI.
289 * Usa requestIdleCallback para aproveitar tempo ocioso e requestAnimationFrame como fallback para sincronizar com repaint.
290 * Adapta o tamanho do lote em dispositivos de baixa performance.
291 *
292 * @param {Iterable} items - Elementos ou dados a serem processados (Array, NodeList, Set, etc).
293 * @param {Function} taskFn - Função a ser executada para cada item (recebe item, índice).
294 * @param {number} [batchSize=1] - Número máximo de itens processados por lote.
295 * @param {number} [delay=20] - Tempo de espera usado somente se nenhuma API para agendamento avançado estiver disponível.
296 * @param {boolean} [adaptive=true] - Reduz batchSize automaticamente em dispositivos fracos.
297 */
298utilsSpeed.runTasksInChunks = function (items, taskFn, batchSize = 1, delay = 20, adaptive = true) {
299	const entries = Array.from(items);
300	let index = 0;
301
302	const effectiveBatchSize =
303	adaptive && typeof utilsSpeed.isLowEndDevice === "function" && utilsSpeed.isLowEndDevice()
304		? Math.min(batchSize, 2)
305		: batchSize;
306
307	function processBatch(deadline) {
308		let count = 0;
309
310		while (
311			index < entries.length &&
312			count < effectiveBatchSize &&
313			(!deadline || deadline.timeRemaining() > 0)
314		) {
315			const value = entries[index];
316			taskFn(value, index);
317			index++;
318			count++;
319		}
320
321		if (index < entries.length) {
322			if ("requestIdleCallback" in window) {
323				requestIdleCallback(processBatch, { timeout: 100 });
324			} else if ("requestAnimationFrame" in window) {
325				requestAnimationFrame(() => processBatch());
326			} else {
327				setTimeout(() => processBatch(), delay);
328			}
329		}
330	}
331
332	processBatch();
333}
334
335/**
336 * Reconstrói o conteúdo de um container DOM de forma eficiente,
337 * evitando reflows e reprocessamento do layout.
338 *
339 * @param {Element} container - Elemento onde o conteúdo será refeito.
340 * @param {Iterable<string|Element>} contentList - Lista de strings HTML ou elementos DOM.
341 * @param {Object} [options]
342 * @param {boolean} [options.clear=true] - Se deve limpar o container antes.
343 *
344 * @example
345 * const items = document.querySelectorAll('.item');
346 * const htmlList = Array.from(items).map(i => `<div>${i.outerHTML}</div>`);
347 * replaceContentEfficiently(container, htmlList);
348 */
349utilsSpeed.replaceContentEfficiently = function (container, contentList, options = {}) {
350	if (!container || typeof container.appendChild !== 'function') {
351		return;
352	}
353
354	const { clear = true, compare = false } = options;
355
356	const fragment = document.createDocumentFragment();
357
358	for (const content of contentList) {
359		if (typeof content === 'string') {
360			const template = document.createElement('template');
361			template.innerHTML = content.trim();
362			fragment.appendChild(template.content.cloneNode(true));
363		} else if (content instanceof Element) {
364			fragment.appendChild(content);
365		}
366	}
367
368	if (clear) {
369		if (compare && container.innerHTML.trim() === fragment.textContent.trim()) {
370			return;
371		} 
372		container.innerHTML = '';
373	}
374
375	container.appendChild(fragment);
376}

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.