countdown-8.x-1.8/js/integrations/countdown.flipclock.integration.js
js/integrations/countdown.flipclock.integration.js
/**
* @file
* Integration for the FlipClock.js library (supports v0.7.x and v0.10.x).
*
* This file handles initialization of FlipClock instances for both the legacy
* jQuery-based version (0.7.x) and the modern ES6 version (0.10.x).
*/
(function (Drupal) {
'use strict';
/**
* Initialize a FlipClock timer.
*
* @param {Element} element
* The DOM element to initialize as a timer.
* @param {Object} settings
* The settings object from drupalSettings.
*/
function initializeFlipClock(element, settings) {
// Resolve settings using shared utility.
const config = Drupal.countdown.utils.resolveCountdownSettings(element, settings, 'flipclock');
// Determine library version from settings or detection.
const isModernVersion = config.isModernVersion || detectFlipClockVersion();
const libraryVersion = config.libraryVersion || (isModernVersion ? '0.10.8' : '0.7.8');
// Get target date from various possible sources.
const targetDate = config.target_date || element.dataset.countdownTarget;
if (!targetDate) {
Drupal.countdown.utils.handleError(element, 'No target date specified', 'flipclock');
return;
}
// Initialize based on detected version.
if (isModernVersion) {
initializeModernFlipClock(element, config, targetDate);
} else {
initializeLegacyFlipClock(element, config, targetDate);
}
}
/**
* Detect FlipClock version based on available APIs.
*
* @return {boolean}
* True if modern version (0.10.x), false if legacy (0.7.x).
*/
function detectFlipClockVersion() {
// Modern version has FlipClock.Face, legacy doesn't.
return typeof FlipClock !== 'undefined' && typeof FlipClock.Face !== 'undefined';
}
/**
* Initialize modern FlipClock (v0.10.x).
*
* @param {Element} element
* The DOM element.
* @param {Object} settings
* The merged settings.
* @param {string} targetDate
* The target date string.
*/
function initializeModernFlipClock(element, settings, targetDate) {
// Validate library availability.
if (typeof FlipClock === 'undefined') {
Drupal.countdown.utils.handleError(element, 'Modern FlipClock library not loaded', 'flipclock');
return;
}
// Parse target date and calculate time difference.
const now = new Date();
const target = new Date(targetDate);
// Validate target date.
if (isNaN(target.getTime())) {
Drupal.countdown.utils.handleError(element, 'Invalid target date: ' + targetDate, 'flipclock');
return;
}
const diffMs = settings.countdown !== false ? target - now : now - target;
// Check if countdown has already expired.
if (settings.countdown !== false && diffMs <= 0) {
Drupal.countdown.utils.showExpiredMessage(element, settings, 'flipclock');
return;
}
// Get face name from settings (v0.10.x uses 'face' key).
const faceName = settings.face || settings.clockFace || 'DayCounter';
// Build configuration for modern FlipClock.
const config = {
face: faceName,
countdown: settings.countdown !== false,
autoPlay: settings.autoPlay !== false,
autoStart: settings.autoStart !== false
};
// Add language/labels if specified.
if (settings.language) {
config.language = settings.language;
}
if (settings.labels) {
config.labels = settings.labels;
}
// Add minimum digits if specified.
if (settings.minimumDigits !== undefined) {
config.minimumDigits = settings.minimumDigits;
}
try {
// Create FlipClock instance with value directly.
const clock = new FlipClock(element, target, config);
// Store instance for cleanup.
Drupal.countdown.storeInstance(element, {
instance: clock,
stop: function () {
if (clock && clock.stop) {
clock.stop();
}
}
});
// Mark as initialized.
element.classList.add('countdown-initialized');
element.classList.add('flipclock');
// Dispatch initialization event.
Drupal.countdown.utils.dispatchEvent(element, 'initialized', {
library: 'flipclock',
element: element,
settings: settings,
instance: clock
});
// Monitor for completion.
monitorFlipClockCompletion(element, clock, settings);
}
catch (error) {
console.error('Countdown: Failed to create modern FlipClock', error);
Drupal.countdown.utils.handleError(element, 'Failed to create FlipClock: ' + error.message, 'flipclock');
}
}
/**
* Initialize legacy FlipClock (v0.7.x).
*
* @param {Element} element
* The DOM element.
* @param {Object} settings
* The merged settings.
* @param {string} targetDate
* The target date string.
*/
function initializeLegacyFlipClock(element, settings, targetDate) {
// Validate library and jQuery availability.
if (typeof FlipClock === 'undefined' || typeof jQuery === 'undefined') {
console.error('Countdown: Legacy FlipClock or jQuery not loaded.');
element.dispatchEvent(new CustomEvent('countdown:error', {
detail: { message: 'Legacy FlipClock library or jQuery not loaded' }
}));
return;
}
const $ = jQuery;
// Parse dates and calculate difference.
const now = new Date();
const target = new Date(targetDate);
// Validate target date.
if (isNaN(target.getTime())) {
console.error('Countdown: Invalid target date for FlipClock:', targetDate);
element.dispatchEvent(new CustomEvent('countdown:error', {
detail: { message: 'Invalid target date: ' + targetDate }
}));
return;
}
const diffMs = settings.countdown ? target - now : now - target;
const diffSeconds = Math.floor(Math.abs(diffMs) / 1000);
// Check if countdown has already expired.
if (settings.countdown && diffMs <= 0) {
const finishMessage = settings.finish_message ||
settings.finishMessage || "Time's up!";
$(element).html('<div class="flipclock-expired">' +
finishMessage + '</div>');
element.dispatchEvent(new CustomEvent('countdown:complete', {
detail: { element: element, library: 'flipclock' }
}));
return;
}
// Build configuration for legacy FlipClock.
const config = {
clockFace: settings.clockFace || settings.face || 'DayCounter',
countdown: settings.countdown !== false,
autoStart: settings.autoStart !== false,
callbacks: {
stop: function () {
const finishMessage = settings.finish_message ||
settings.finishMessage || "Time's up!";
$(element).find('.flip-clock-wrapper').append(
'<div class="flipclock-finish-message">' + finishMessage + '</div>'
);
element.dispatchEvent(new CustomEvent('countdown:complete', {
detail: { element: element, library: 'flipclock' }
}));
}
}
};
// Add language if specified.
if (settings.language) {
config.language = settings.language;
}
// Add minimum digits if specified.
if (settings.minimumDigits !== undefined) {
config.minimumDigits = settings.minimumDigits;
}
try {
// Create legacy FlipClock instance.
const clock = $(element).FlipClock(diffSeconds, config);
// Store instance for cleanup.
Drupal.countdown.storeInstance(element, {
instance: clock,
stop: function () {
if (clock && clock.stop) {
clock.stop();
}
}
});
// Mark as initialized.
element.classList.add('countdown-initialized');
element.classList.add('flipclock-legacy');
// Dispatch initialization event.
element.dispatchEvent(new CustomEvent('countdown:initialized', {
detail: {
library: 'flipclock',
element: element,
settings: settings,
instance: clock
}
}));
}
catch (error) {
console.error('Countdown: Failed to create legacy FlipClock', error);
element.dispatchEvent(new CustomEvent('countdown:error', {
detail: { message: 'Failed to create FlipClock: ' + error.message }
}));
}
}
/**
* Monitor FlipClock for completion.
*
* @param {Element} element
* The countdown element.
* @param {Object} clock
* The FlipClock instance.
* @param {Object} settings
* The settings object.
*/
function monitorFlipClockCompletion(element, clock, settings) {
// Set up an interval to check for completion.
const checkInterval = setInterval(function () {
let isComplete = false;
try {
// Check different ways based on clock configuration.
if (clock.value !== undefined) {
const value = clock.value;
if (value instanceof Date) {
const now = new Date();
isComplete = settings.countdown !== false ? value <= now : value >= now;
} else if (typeof value === 'number') {
isComplete = settings.countdown !== false ? value <= 0 : value >= 0;
}
}
// Additional check for modern FlipClock versions
if (clock.face && clock.face.value) {
const faceValue = clock.face.value.value;
if (faceValue instanceof Date) {
const now = new Date();
isComplete = settings.countdown !== false ? faceValue <= now : faceValue >= now;
} else if (typeof faceValue === 'number') {
isComplete = settings.countdown !== false ? faceValue <= 0 : faceValue >= 0;
}
}
} catch (e) {
// Silently handle access errors.
}
if (isComplete) {
clearInterval(checkInterval);
// Stop the clock if possible.
if (clock.stop && typeof clock.stop === 'function') {
clock.stop();
}
// Show completion message.
Drupal.countdown.utils.showExpiredMessage(element, settings, 'flipclock');
}
}, 1000);
// Store interval ID for cleanup.
element.dataset.flipclockInterval = checkInterval;
}
// Register loader with countdown system.
Drupal.countdown.registerLoader('flipclock', initializeFlipClock);
})(Drupal);
