Files
bom-local-service/wwwroot/js/radar-test/utils.js
T

185 lines
5.8 KiB
JavaScript

// Utility functions
// Cookie management
export function setCookie(name, value, days = 365) {
const expires = new Date();
expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000));
document.cookie = `${name}=${encodeURIComponent(JSON.stringify(value))};expires=${expires.toUTCString()};path=/`;
}
export function getCookie(name) {
const nameEQ = name + "=";
const ca = document.cookie.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) === 0) {
try {
return JSON.parse(decodeURIComponent(c.substring(nameEQ.length, c.length)));
} catch (e) {
return null;
}
}
}
return null;
}
// Date formatting utilities
export function formatDate(dateString) {
if (!dateString) return '-';
const date = new Date(dateString);
return date.toLocaleString('en-AU', {
timeZone: 'Australia/Brisbane',
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
}
export function getRelativeTime(dateString) {
if (!dateString) return '';
const date = new Date(dateString);
const now = new Date();
const diffMs = date - now;
const diffMins = Math.floor(diffMs / 60000);
const diffSecs = Math.floor((diffMs % 60000) / 1000);
if (diffMs < 0) {
return Math.abs(diffMins) + ' minute(s) ago';
} else if (diffMins > 0) {
return 'in ' + diffMins + ' minute(s)';
} else {
return 'in ' + diffSecs + ' second(s)';
}
}
export function getMinutesAgo(dateString) {
if (!dateString) return null;
// Handle both string and Date object inputs
let date;
if (typeof dateString === 'string') {
date = new Date(dateString);
} else if (dateString instanceof Date) {
date = dateString;
} else {
console.warn('getMinutesAgo: Expected string or Date, got:', typeof dateString, dateString);
return null;
}
// Check if date is valid
if (isNaN(date.getTime())) {
console.warn('getMinutesAgo: Invalid date:', dateString, 'parsed as:', date);
return null;
}
// Calculate difference in milliseconds
// Date.now() is UTC-based, date.getTime() is UTC-based, so this should be correct
const now = Date.now();
const dateTime = date.getTime();
const diffMs = now - dateTime;
// Convert to minutes and round
const minutes = Math.round(diffMs / 60000);
// If negative (future date), return null
if (minutes < 0) {
console.warn('getMinutesAgo: Future date detected:', dateString, 'parsed as:', date.toISOString());
return null;
}
// Safeguard against unreasonably large values (likely parsing errors)
// But don't cap at 60 - radar frames can legitimately be older in some cases
if (minutes > 100000) { // e.g., more than ~69 days ago
console.warn('getMinutesAgo: Unreasonably large minutes value:', minutes, 'for date:', dateString, 'parsed as:', date.toISOString());
return null;
}
return minutes;
}
// Format frame time for display (consolidates duplicate logic)
export function formatFrameTime(frame, isExtendedMode) {
if (!frame.absoluteObservationTime) return '-';
if (isExtendedMode) {
return formatDate(frame.absoluteObservationTime);
} else {
const minutesAgo = getMinutesAgo(frame.absoluteObservationTime);
// If minutesAgo is null (invalid date or edge case), fall back to formatted date
if (minutesAgo === null) {
return formatDate(frame.absoluteObservationTime);
}
return `${minutesAgo} min ago`;
}
}
// Network error detection (consolidates duplicate checks)
export function isNetworkError(error) {
return error.name === 'TypeError' ||
error.name === 'AbortError' ||
error.message.includes('fetch') ||
error.message.includes('network') ||
error.message.includes('Failed to fetch') ||
error.message === 'Network error';
}
export function isNetworkErrorResponse(response) {
return response.status === 0 || response.status >= 500;
}
// DOM helpers
export function getElement(id) {
return document.getElementById(id);
}
export function setElementText(id, text) {
const el = getElement(id);
if (el) el.textContent = text;
}
export function setElementHTML(id, html) {
const el = getElement(id);
if (el) el.innerHTML = html;
}
// Create cache status data object (consolidates duplicate creation)
export function createCacheStatusData(errorData) {
const details = errorData.details || errorData;
const isUpdating = details.updateTriggered === true ||
(errorData.message && errorData.message.includes('in progress')) ||
(details.statusMessage && details.statusMessage.includes('in progress'));
return {
frames: [],
isUpdating: isUpdating,
cacheIsValid: details.cacheIsValid || false,
cacheExpiresAt: details.cacheExpiresAt || null,
nextUpdateTime: details.nextUpdateTime || null
};
}
// Trigger background cache refresh (consolidates duplicate calls)
export function triggerBackgroundRefresh(refreshEndpoint) {
if (refreshEndpoint) {
fetch(refreshEndpoint, {
method: 'POST',
signal: AbortSignal.timeout(5000)
}).catch(err => {
console.debug('Background cache refresh trigger failed (non-critical):', err);
});
}
}
// Preload all frame images to prevent jiggle
export function preloadImages(frames) {
frames.forEach(frame => {
const img = new Image();
img.src = frame.imageUrl;
});
}