(function (window) { 'use strict'; const IMG_SUFFIX = '_0.'; const STYLE_ATTR = 'style'; const DEFAULT_SELECTORS = { 'img[src*="_0."]': 'src', 'img[srcset*="_0."]': 'srcset', 'source[srcset*="_0."]': 'srcset', '[data-src-rs-ref*="_0."]': 'data-src-rs-ref', '[data-lazyload*="_0."]': 'data-lazyload', '[style*="background"][style*="_0."]': STYLE_ATTR }; const PICTURE_IMG_SELECTOR = 'picture:has(source) img'; const observerOptions = { root: null, rootMargin: '0px', threshold: 0.1 }; let observer = null; let currentConfig = null; let currentSelector = ''; const isCallback = (value) => typeof value === 'function'; const normalizeAttrs = (value) => { if (Array.isArray(value)) return value.slice(); if (value === null || value === undefined) return []; return [value]; }; const cleanPath = (path) => { if (!path) return ''; return path .split('_-s-_').join('/') .split('/spectro_speed/img').join('') .replace(/\/wp-content\/early-cache\/img/g, '') .replace(/_0\./g, '.'); }; const preload = (url, onLoad, onError) => { if (!url) { if (onError) onError(); return; } const tempImage = new Image(); tempImage.onload = onLoad; if (onError) tempImage.onerror = onError; tempImage.src = url; }; const replaceAttribute = (el, attr, onDone) => { const finish = () => { if (typeof onDone === 'function') onDone.call(el); }; if (attr === STYLE_ATTR) { const bg = el.style.backgroundImage || getComputedStyle(el).backgroundImage; const match = bg.match(/url\(["']?(.*?)["']?\)/); if (!match || !match[1] || !match[1].includes(IMG_SUFFIX)) return finish(); const newPath = cleanPath(match[1]); preload(newPath, () => { el.style.backgroundImage = `url("${newPath}")`; finish(); }, finish); return; } const value = el.getAttribute(attr); if (!value || !value.includes(IMG_SUFFIX)) return finish(); const newValue = cleanPath(value); preload(newValue.split(' ')[0], () => { el.setAttribute(attr, newValue); finish(); }, finish); }; const getMatchedAttributes = (el, config) => { const attrs = []; if (!el || typeof el.matches !== 'function') return attrs; Object.keys(config).forEach((selector) => { let matched = false; try { matched = el.matches(selector); } catch (e) { matched = false; } if (!matched) return; normalizeAttrs(config[selector]).forEach((attr) => { if (attr && attrs.indexOf(attr) === -1) attrs.push(attr); }); }); return attrs; }; const processEntries = (el, entries) => { if (!el || !entries || !entries.length) return; const attrs = []; const callbacks = []; entries.forEach((entry) => { if (isCallback(entry)) { if (callbacks.indexOf(entry) === -1) callbacks.push(entry); return; } if (entry && attrs.indexOf(entry) === -1) attrs.push(entry); }); const runCallbacks = () => { callbacks.forEach((callback) => callback.call(el, el)); }; if (!attrs.length) { runCallbacks(); return; } let pending = attrs.length; const onAttrDone = () => { pending -= 1; if (pending <= 0) runCallbacks(); }; attrs.forEach((attr) => replaceAttribute(el, attr, onAttrDone)); }; const processElement = (el, config = currentConfig) => { if (!el) return; processEntries(el, getMatchedAttributes(el, config)); if (el.tagName === 'IMG' && typeof el.closest === 'function') { const picture = el.closest('picture'); if (picture) { picture.querySelectorAll('source').forEach((sourceEl) => { processEntries(sourceEl, getMatchedAttributes(sourceEl, config)); }); } } }; const buildSelector = (config) => { return Object.keys(config).concat(PICTURE_IMG_SELECTOR).join(', '); }; const mergeSelectors = (base, extra) => { const result = {}; const add = (source) => { if (!source || typeof source !== 'object') return; Object.keys(source).forEach((selector) => { const attrs = normalizeAttrs(source[selector]); const merged = result[selector] ? result[selector].slice() : []; attrs.forEach((attr) => { if (attr && merged.indexOf(attr) === -1) merged.push(attr); }); result[selector] = merged; }); }; add(base); add(extra); return result; }; const observeElements = (container = document) => { if (!observer || !currentSelector) return; const elements = container.querySelectorAll(currentSelector); elements.forEach(el => observer.observe(el)); }; const init = (options = {}) => { const extraSelectors = options && typeof options.selectors === 'object' && options.selectors !== null ? options.selectors : options; currentConfig = mergeSelectors(DEFAULT_SELECTORS, extraSelectors); currentSelector = buildSelector(currentConfig); observer = new IntersectionObserver((entries, obs) => { entries.forEach(entry => { if (entry.isIntersecting) { const el = entry.target; processElement(el, currentConfig); obs.unobserve(el); } }); }, observerOptions); observeElements(); const mutationObserver = new MutationObserver((mutations) => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { if (node.matches(currentSelector)) { observer.observe(node); } observeElements(node); } }); }); }); mutationObserver.observe(document.body, { childList: true, subtree: true }); return observer; }; window.SpectroImages = { DEFAULT_SELECTORS, cleanPath, normalizeAttrs, isCallback, preload, replaceAttribute, getMatchedAttributes, processEntries, processElement, buildSelector, mergeSelectors, observeElements, init }; })(window);