Enhance theme loading by tracking animation frames, MutationObservers, and intervals; wrap JS in IIFE for scope isolation
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 37s
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 37s
This commit is contained in:
@@ -370,9 +370,69 @@
|
|||||||
console.log('[Test Site] Theme cleared.');
|
console.log('[Test Site] Theme cleared.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track active animation frames and observers for cleanup
|
||||||
|
let activeAnimationFrames = [];
|
||||||
|
let activeBlobUrls = [];
|
||||||
|
|
||||||
|
// Patch requestAnimationFrame and MutationObserver to track them
|
||||||
|
const origRAF = window.requestAnimationFrame;
|
||||||
|
const origCAF = window.cancelAnimationFrame;
|
||||||
|
let trackingEnabled = false;
|
||||||
|
|
||||||
|
window.requestAnimationFrame = function(cb) {
|
||||||
|
const id = origRAF.call(window, cb);
|
||||||
|
if (trackingEnabled) activeAnimationFrames.push(id);
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
|
||||||
|
window.cancelAnimationFrame = function(id) {
|
||||||
|
origCAF.call(window, id);
|
||||||
|
activeAnimationFrames = activeAnimationFrames.filter(f => f !== id);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Track MutationObservers created by themes
|
||||||
|
let activeObservers = [];
|
||||||
|
const OrigMO = window.MutationObserver;
|
||||||
|
window.MutationObserver = class extends OrigMO {
|
||||||
|
constructor(cb) {
|
||||||
|
super(cb);
|
||||||
|
if (trackingEnabled) activeObservers.push(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Track intervals created by themes
|
||||||
|
let activeIntervals = [];
|
||||||
|
const origSetInterval = window.setInterval;
|
||||||
|
const origClearInterval = window.clearInterval;
|
||||||
|
window.setInterval = function(...args) {
|
||||||
|
const id = origSetInterval.apply(window, args);
|
||||||
|
if (trackingEnabled) activeIntervals.push(id);
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
window.clearInterval = function(id) {
|
||||||
|
origClearInterval.call(window, id);
|
||||||
|
activeIntervals = activeIntervals.filter(i => i !== id);
|
||||||
|
};
|
||||||
|
|
||||||
function loadTheme() {
|
function loadTheme() {
|
||||||
clearTheme();
|
clearTheme();
|
||||||
|
|
||||||
|
// Cancel all tracked animation frames
|
||||||
|
activeAnimationFrames.forEach(id => origCAF.call(window, id));
|
||||||
|
activeAnimationFrames = [];
|
||||||
|
|
||||||
|
// Disconnect all tracked MutationObservers
|
||||||
|
activeObservers.forEach(obs => obs.disconnect());
|
||||||
|
activeObservers = [];
|
||||||
|
|
||||||
|
// Clear all tracked intervals
|
||||||
|
activeIntervals.forEach(id => origClearInterval.call(window, id));
|
||||||
|
activeIntervals = [];
|
||||||
|
|
||||||
|
// Revoke old blob URLs
|
||||||
|
activeBlobUrls.forEach(url => URL.revokeObjectURL(url));
|
||||||
|
activeBlobUrls = [];
|
||||||
|
|
||||||
const value = select.value;
|
const value = select.value;
|
||||||
if (!value || value === '') return;
|
if (!value || value === '') return;
|
||||||
|
|
||||||
@@ -403,16 +463,31 @@
|
|||||||
document.head.appendChild(link);
|
document.head.appendChild(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inject JS (after a short delay to let CSS load)
|
// Inject JS wrapped in IIFE to avoid const redeclaration errors
|
||||||
if (jsFile) {
|
if (jsFile) {
|
||||||
setTimeout(() => {
|
setTimeout(async () => {
|
||||||
const script = document.createElement('script');
|
try {
|
||||||
script.src = jsFile;
|
const response = await fetch(jsFile);
|
||||||
script.setAttribute('data-seasonal', 'true');
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||||
script.onerror = () => console.error(`[Test Site] Failed to load JS: ${jsFile}`);
|
const code = await response.text();
|
||||||
document.body.appendChild(script);
|
|
||||||
console.log(`[Test Site] Loaded theme: ${value} (${jsFile})`);
|
// Wrap in IIFE so each theme has its own scope
|
||||||
}, 100);
|
const wrappedCode = `(function() {\n${code}\n})();`;
|
||||||
|
const blob = new Blob([wrappedCode], { type: 'application/javascript' });
|
||||||
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
|
activeBlobUrls.push(blobUrl);
|
||||||
|
|
||||||
|
trackingEnabled = true;
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = blobUrl;
|
||||||
|
script.setAttribute('data-seasonal', 'true');
|
||||||
|
script.onerror = () => console.error(`[Test Site] Failed to execute JS: ${jsFile}`);
|
||||||
|
document.body.appendChild(script);
|
||||||
|
console.log(`[Test Site] Loaded theme: ${value} (${jsFile}) [IIFE-wrapped]`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[Test Site] Failed to load JS: ${jsFile}`, err);
|
||||||
|
}
|
||||||
|
}, 150);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user