// How often to query the server? const REFRESH_INTERVAL_SEC = 60 * 30; // Storage for the alert data that the server gives us. var alerts = [] // Load alerts and populate the table. function loadAlerts() { $.getJSON('/api/v1/alerts' + buildQueryString(), function(jsonData) { // Store last updated time lastUpdateTime = moment.utc(); updateRefreshDisplay(); // Store data alerts = jsonData; // Update table updateTable(); }); } // Build a query string for the API, based on the filters that the user has selected. function buildQueryString() { var str = "?"; ["dx_continent", "source"].forEach(fn => { if (!allFilterOptionsSelected(fn)) { str = str + getQueryStringFor(fn) + "&"; } }); str = str + "limit=" + $("#alerts-to-fetch option:selected").val(); var maxDur = $("#max-duration option:selected").val(); if (maxDur != "9999999999") { str = str + "&max_duration=" + maxDur; } if ($("#dxpeditions_skip_max_duration_check")[0].checked) { str = str + "&dxpeditions_skip_max_duration_check=true"; } return str; } // Update the alerts table function updateTable() { // Use local time instead of UTC? var useLocalTime = $("#useLocalTime")[0].checked; // Populate table with headers let table = $('').append(''); table.find('thead tr').append(``); table.find('thead tr').append(``); table.find('thead tr').append(``); table.find('thead tr').append(``); table.find('thead tr').append(``); table.find('thead tr').append(``); table.find('thead tr').append(``); // Split alerts into three types, each of which will get its own table header: On now, next 24h, and later. "On now" // is considered to be events with an end_time where start (a["end_time"] != null && a["end_time"] != 0 && moment.unix(a["end_time"]).utc().isSameOrAfter() && moment.unix(a["start_time"]).utc().isBefore()) || ((a["end_time"] == null || a["end_time"] == 0) && moment.unix(a["start_time"]).utc().add(1, 'hours').isSameOrAfter() && moment.unix(a["start_time"]).utc().isBefore())); next24h = alerts.filter(a => moment.unix(a["start_time"]).utc().isSameOrAfter() && moment.unix(a["start_time"]).utc().subtract(24, 'hours').isBefore()); later = alerts.filter(a => moment.unix(a["start_time"]).utc().subtract(24, 'hours').isSameOrAfter()); if (onNow.length > 0) { table.find('tbody').append(''); addAlertRowsToTable(table.find('tbody'), onNow); } if (next24h.length > 0) { table.find('tbody').append(''); addAlertRowsToTable(table.find('tbody'), next24h); } if (later.length > 0) { table.find('tbody').append(''); addAlertRowsToTable(table.find('tbody'), later); } if (onNow.length == 0 && next24h.length == 0 && later.length == 0) { table.find('tbody').append(''); } // Update DOM $('#table-container').html(table); } // Add a row to tbody for each alert in the provided list function addAlertRowsToTable(tbody, alerts) { alerts.forEach(a => { // Create row let $tr = $(''); // Use local time instead of UTC? var useLocalTime = $("#useLocalTime")[0].checked; // Get times for the alert, and convert to local time if necessary. var start_time_unix = moment.unix(a["start_time"]); var start_time = start_time_unix.utc(); if (useLocalTime) { start_time = start_time.local(); } var end_time_unix = moment.unix(a["end_time"]); var end_time = end_time_unix.utc(); if (useLocalTime) { end_time = end_time.local(); } // Format the times for display. Start time is displayed as e.g. 7 Oct 12:34 unless the time is in a // different year to the current year, in which case the year is inserted between month and hour. // If the time is set to local not UTC, and the date in local time is "today", we display that instead. // End time is displayed the same as above, except if the end date is the same as the start date, in which case // just e.g. 23:45 is used. // Overriding all of that, if the start time is 00:00 and the end time is 23:59 when considered in UTC, the // hours and minutes are stripped out from the display, as we assume the server is just giving us full days. // Finally, if there is no end date set, "---" is displayed. var whole_days = start_time_unix.utc().format("HH:mm") == "00:00" && (end_time_unix != null || end_time_unix > 0 || end_time_unix.utc().format("HH:mm") == "23:59"); var hours_minutes_format = whole_days ? "" : " HH:mm"; var start_time_formatted = start_time.format("D MMM" + hours_minutes_format); if (start_time.format("YYYY") != moment().format("YYYY")) { start_time_formatted = start_time.format("D MMM YYYY" + hours_minutes_format); } else if (useLocalTime && start_time.format("D MMM YYYY") == moment().format("D MMM YYYY")) { start_time_formatted = start_time.format("[Today]" + hours_minutes_format); } var end_time_formatted = "---"; if (end_time_unix != null && end_time_unix > 0 && end_time != null) { var end_time_formatted = whole_days ? start_time_formatted : end_time.format("HH:mm"); if (end_time.format("D MMM") != start_time.format("D MMM")) { if (end_time.format("YYYY") != moment().format("YYYY")) { end_time_formatted = end_time.format("D MMM YYYY" + hours_minutes_format); } else { end_time_formatted = end_time.format("D MMM" + hours_minutes_format); } } } // Format DX flag var dx_flag = ""; if (a["dx_flag"] && a["dx_flag"] != null && a["dx_flag"] != "") { dx_flag = a["dx_flag"]; } // Format dx country var dx_country = a["dx_country"] if (dx_country == null) { dx_country = "Unknown or not a country" } // Format dx calls var dx_calls_html = ""; if (a["dx_calls"] != null) { dx_calls_html = a["dx_calls"].map(call => `${call}`).join(", "); } // Format freqs & modes var freqsModesText = ""; if (a["freqs_modes"] != null) { freqsModesText = escapeHtml(a["freqs_modes"]); } // Format comment var commentText = ""; if (a["comment"] != null) { commentText = escapeHtml(a["comment"]); } // Sig or fallback to source var sigSourceText = a["source"]; if (a["sig"]) { sigSourceText = a["sig"]; } // Format sig_refs var sig_refs = "" if (a["sig_refs"]) { sig_refs = a["sig_refs"].map(a => `${a}`).join(", "); } // Populate the row $tr.append(``); $tr.append(``); $tr.append(``); $tr.append(``); $tr.append(``); $tr.append(``); $tr.append(``); tbody.append($tr); // Second row for mobile view only, containing source, ref, freqs/modes & comment $tr2 = $(""); $tr2.append(``); tbody.append($tr2); }); } // Load server options. Once a successful callback is made from this, we then query alerts. function loadOptions() { $.getJSON('/api/v1/options', function(jsonData) { // Store options options = jsonData; // Populate the filters panel $("#filters-container").append(generateMultiToggleFilterCard("DX Continent", "dx_continent", options["continents"])); $("#filters-container").append(generateMultiToggleFilterCard("Sources", "source", options["alert_sources"])); // Options doesn't give us anything for Max Duration as it's a free numeric input, but we generate our own // filter card for this. $("#filters-container").append(generateMaxDurationDropdownFilterCard(options["alert_sources"])); // Load filters from settings storage loadSettings(); // Load alerts and set up the timer loadAlerts(); setInterval(loadAlerts, REFRESH_INTERVAL_SEC * 1000); }); } // Generate maximum duration drop-down filter card. This one is a special case. function generateMaxDurationDropdownFilterCard(band_options) { let $col = $("
") let $card = $("
"); let $card_body = $("
"); $card_body.append(`
Duration Limit
`); $p = $("

"); $p.append("Hide any alerts lasting more than:
"); $p.append(``); $p2 = $("

"); $p2.append(``); // Compile HTML elements to return $card_body.append($p); $card_body.append($p2); $card.append($card_body); $col.append($card); return $col; } // Method called when any filter is changed to reload the alerts and persist the filter settings. function filtersUpdated() { loadAlerts(); saveSettings(); } // Set up UI element event listeners, after the document is ready function setUpEventListeners() { $("#filters-button").click(function() { $("#filters-area").toggle(); }); $("#close-filters-button").click(function() { $("#filters-button").button("toggle"); $("#filters-area").hide(); }); } // Startup $(document).ready(function() { // Call loadOptions(), this will then trigger loading alerts and setting up timers. loadOptions(); // Update the refresh timing display every second setInterval(updateRefreshDisplay, 1000); // Set up event listeners setUpEventListeners(); });

${useLocalTime ? "Start (Local)" : "Start UTC"}${useLocalTime ? "End (Local)" : "End UTC"}DXFrequencies & ModesCommentSourceRef.
On Now
Starting within 24 hours
Starting later
No alerts match your filters.
${start_time_formatted}${end_time_formatted}${dx_flag}${dx_calls_html}${freqsModesText}${commentText} ${sigSourceText}${sig_refs}
${sig_refs} ${freqsModesText}
${commentText}