new site
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
/**
|
||||
* 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
|
||||
};
|
||||
Reference in New Issue
Block a user