| | |
| | | * @param {string|Date} dateStr Date to format |
| | | * @returns {string} Formatted time string |
| | | */ |
| | | window.formatTimeAgo = function(dateStr) { |
| | | window.formatTimeAgo = function(dateStr, dateFormat = 'default') { |
| | | const date = dateStr instanceof Date ? dateStr : new Date(dateStr); |
| | | const now = new Date(); |
| | | const diffMs = date - now; |
| | |
| | | timeStr = `${minutes} ${minutes === 1 ? 'minute' : 'minutes'}`; |
| | | } else { |
| | | // Hours |
| | | timeStr = `${hours} ${hours === 1 ? 'hour' : 'hours'}`; |
| | | timeStr = `about ${hours} ${hours === 1 ? 'hour' : 'hours'}`; |
| | | } |
| | | } else if (days < 7) { |
| | | if (days === 1) { |
| | | return isPast ? 'yesterday' : 'tomorrow'; |
| | | } |
| | | timeStr = `about ${days} days`; |
| | | // Days |
| | | timeStr = `${days} ${days === 1 ? 'day' : 'days'}`; |
| | | } else { |
| | | // More than a week - just show the date |
| | | return date.toLocaleDateString(); |
| | | // More than a week - show the date based on format |
| | | if (dateFormat === 'default') { |
| | | return date.toLocaleDateString(); |
| | | } |
| | | |
| | | // Parse PHP-style format string |
| | | const formatMap = { |
| | | 'Y': date.getFullYear(), |
| | | 'y': String(date.getFullYear()).slice(-2), |
| | | 'F': date.toLocaleDateString('en-CA', { month: 'long' }), |
| | | 'M': date.toLocaleDateString('en-CA', { month: 'short' }), |
| | | 'm': String(date.getMonth() + 1).padStart(2, '0'), |
| | | 'n': date.getMonth() + 1, |
| | | 'd': String(date.getDate()).padStart(2, '0'), |
| | | 'j': date.getDate(), |
| | | 'D': date.toLocaleDateString('en-CA', { weekday: 'short' }), |
| | | 'l': date.toLocaleDateString('en-CA', { weekday: 'long' }), |
| | | 'H': String(date.getHours()).padStart(2, '0'), |
| | | 'i': String(date.getMinutes()).padStart(2, '0'), |
| | | 's': String(date.getSeconds()).padStart(2, '0'), |
| | | 'h': String(date.getHours() % 12 || 12).padStart(2, '0'), |
| | | 'g': date.getHours() % 12 || 12, |
| | | 'A': date.getHours() >= 12 ? 'PM' : 'AM', |
| | | 'a': date.getHours() >= 12 ? 'pm' : 'am', |
| | | }; |
| | | |
| | | return dateFormat.replace(/[YyFMmnjDlHishgAa]/g, match => formatMap[match]); |
| | | } |
| | | |
| | | // Add appropriate prefix/suffix based on past or future |
| | |
| | | return div.innerHTML; |
| | | } |
| | | |
| | | window.generateID = function(prefix = 'jvb') { |
| | | return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2,9)}`; |
| | | } |
| | | |
| | | window.showProgress = function(elements, current, total, message = '', icon = '') { |
| | | const show = current < total; |
| | | if (elements.progress && show) { |
| | | window.fade(elements.progress, true); |
| | | } |
| | | const percent = total > 0 ? (current / total) * 100 : 0; |
| | | if (elements.fill) elements.fill.style.width = `${percent}%`; |
| | | if (elements.details) elements.details.textContent = message; |
| | | if (elements.count) elements.count.textContent = `${current}/${total}`; |
| | | if (elements.icon) elements.icon.className = (icon === '') ? 'icon' : 'icon icon-'+icon; |
| | | |
| | | if (elements.progress && current === total) { |
| | | window.fade(elements.progress, false); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Format a date string for display |
| | | * @param {string} dateString - ISO date string |