Files
odoo-expertos.com/performance-optimizer.js
T
2026-08-28 10:11:11 -03:00

274 lines
8.3 KiB
JavaScript

/**
* Performance Optimizer for Odoo Expertos
* Adds lazy loading, caching, and optimizations without breaking existing functionality
*/
// 1. Lazy Load Images
function setupLazyLoading() {
// Create Intersection Observer for lazy loading images
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
// Load the actual image
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute('data-src');
}
// Remove lazy loading class
img.classList.remove('lazy-bg');
// Stop observing this image
observer.unobserve(img);
}
});
}, {
rootMargin: '50px' // Start loading 50px before entering viewport
});
// Observe all images with data-src attribute
document.querySelectorAll('img[data-src]').forEach(img => {
imageObserver.observe(img);
});
}
// 2. Defer D3.js Loading
function loadD3Async() {
// Only load D3 when needed
if (document.querySelector('.visualization, #network-visualization, .hero-visual')) {
const script = document.createElement('script');
script.src = 'https://d3js.org/d3.v7.min.js';
script.async = true;
script.onload = () => {
// Initialize D3 visualizations after loading
if (typeof initializeVisualizations === 'function') {
initializeVisualizations();
}
};
document.head.appendChild(script);
}
}
// 3. Resource Hints for Critical Resources
function addResourceHints() {
const hints = [
{ rel: 'preconnect', href: 'https://d3js.org' },
{ rel: 'preconnect', href: 'https://images.pexels.com' },
{ rel: 'dns-prefetch', href: 'https://fonts.googleapis.com' }
];
hints.forEach(hint => {
const link = document.createElement('link');
link.rel = hint.rel;
link.href = hint.href;
if (hint.rel === 'preconnect') {
link.crossOrigin = '';
}
document.head.appendChild(link);
});
}
// 4. Cache API Responses using localStorage with TTL
class CacheManager {
constructor() {
this.prefix = 'odoo_cache_';
this.defaultTTL = 3600000; // 1 hour in milliseconds
}
set(key, data, ttl = this.defaultTTL) {
const cacheData = {
data: data,
timestamp: Date.now(),
ttl: ttl
};
try {
localStorage.setItem(this.prefix + key, JSON.stringify(cacheData));
} catch (e) {
// Handle quota exceeded
this.clearOldCache();
try {
localStorage.setItem(this.prefix + key, JSON.stringify(cacheData));
} catch (e) {
console.warn('Cache storage full');
}
}
}
get(key) {
const item = localStorage.getItem(this.prefix + key);
if (!item) return null;
try {
const cacheData = JSON.parse(item);
const now = Date.now();
// Check if cache is expired
if (now - cacheData.timestamp > cacheData.ttl) {
localStorage.removeItem(this.prefix + key);
return null;
}
return cacheData.data;
} catch (e) {
return null;
}
}
clearOldCache() {
const now = Date.now();
const keys = Object.keys(localStorage);
keys.forEach(key => {
if (key.startsWith(this.prefix)) {
try {
const item = JSON.parse(localStorage.getItem(key));
if (now - item.timestamp > item.ttl) {
localStorage.removeItem(key);
}
} catch (e) {
localStorage.removeItem(key);
}
}
});
}
}
// 5. Optimize Component Loading
function optimizeComponentLoading() {
// Load header and footer asynchronously
const loadComponent = (scriptSrc, callback) => {
const script = document.createElement('script');
script.src = scriptSrc;
script.async = true;
script.onload = callback;
document.head.appendChild(script);
};
// Load components in parallel
Promise.all([
new Promise(resolve => loadComponent('/components/header.js', resolve)),
new Promise(resolve => loadComponent('/components/footer.js', resolve))
]).then(() => {
// Initialize components after loading
if (typeof createHeader === 'function') {
const headerPlaceholder = document.getElementById('header-placeholder');
if (headerPlaceholder) {
headerPlaceholder.innerHTML = createHeader();
}
}
if (typeof createFooter === 'function') {
const footerPlaceholder = document.getElementById('footer-placeholder');
if (footerPlaceholder) {
footerPlaceholder.innerHTML = createFooter();
}
}
});
}
// 6. Request Idle Callback for Non-Critical Tasks
function deferNonCritical() {
if ('requestIdleCallback' in window) {
requestIdleCallback(() => {
// Load cookie banner
const cookieScript = document.createElement('script');
cookieScript.src = '/js/cookie-banner.js';
cookieScript.async = true;
document.body.appendChild(cookieScript);
// Preload next page resources
preloadNextPage();
});
} else {
// Fallback for browsers without requestIdleCallback
setTimeout(() => {
const cookieScript = document.createElement('script');
cookieScript.src = '/js/cookie-banner.js';
cookieScript.async = true;
document.body.appendChild(cookieScript);
preloadNextPage();
}, 2000);
}
}
// 7. Preload likely next pages
function preloadNextPage() {
// Preload links that are likely to be clicked
const links = document.querySelectorAll('a[href^="/"]');
const preloadedUrls = new Set();
links.forEach(link => {
link.addEventListener('mouseenter', () => {
const href = link.getAttribute('href');
if (!preloadedUrls.has(href)) {
const prefetch = document.createElement('link');
prefetch.rel = 'prefetch';
prefetch.href = href;
document.head.appendChild(prefetch);
preloadedUrls.add(href);
}
}, { once: true });
});
}
// 8. WebP Image Support Detection
function checkWebPSupport(callback) {
const webP = new Image();
webP.onload = webP.onerror = function () {
callback(webP.height === 2);
};
webP.src = 'data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';
}
// 9. Initialize Performance Optimizations
function initializeOptimizations() {
// Add resource hints immediately
addResourceHints();
// Setup lazy loading for images
if ('IntersectionObserver' in window) {
setupLazyLoading();
}
// Load D3.js only when needed
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', loadD3Async);
} else {
loadD3Async();
}
// Defer non-critical resources
deferNonCritical();
// Check WebP support
checkWebPSupport(supported => {
if (supported) {
document.documentElement.classList.add('webp');
}
});
// Initialize cache manager
window.cacheManager = new CacheManager();
// Clear old cache on load
window.cacheManager.clearOldCache();
}
// Start optimizations
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeOptimizations);
} else {
initializeOptimizations();
}
// Export for use in other scripts
window.performanceOptimizer = {
CacheManager,
setupLazyLoading,
loadD3Async,
checkWebPSupport
};