countdown-8.x-1.8/js/integrations/countdown.core.integration.js
js/integrations/countdown.core.integration.js
/**
* @file
* Integration for the core CountdownTimer library.
*
* This file handles initialization and management of the built-in
* CountdownTimer instances that ship with the module.
*/
(function (Drupal) {
'use strict';
/**
* Initialize a core countdown timer.
*
* @param {Element} element
* The DOM element to initialize as a timer.
* @param {Object} settings
* The settings object from drupalSettings.
*/
function initializeCoreTimer(element, settings) {
// Validate library availability.
if (typeof CountdownTimer === 'undefined') {
Drupal.countdown.utils.handleError(element, 'Core timer library not loaded', 'countdown');
return;
}
// Resolve settings using shared utility.
const config = Drupal.countdown.utils.resolveCountdownSettings(element, settings, 'countdown');
// Verify this is for the core timer.
if (config.library && config.library !== 'countdown') {
return;
}
// Ensure element has an ID for the selector option.
if (!element.id) {
element.id = 'countdown-' + Math.random().toString(36).substr(2, 9);
}
// Build configuration object for CountdownTimer.
const timerConfig = {
selector: '#' + element.id,
mode: config.mode || 'countdown',
precision: config.precision || 'seconds',
driftCompensation: config.driftCompensation !== undefined ? config.driftCompensation : true,
autoStart: config.autoStart !== undefined ? config.autoStart : true,
debug: config.debug || false,
benchmark: config.benchmark || false
};
// Add style configuration if provided.
if (config.style) {
timerConfig.style = config.style;
}
// Handle custom format template properly.
// Check for both formatTemplate and useCustomFormat flag.
if (config.formatTemplate && config.formatTemplate.length > 0) {
timerConfig.formatTemplate = config.formatTemplate;
// Override precision if custom format is used.
// This ensures the timer uses the custom format.
timerConfig.useCustomFormat = true;
}
// Add start value if provided (already in milliseconds from PHP).
if (config.start !== undefined) {
timerConfig.start = config.start;
}
// Add target value for countup mode or countdown target.
if (config.target_date !== undefined) {
timerConfig.target = config.target_date;
}
// Add offset if specified.
if (config.offset) {
timerConfig.offset = parseInt(config.offset, 10);
}
// Configure the onComplete callback.
timerConfig.onComplete = function (timeObj, timerInstance) {
Drupal.countdown.utils.showExpiredMessage(element, config, 'countdown');
};
// Configure the onTick callback.
timerConfig.onTick = function (timeObj, timerInstance) {
// Dispatch tick event for other scripts to listen to.
Drupal.countdown.utils.dispatchEvent(element, 'tick', {
element: element,
library: 'countdown',
timeObj: timeObj
});
};
// Configure the onStart callback.
timerConfig.onStart = function (timeObj, timerInstance) {
// Mark as initialized and dispatch event.
element.classList.add('countdown-initialized');
Drupal.countdown.utils.dispatchEvent(element, 'initialized', {
element: element,
library: 'countdown'
});
};
try {
// Create the timer instance.
const timer = new CountdownTimer(timerConfig);
// Apply custom format override to the timer's _tick method.
// This ensures formatTemplate is used when rendering.
if (timerConfig.formatTemplate && timerConfig.useCustomFormat) {
// Store the original _tick method.
const originalTick = timer._tick.bind(timer);
// Override the _tick method to use formatTemplate.
timer._tick = function() {
// Ensure element is available.
if (timer.config.selector && !timer._element) {
timer._element = document.querySelector(timer.config.selector);
}
// Calculate elapsed time.
const elapsed = timer.getElapsed();
const effectiveMs = timer.config.mode === 'countdown'
? Math.max(0, timer.state.targetMs - elapsed)
: elapsed;
// Check for completion.
if (timer._checkCompletion(effectiveMs)) return;
// Format time with the custom template.
const formattedTime = timer.formatTime(timer.config.formatTemplate);
const timeComponents = timer.getTime();
// Handle style rendering if needed.
if (timer.config.style && CountdownTimer._styles && CountdownTimer._styles.has(timer.config.style)) {
if (!timer._styleRenderer) {
const StyleClass = CountdownTimer._styles.get(timer.config.style);
timer._styleRenderer = new StyleClass(timer, timer.config.styleOptions);
}
if (timer._element && timer._styleRenderer.render) {
timer._styleRenderer.render(formattedTime, timeComponents);
}
} else {
// Default text rendering with custom format.
if (timer._element) {
timer._element.textContent = formattedTime;
}
}
// Debug output if enabled.
if (timer.config.debug) {
console.log('[CountdownTimer]', formattedTime, timeComponents);
}
// Emit tick event.
timer._emit('tick', timeComponents);
if (timer.config.onTick) {
timer.config.onTick(timeComponents, timer);
}
};
}
// Store instance for cleanup.
Drupal.countdown.storeInstance(element, {
instance: timer,
stop: function () {
if (timer && timer.stop) {
timer.stop();
}
}
});
// Start the timer if autoStart is false.
// (The timer auto-starts by default if autoStart is true.)
if (!timerConfig.autoStart && timer.start) {
timer.start();
}
// Debug output if enabled.
if (config.debug) {
console.log('Core countdown initialized:', {
element: element,
config: timerConfig,
settings: config
});
}
}
catch (error) {
console.error('Countdown: Failed to create core timer', error);
Drupal.countdown.utils.handleError(element, 'Failed to create timer: ' + error.message, 'countdown');
}
}
// Register loader with countdown system.
Drupal.countdown.registerLoader('countdown', initializeCoreTimer);
})(Drupal);
