172 lines
5.4 KiB
JavaScript
172 lines
5.4 KiB
JavaScript
/**
|
|
* Event-based time selection functionality for FDSNWS DataSelect builder
|
|
*/
|
|
|
|
// Simple debounce utility
|
|
function debounce(fn, delay) {
|
|
let timeout;
|
|
return function(...args) {
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(() => fn.apply(this, args), delay);
|
|
};
|
|
}
|
|
|
|
async function checkEventService() {
|
|
try {
|
|
const response = await fetch('../../event/1/version');
|
|
return response.ok;
|
|
} catch (error) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Event functionality
|
|
let currentOriginTime = null;
|
|
let currentController = null; // Track current request for cancellation
|
|
|
|
async function fetchEventOriginTime(eventId, signal = null) {
|
|
const params = new URLSearchParams({
|
|
eventid: eventId,
|
|
format: 'text',
|
|
nodata: '404'
|
|
});
|
|
|
|
const response = await fetch(`../../event/1/query?${params}`, { signal });
|
|
|
|
if (!response.ok) {
|
|
throw new Error(response.status === 404 ? 'Event not found' : 'Failed to fetch event');
|
|
}
|
|
|
|
const text = await response.text();
|
|
const lines = text.trim().split('\n');
|
|
if (lines.length < 2) throw new Error('Invalid response format');
|
|
|
|
const originTimeStr = lines[1].split('|')[1]?.trim();
|
|
if (!originTimeStr) throw new Error('No origin time found');
|
|
|
|
const originTime = originTimeStr.endsWith('Z')
|
|
? new Date(originTimeStr)
|
|
: new Date(originTimeStr + 'Z');
|
|
|
|
if (isNaN(originTime.getTime())) throw new Error('Invalid origin time');
|
|
|
|
return originTime;
|
|
}
|
|
|
|
function updateTimeFields() {
|
|
if (!currentOriginTime) return;
|
|
|
|
const before = parseFloat(document.getElementById('before-time').value) || 0;
|
|
const after = parseFloat(document.getElementById('after-time').value) || 0;
|
|
|
|
const startTime = new Date(currentOriginTime.getTime() - before * 60000);
|
|
const endTime = new Date(currentOriginTime.getTime() + after * 60000);
|
|
|
|
const startField = document.querySelector('input[name="starttime"]');
|
|
const endField = document.querySelector('input[name="endtime"]');
|
|
|
|
startField.value = startTime.toISOString().replace(/\.\d{3}Z$/, '');
|
|
endField.value = endTime.toISOString().replace(/\.\d{3}Z$/, '');
|
|
|
|
// Trigger URL update by dispatching change events on the time fields
|
|
startField.dispatchEvent(new Event('change', { bubbles: true }));
|
|
endField.dispatchEvent(new Event('change', { bubbles: true }));
|
|
}
|
|
|
|
const debouncedEventLookup = debounce(async function(eventId) {
|
|
const statusDiv = document.getElementById('event-status');
|
|
|
|
// Cancel previous request if still running
|
|
if (currentController) {
|
|
currentController.abort();
|
|
}
|
|
|
|
if (!eventId.trim()) {
|
|
currentController = null;
|
|
currentOriginTime = null;
|
|
statusDiv.textContent = '';
|
|
statusDiv.className = '';
|
|
return;
|
|
}
|
|
|
|
if (eventId.length < 3) {
|
|
statusDiv.textContent = 'Enter complete Event ID...';
|
|
statusDiv.className = 'loading';
|
|
return;
|
|
}
|
|
|
|
// Create new controller - this becomes the "current" request
|
|
currentController = new AbortController();
|
|
const signal = currentController.signal;
|
|
|
|
statusDiv.textContent = 'Fetching event...';
|
|
statusDiv.className = 'loading';
|
|
|
|
try {
|
|
currentOriginTime = await fetchEventOriginTime(eventId, signal);
|
|
|
|
// Only update UI if this request wasn't cancelled
|
|
if (!signal.aborted) {
|
|
statusDiv.textContent = `Origin Time: ${currentOriginTime.toISOString().replace(/\.\d{3}Z$/, '')}`;
|
|
statusDiv.className = 'success';
|
|
updateTimeFields();
|
|
}
|
|
} catch (error) {
|
|
// Don't show error if request was just cancelled
|
|
if (!signal.aborted && error.name !== 'AbortError') {
|
|
statusDiv.textContent = error.message;
|
|
statusDiv.className = 'error';
|
|
currentOriginTime = null;
|
|
}
|
|
}
|
|
}, 800);
|
|
|
|
function clearEventData() {
|
|
// Cancel any pending request
|
|
if (currentController) {
|
|
currentController.abort();
|
|
currentController = null;
|
|
}
|
|
|
|
document.getElementById('event-id').value = '';
|
|
document.getElementById('before-time').value = '';
|
|
document.getElementById('after-time').value = '';
|
|
|
|
const startField = document.querySelector('input[name="starttime"]');
|
|
const endField = document.querySelector('input[name="endtime"]');
|
|
startField.value = '';
|
|
endField.value = '';
|
|
|
|
document.getElementById('event-status').textContent = '';
|
|
document.getElementById('event-status').className = '';
|
|
currentOriginTime = null;
|
|
|
|
// Trigger URL update
|
|
startField.dispatchEvent(new Event('change', { bubbles: true }));
|
|
endField.dispatchEvent(new Event('change', { bubbles: true }));
|
|
}
|
|
|
|
async function initializeEventTimeSelection() {
|
|
// Check event service on load and configure UI
|
|
const serviceAvailable = await checkEventService();
|
|
const eventSection = document.querySelector('.event-section');
|
|
const eventLegend = eventSection.querySelector('legend');
|
|
|
|
if (serviceAvailable) {
|
|
eventLegend.textContent = 'Event-based Time Selection ✓';
|
|
eventLegend.style.color = '#28a745';
|
|
|
|
// Set up event listeners
|
|
document.getElementById('event-id').addEventListener('input', (e) => {
|
|
debouncedEventLookup(e.target.value);
|
|
});
|
|
|
|
document.getElementById('before-time').addEventListener('input', updateTimeFields);
|
|
document.getElementById('after-time').addEventListener('input', updateTimeFields);
|
|
document.getElementById('clear-event').addEventListener('click', clearEventData);
|
|
} else {
|
|
eventLegend.textContent = 'Event-based Time Selection (service unavailable)';
|
|
eventLegend.style.color = '#dc3545';
|
|
eventSection.disabled = true;
|
|
}
|
|
} |