Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/**
* Script: CountdownScroller
* Author: Marisa1980
* Description: Create a dynamic countdown clock with scrolling effect
* Other: This script can not run on mobile site
**/
// IMPORT CSS
importArticle({
type: 'style',
article: 'u:dev:MediaWiki:CountdownScroller.css',
});
(function() {
// PAD A NUMBER WITH A LEADING ZERO TO A LENGTH OF 2
function pad2(n) { return String(n).padStart(2, '0'); }
// PAD A NUMBER WITH A LEADING ZERO TO A LENGTH OF 3
function pad3(n) { return String(n).padStart(3, '0'); }
// CREATE DIGIT RANGE
var DIGIT_RANGES = {
d3: 10, d2: 10, d1: 10, // days
h2: 3, // hour2
h1: 24, // hour1, with the set of 24 digits 0-9, 0-9, 0-3
m2: 6, m1: 10, // minutes
s2: 6, s1: 10, // seconds
};
// DEFINE CUSTOM SEQUENCE FOR H1 STRIP SET
var H1_SEQUENCE = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3];
// LOGIC FOR CREATING AND CONTROLLING DIGIT SCROLLER BEHAVIOR
var DigitScroller = function(container, digitId, type) {
var range = DIGIT_RANGES[digitId] || 10;
var isGoUp = type === 'go-up';
var isH1 = digitId === 'h1';
var isTransitioning = false;
var digitHeight = 0;
var numDigits = range;
var currentValue = 0;
// Create the digit strip content
var createSet = function() {
var set = [];
for (var i = 0; i < range; i++) {
var digit = document.createElement('div');
var displayValue;
if (isH1) {
displayValue = isGoUp ? H1_SEQUENCE[range - 1 - i] : H1_SEQUENCE[i];
} else {
displayValue = isGoUp ? (range - 1 - i) : i;
}
digit.textContent = displayValue;
set.push(digit);
}
return set;
};
var primarySet = createSet();
var cloneSet = primarySet.map(function(d) {
return d.cloneNode(true);
});
// Build the strip
var strip = document.createElement('div');
strip.classList.add('countdown-scroller__digit-strip');
if (isGoUp) { // go-up (A then B)
primarySet.forEach(function(d) {
strip.appendChild(d);
});
cloneSet.forEach(function(d) {
strip.appendChild(d);
});
} else { // go-down (B then A)
cloneSet.forEach(function(d) {
strip.appendChild(d);
});
primarySet.forEach(function(d) {
strip.appendChild(d);
});
}
container.appendChild(strip);
// Repositioning logic
var calculateAndApplyPosition = function(value, animate) {
// Recalculate height if the font size has changed
digitHeight = container.clientHeight || 48;
var targetTranslateY;
var resetTranslateY;
var useRollOverAnimation = false;
// Standard movement calculation (Target the new index in the Primary Set A) ---
var indexInA;
if (isH1) {
var targetHourIndex = value;
if (isGoUp) {
// go-up index: Index 0 is value 23, Index 23 is value 0.
indexInA = range - 1 - targetHourIndex;
} else {
// go-down index: Index 24 is value 0, Index 47 is value 23.
indexInA = numDigits + targetHourIndex;
}
} else { // Original logic for all other digits
if (isGoUp) {
indexInA = numDigits - 1 - value;
} else {
indexInA = numDigits + value;
}
}
targetTranslateY = -indexInA * digitHeight;
// Check for rollover
var prevValue = currentValue;
// H1 rollover logic: Hour index changes from 23 to 0 (or 0 to 23 for go-up).
if (isH1 && animate && (prevValue === range - 1 && value === 0 || prevValue === 0 && value === range - 1)) {
if (!isGoUp) {
// GO DOWN (23 -> 0): Animate DOWN from Index 24 to Index 23, then SNAP to Index 47.
if (prevValue === range - 1 && value === 0) {
var targetIndexInB = 0;
targetTranslateY = -targetIndexInB * digitHeight;
// Reset position is Index 24
var resetIndex = numDigits + value;
resetTranslateY = -resetIndex * digitHeight;
useRollOverAnimation = true;
} else if (prevValue === 0 && value === range - 1) {
// GO DOWN (0 -> 23)
// Animation target: h=23 in Clone B (Index 23)
var targetIndexInB = numDigits - 1; // 23
targetTranslateY = -targetIndexInB * digitHeight;
// Reset target: h=23 in Primary A (Index 47)
var resetIndex = numDigits + range - 1; // 47
resetTranslateY = -resetIndex * digitHeight;
useRollOverAnimation = true;
}
} else {
// GO UP (0 -> 23): Animate UP from Index 23 to Index 24, then SNAP to Index 0.
var targetIndexInB = numDigits; // 24
targetTranslateY = -targetIndexInB * digitHeight;
var resetIndex = 0;
resetTranslateY = -resetIndex * digitHeight;
useRollOverAnimation = true;
}
} else if (!isH1 && animate && prevValue === 0 && value === numDigits - 1) { // Original non-H1 rollover
if (!isGoUp) {
var targetIndexInB = numDigits - 1;
targetTranslateY = -targetIndexInB * digitHeight;
resetTranslateY = -indexInA * digitHeight;
useRollOverAnimation = true;
} else {
var targetIndexInB = numDigits;
targetTranslateY = -targetIndexInB * digitHeight;
resetTranslateY = -indexInA * digitHeight;
useRollOverAnimation = true;
}
}
// Apply the scroll behavior
if (animate) {
var duration = getComputedStyle(strip).getPropertyValue('--countdown-scroller-duration') || '0.3s';
strip.style.transition = 'transform ' + duration + ' ease-in-out';
} else {
strip.style.transition = 'none';
}
strip.style.transform = 'translateY(' + targetTranslateY + 'px)';
currentValue = value;
container.setAttribute('data-current-value', value);
// Handle seamless loop reset
if (animate && useRollOverAnimation) {
isTransitioning = true;
strip.addEventListener('transitionend', function resetPosition(e) {
if (e.target !== strip) return;
strip.removeEventListener('transitionend', resetPosition);
requestAnimationFrame(function() {
// Instant jump back to the primary set
strip.style.transition = 'none';
strip.style.transform = 'translateY(' + resetTranslateY + 'px)';
isTransitioning = false;
// Re-enable transition for the next tick
setTimeout(function() {
var duration = getComputedStyle(strip).getPropertyValue('--countdown-scroller-duration') || '0.3s';
strip.style.transition = 'transform ' + duration + ' ease-in-out';
}, 50);
});
}, { once: true });
}
if (!animate && !useRollOverAnimation) {
setTimeout(function() {
var duration = getComputedStyle(strip).getPropertyValue('--countdown-scroller-duration') || '0.3s';
strip.style.transition = 'transform ' + duration + ' ease-in-out';
}, 50);
}
};
// Set the new digit value
var setValue = function(value, animate, force) {
if (animate === undefined) animate = true;
if (digitHeight === 0) {
digitHeight = container.clientHeight || (container.firstChild ? container.firstChild.offsetHeight : 0);
}
var prevValue = currentValue;
// Only return if the value is the same supposed to animate
if (!force && value === prevValue && animate && !isTransitioning && digitHeight > 0) return;
// Do not interrupt an active loop transition
if (!force && isTransitioning) return;
if (force) isTransitioning = false;
calculateAndApplyPosition(value, animate);
};
var recalculateDigitHeight = function() {
calculateAndApplyPosition(currentValue, false);
};
// Initial setup
currentValue = null;
return {
setValue: setValue,
recalculateDigitHeight: recalculateDigitHeight,
strip: strip // Expose strip for opacity control
};
};
// CREATE TIME BLOCK
function createTimeBlock(name, digitIds) {
var block = document.createElement("div");
block.className = "countdown-scroller__time-block " + name;
block.setAttribute('data-unit', name);
digitIds.forEach(function(id) {
var container = document.createElement("div");
container.className = "countdown-scroller__digit";
container.setAttribute('data-digit-position', id);
block.appendChild(container);
});
return block;
}
// BUILD INITIAL COUNTDOWN STRUCTURE
function buildCountdownSkeleton(container, type) {
container.innerHTML = '';
var options = container.dataset.countdownOptions || '';
var useTextLabelDay = options.includes('time-text-day');
var useTextLabelHms = options.includes('time-text-hms');
var targetDateAttr = container.dataset.countdown;
var diff = Math.abs(new Date(targetDateAttr).getTime() - new Date().getTime());
var totalDays = Math.floor(diff / (1000 * 60 * 60 * 24));
var dayDigitCount = Math.max(3, String(totalDays).length); // Mimimun of 3 for day digit
var dayIds = [];
for (var i = dayDigitCount; i >= 1; i--) {
dayIds.push("d" + i);
}
var dayBlock = createTimeBlock("day", dayIds);
var hourBlock = createTimeBlock("hour", ["h2", "h1"]);
var minuteBlock = createTimeBlock("minute", ["m2", "m1"]);
var secondBlock = createTimeBlock("second", ["s2", "s1"]);
var daySeparator = document.createElement("span");
daySeparator.className = "countdown-scroller__separator day-separator";
if (useTextLabelDay) daySeparator.classList.add('countdown-scroller__label');
var hourSeparator = document.createElement("span");
hourSeparator.className = "countdown-scroller__separator hour-separator";
if (useTextLabelHms) hourSeparator.classList.add('countdown-scroller__label');
var minuteSeparator = document.createElement("span");
minuteSeparator.className = "countdown-scroller__separator minute-separator";
if (useTextLabelHms) minuteSeparator.classList.add('countdown-scroller__label');
var secondSeparator = document.createElement("span");
secondSeparator.className = "countdown-scroller__separator second-separator";
if (useTextLabelHms) secondSeparator.classList.add('countdown-scroller__label');
container.appendChild(dayBlock);
container.appendChild(daySeparator);
container.appendChild(hourBlock);
container.appendChild(hourSeparator);
container.appendChild(minuteBlock);
container.appendChild(minuteSeparator);
container.appendChild(secondBlock);
container.appendChild(secondSeparator);
}
// Global array to store all scroller behavior for accessing by resize handler
var ALL_SCROLLERS = [];
var resizeTimeout;
// UPDATE ENTIRE COUNTDOWN
function updateCountdownFor(scrollerElement) {
var targetDateAttr = scrollerElement.dataset.countdown;
var type = scrollerElement.dataset.countdownType || '';
var options = scrollerElement.dataset.countdownOptions || '';
var startDayGate = parseInt(scrollerElement.dataset.countdownDateStart) || Infinity;
var isDayGated = !isNaN(startDayGate) && startDayGate !== Infinity;
var targetTime = new Date(targetDateAttr).getTime();
buildCountdownSkeleton(scrollerElement, type);
var scrollerDigits = {};
scrollerElement.querySelectorAll('.countdown-scroller__digit').forEach(function(container) {
var id = container.dataset.digitPosition;
var controller = DigitScroller(container, id, type);
scrollerDigits[id] = controller;
});
ALL_SCROLLERS.push(scrollerDigits);
var unitContainers = {
days: scrollerElement.querySelector('[data-unit="day"]'),
hours: scrollerElement.querySelector('[data-unit="hour"]'),
minutes: scrollerElement.querySelector('[data-unit="minute"]'),
seconds: scrollerElement.querySelector('[data-unit="second"]'),
};
var intervalId = null;
var hasFinished = false;
var updateTime = function(optionsArray, isDayGated, startDayGate) {
var config = window.CountdownScroller || {};
var separatorForDay = config.daySeparator || "-";
var separatorForHms = config.hmsSeparator || ":";
var useTimeTextHms = optionsArray.includes('time-text-hms');
var useTimeTextDay = optionsArray.includes('time-text-day');
var noDateLeadingZero = optionsArray.includes('no-date-leading-zero');
var noTimeLeadingZero = optionsArray.includes('no-time-leading-zero');
var now = new Date().getTime();
var diff = targetTime - now;
var isCountingDown = (type === 'go-down');
var messageAttr = scrollerElement.getAttribute('data-countdown-message');
var hasMessage = (messageAttr !== null);
var isComplete = (diff < 0);
var timeRemaining;
if (isComplete) {
timeRemaining = 0;
} else if (isCountingDown) {
timeRemaining = diff;
} else {
timeRemaining = Math.abs(diff);
}
var totalSeconds = Math.floor(timeRemaining / 1000);
var d = Math.floor(totalSeconds / (60 * 60 * 24));
var h = Math.floor((totalSeconds / (60 * 60)) % 24);
var m = Math.floor((totalSeconds / 60) % 60);
var s = Math.floor(totalSeconds % 60);
var dayDigitCount = Math.max(3, String(d).length);
if (isComplete) {
if (hasFinished) return;
hasFinished = true;
clearInterval(intervalId);
// If data-countdown-message is used
if (hasMessage) {
var isDefaultMessage = (messageAttr.trim() === '');
var displayText = isDefaultMessage ? 'Countdown ended!' : messageAttr;
setTimeout(function() {
scrollerElement.innerHTML = '<span class="countdown-scroller__complete-message">' + displayText + '</span>';
}, 250);
if (!isDefaultMessage) {
console.log('Countdown ended. Message has displayed.');
} else {
console.log('Countdown ended. Default message used.');
}
} else {
console.log('Countdown ended. No message configured.');
}
}
var isGated = isDayGated && d > startDayGate;
var daySeparator = scrollerElement.querySelector('.day-separator');
var hourSeparator = scrollerElement.querySelector('.hour-separator');
var minuteSeparator = scrollerElement.querySelector('.minute-separator');
var secondSeparator = scrollerElement.querySelector('.second-separator');
var dContainers = scrollerElement.querySelectorAll('.countdown-scroller__time-block.day .countdown-scroller__digit');
var hContainers = scrollerElement.querySelectorAll('.countdown-scroller__time-block.hour .countdown-scroller__digit');
var mContainers = scrollerElement.querySelectorAll('.countdown-scroller__time-block.minute .countdown-scroller__digit');
var sContainers = scrollerElement.querySelectorAll('.countdown-scroller__time-block.second .countdown-scroller__digit');
var forceSeparatorsVisible = isComplete && (messageAttr !== null);
// Hide day number digit when it reaches 0
var alwaysShowDay = config.hideDay === false;
var isDayHidden = (d === 0 && !isGated && !alwaysShowDay);
if (unitContainers.days) unitContainers.days.style.display = (isDayHidden && !isGated) ? 'none' : 'flex';
if (daySeparator) daySeparator.style.display = (isDayHidden && !isGated) ? 'none' : 'block';
// Show day if it reaches 0 and matches the configuration for hideDay
if (unitContainers.days && unitContainers.days.style.display === 'flex' && alwaysShowDay) {
var dayDigits = scrollerElement.querySelectorAll('.countdown-scroller__time-block.day .countdown-scroller__digit');
dayDigits.forEach(function(container) {
var id = container.dataset.digitPosition;
if (scrollerDigits[id]) {
scrollerDigits[id].recalculateDigitHeight();
}
});
}
if (unitContainers.hours) unitContainers.hours.style.display = isGated ? 'none' : 'flex';
if (unitContainers.minutes) unitContainers.minutes.style.display = isGated ? 'none' : 'flex';
if (unitContainers.seconds) unitContainers.seconds.style.display = isGated ? 'none' : 'flex';
if (unitContainers.days && daySeparator) {
var dayText = useTimeTextDay ? (d === 1 ? 'day' : 'days') : separatorForDay;
daySeparator.textContent = isGated ? dayText : dayText;
}
if (hourSeparator) hourSeparator.style.display = isGated ? 'none' : 'block';
if (minuteSeparator) minuteSeparator.style.display = isGated ? 'none' : 'block';
if (secondSeparator) secondSeparator.style.display = isGated ? 'none' : 'block';
if (!isGated) {
if (unitContainers.hours && hourSeparator) hourSeparator.textContent = useTimeTextHms ? (h === 1 ? 'hour' : 'hours') : separatorForHms;
if (unitContainers.minutes && minuteSeparator) minuteSeparator.textContent = useTimeTextHms ? (m === 1 ? 'minute' : 'minutes') : separatorForHms;
if (unitContainers.seconds && secondSeparator) secondSeparator.textContent = useTimeTextHms ? (s === 1 ? 'second' : 'seconds') : '';
}
// If data-countdown-option="no-time-leading-zero" is used
if (noTimeLeadingZero) {
var shouldHideH2 = (h < 10);
var shouldHideM2 = (m < 10);
var shouldHideS2 = (s < 10);
var wasH2Hidden = hContainers.length >= 2 && hContainers[0].style.display === 'none';
var wasM2Hidden = mContainers.length >= 2 && mContainers[0].style.display === 'none';
var wasS2Hidden = sContainers.length >= 2 && sContainers[0].style.display === 'none';
if (hContainers.length >= 2) hContainers[0].style.display = shouldHideH2 ? 'none' : 'flex';
if (mContainers.length >= 2) mContainers[0].style.display = shouldHideM2 ? 'none' : 'flex';
if (sContainers.length >= 2) sContainers[0].style.display = shouldHideS2 ? 'none' : 'flex';
var hoursStr = pad2(h);
var minutesStr = pad2(m);
var secondsStr = pad2(s);
var h2_val = parseInt(hoursStr.charAt(0));
var m2_val = parseInt(minutesStr.charAt(0));
var s2_val = parseInt(secondsStr.charAt(0));
var animateH2 = !shouldHideH2 && !wasH2Hidden;
var animateM2 = !shouldHideM2 && !wasM2Hidden;
var animateS2 = !shouldHideS2 && !wasS2Hidden;
var daysArr = String(d).padStart(dayDigitCount, '0').split('');
daysArr.forEach(function(digitChar, index) {
var id = "d" + (daysArr.length - index);
if (scrollerDigits[id]) scrollerDigits[id].setValue(parseInt(digitChar), !isComplete, isComplete);
});
if (!isGated) {
scrollerDigits.h2 && scrollerDigits.h2.setValue(h2_val, animateH2 && !isComplete, isComplete);
scrollerDigits.h1 && scrollerDigits.h1.setValue(h, !isComplete, isComplete);
scrollerDigits.m2 && scrollerDigits.m2.setValue(m2_val, animateM2 && !isComplete, isComplete);
scrollerDigits.m1 && scrollerDigits.m1.setValue(parseInt(minutesStr.charAt(1)), !isComplete, isComplete);
scrollerDigits.s2 && scrollerDigits.s2.setValue(s2_val, animateS2 && !isComplete, isComplete);
scrollerDigits.s1 && scrollerDigits.s1.setValue(parseInt(secondsStr.charAt(1)), !isComplete, isComplete);
}
if (hourSeparator) {
hourSeparator.style.display = (unitContainers.hours.style.display !== 'none' || forceSeparatorsVisible) ? 'block' : 'none';
if (useTimeTextHms) hourSeparator.textContent = (h === 1 ? 'hour' : 'hours');
}
if (minuteSeparator) {
minuteSeparator.style.display = (unitContainers.minutes.style.display !== 'none' || forceSeparatorsVisible) ? 'block' : 'none';
if (useTimeTextHms) minuteSeparator.textContent = (m === 1 ? 'minute' : 'minutes');
}
if (secondSeparator) {
secondSeparator.style.display = (useTimeTextHms || forceSeparatorsVisible) ? 'block' : 'none';
}
if (noDateLeadingZero) {
dContainers.forEach(function(container, index) {
if (index < dContainers.length - 1) {
var threshold = Math.pow(10, dContainers.length - 1 - index);
container.style.display = (d < threshold) ? 'none' : 'flex';
}
});
} else {
dContainers.forEach(function(container) { container.style.display = 'flex'; });
}
}
// Full countdown format
else {
if (hContainers.length >= 2) hContainers[0].style.display = isGated ? 'none' : 'flex';
if (mContainers.length >= 2) mContainers[0].style.display = isGated ? 'none' : 'flex';
if (sContainers.length >= 2) sContainers[0].style.display = isGated ? 'none' : 'flex';
if (hourSeparator) hourSeparator.style.display = isGated ? 'none' : 'block';
if (minuteSeparator) minuteSeparator.style.display = isGated ? 'none' : 'block';
if (secondSeparator) secondSeparator.style.display = isGated ? 'none' : 'block';
var daysStr = String(d).padStart(3, '0');
var hoursStr = pad2(h);
var minutesStr = pad2(m);
var secondsStr = pad2(s);
var daysArr = String(d).padStart(dayDigitCount, '0').split('');
daysArr.forEach(function(digitChar, index) {
var id = "d" + (daysArr.length - index);
if (scrollerDigits[id]) {
scrollerDigits[id].setValue(parseInt(digitChar), !isComplete, isComplete);
}
});
if (!isGated) {
scrollerDigits.h2 && scrollerDigits.h2.setValue(parseInt(hoursStr.charAt(0)), !isComplete, isComplete);
scrollerDigits.h1 && scrollerDigits.h1.setValue(h, !isComplete, isComplete);
scrollerDigits.m2 && scrollerDigits.m2.setValue(parseInt(minutesStr.charAt(0)), !isComplete, isComplete);
scrollerDigits.m1 && scrollerDigits.m1.setValue(parseInt(minutesStr.charAt(1)), !isComplete, isComplete);
scrollerDigits.s2 && scrollerDigits.s2.setValue(parseInt(secondsStr.charAt(0)), !isComplete, isComplete);
scrollerDigits.s1 && scrollerDigits.s1.setValue(parseInt(secondsStr.charAt(1)), !isComplete, isComplete);
}
// If data-countdown-option="no-date-leading-zero" is used
if (noDateLeadingZero) {
dContainers.forEach(function(container, index) {
if (index < dContainers.length - 1) {
var threshold = Math.pow(10, dContainers.length - 1 - index);
container.style.display = (d < threshold) ? 'none' : 'flex';
}
});
} else {
dContainers.forEach(function(container) {
container.style.display = 'flex';
});
}
}
};
updateTime(options, isDayGated, startDayGate);
var diff = targetTime - new Date().getTime();
if (diff > 0 || isDayGated) {
intervalId = setInterval(updateTime, 1000, options, isDayGated, startDayGate);
}
}
// DIGIT REALIGNMENT
function handleDigitRealignment() {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function() {
ALL_SCROLLERS.forEach(function(scrollerSet) {
for (var key in scrollerSet) {
if (scrollerSet.hasOwnProperty(key) && scrollerSet[key] && scrollerSet[key].recalculateDigitHeight) {
scrollerSet[key].recalculateDigitHeight();
}
}
});
}, 150);
}
window.addEventListener('resize', handleDigitRealignment);
if (window.ResizeObserver) {
var resizeObserver = new ResizeObserver(function(entries) {
handleDigitRealignment();
});
var observationTarget = document.body || document.documentElement;
if (observationTarget) {
resizeObserver.observe(observationTarget);
}
}
// INIT COUNTDOWN PROGRESS BAR
function initCountdownProgress(scrollerElement, targetTime) {
if (scrollerElement.dataset.countdownProgress !== 'true') return;
var progressWrap = document.createElement('div');
progressWrap.className = 'countdown-scroller__progress';
var progressBar = document.createElement('div');
progressBar.className = 'countdown-scroller__progress-bar';
progressWrap.appendChild(progressBar);
scrollerElement.prepend(progressWrap);
var startAttr = scrollerElement.dataset.countdownStart;
var startTime = startAttr ? new Date(startAttr).getTime() : Date.now();
var totalDuration = targetTime - startTime;
if (isNaN(startTime) || totalDuration <= 0) {
progressWrap.remove();
return;
}
if (totalDuration <= 0) {
progressBar.style.width = '100%';
return;
}
function updateCountdownProgress() {
var now = Date.now();
if (now >= targetTime) {
progressBar.style.width = '100%';
progressWrap.remove();
return;
}
if (now <= startTime) {
progressBar.style.width = '0%';
requestAnimationFrame(updateCountdownProgress);
return;
}
var elapsed = now - startTime;
var ratio = Math.min(elapsed / totalDuration, 1);
var config = window.CountdownScroller || {};
var reverseProgress = config.reverseProgress === true;
var finalRatio = 1 - ratio;
if (reverseProgress) {
finalRatio = ratio;
}
progressBar.style.width = (finalRatio * 100).toFixed(3) + '%';
requestAnimationFrame(updateCountdownProgress);
}
updateCountdownProgress();
}
// RUN THE COUNTDOWN
mw.hook('wikipage.content').add(function ($content) {
$content.find('.countdown-scroller').each(function() {
var scroller = this;
var dateStr = scroller.getAttribute('data-countdown');
var type = scroller.getAttribute('data-countdown-type');
var targetTime;
/* Check if [data-countdown] is missing or not declared */
if (!scroller.hasAttribute('data-countdown')) {
console.warn("[data-countdown] is empty. Please insert a valid date.");
scroller.textContent = "Missing [data-countdown]";
return;
}
targetTime = new Date(dateStr).getTime();
if (isNaN(targetTime)) {
console.warn("Invalid date:", dateStr);
scroller.textContent = "Invalid date format!";
return;
}
/* Check if [data-countdown-type] is valid */
if (!type || (type !== "go-down" && type !== "go-up")) {
console.warn("Invalid countdown type: [data-countdown-type] should use 'go-down' or 'go-up'.");
scroller.textContent = "Invalid countdown type!";
return; // HALT EXECUTION
}
/* Check if [data-countdown] is not the valid date format */
if (!dateStr || !dateStr.trim()) {
console.warn("Invalid date:", scroller);
scroller.textContent = "Invalid countdown!";
return;
}
/* Build the countdown */
setTimeout(function() {
updateCountdownFor(scroller);
initCountdownProgress(scroller, targetTime);
}, 100);
});
});
})();