/** * 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; } }