countdown-8.x-1.8/js/countdown-preview-cleanup.js
js/countdown-preview-cleanup.js
/**
* @file
* Handles cleanup of countdown preview instances.
*
* Ensures proper disposal of countdown instances to prevent memory leaks.
*/
(function (Drupal, once) {
'use strict';
// Track active countdown instances.
var activeInstances = new WeakMap();
/**
* Clean up countdown instances before AJAX updates.
*/
Drupal.behaviors.countdownPreviewCleanup = {
attach: function (context) {
// Initialize cleanup tracking for new elements.
once('countdown-cleanup-init', '.countdown-preview-container', context)
.forEach(function (container) {
// Mark container as tracked.
container.setAttribute('data-cleanup-tracked', 'true');
});
},
detach: function (context, settings, trigger) {
// Only cleanup on unload or before AJAX refresh.
if (trigger === 'unload' || trigger === 'ajax') {
var containers = context.querySelectorAll('.countdown-preview-container[data-cleanup-tracked]');
containers.forEach(function (container) {
cleanupContainer(container);
});
}
}
};
/**
* Clean up all countdown instances in a container.
*
* @param {HTMLElement} container
* The container element to clean up.
*/
function cleanupContainer(container) {
console.log('Cleaning up countdown instance for:', container);
// Find all countdown elements within the container.
var countdowns = container.querySelectorAll('[data-countdown-instance]');
countdowns.forEach(function (element) {
cleanupInstance(element);
});
// Clear the container's tracked state.
container.removeAttribute('data-cleanup-tracked');
// Remove any stored references.
if (activeInstances.has(container)) {
activeInstances.delete(container);
}
}
/**
* Clean up a specific countdown instance.
*
* @param {HTMLElement} element
* The countdown element to clean up.
*/
function cleanupInstance(element) {
// Clean up Tick instances.
if (element.tickInstance && typeof element.tickInstance.destroy === 'function') {
element.tickInstance.destroy();
delete element.tickInstance;
}
// Clean up FlipClock instances.
if (element.flipClock && typeof element.flipClock.stop === 'function') {
element.flipClock.stop();
delete element.flipClock;
}
// Clean up FlipDown instances.
if (element.flipDown) {
delete element.flipDown;
}
// Clean up Flip instances.
if (element.flipInstance) {
delete element.flipInstance;
}
// Clean up any timers.
if (element.countdownTimer) {
clearInterval(element.countdownTimer);
delete element.countdownTimer;
}
// Remove instance marker.
element.removeAttribute('data-countdown-instance');
}
/**
* Register an instance for tracking.
*
* @param {HTMLElement} container
* The container element.
* @param {Object} instance
* The countdown instance.
*/
window.registerCountdownInstance = function (container, instance) {
if (!activeInstances.has(container)) {
activeInstances.set(container, []);
}
var instances = activeInstances.get(container);
instances.push(instance);
};
/**
* Clean up before form submissions to prevent recursion.
*/
document.addEventListener('submit', function (event) {
var form = event.target;
// Only clean up for AJAX forms.
if (form.classList.contains('countdown-settings-form')) {
var containers = form.querySelectorAll('.countdown-preview-container');
containers.forEach(function (container) {
cleanupContainer(container);
});
}
});
})(Drupal, once);
