// How often to query the server? const REFRESH_INTERVAL_SEC = 60; // Storage for the spot data that the server gives us. var spots = [] // Load spots and populate the table. function loadSpots() { $.getJSON('/api/v1/spots' + buildQueryString(), function(jsonData) { // Store last updated time lastUpdateTime = moment.utc(); updateRefreshDisplay(); // Store data spots = 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", "de_continent", "mode_type", "source", "band"].forEach(fn => { if (!allFilterOptionsSelected(fn)) { str = str + getQueryStringFor(fn) + "&"; } }); str = str + "limit=" + $("#spots-to-fetch option:selected").val(); return str; } // Update the spots table function updateTable() { // Use local time instead of UTC? var useLocalTime = $("#timeZone")[0].value == "local"; // 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(``); table.find('thead tr').append(``); if (spots.length == 0) { table.find('tbody').append(''); } spots.forEach(s => { // Create row let $tr = $(''); // Show faded out if QRT if (s["qrt"] == true) { $tr.addClass("table-faded"); } // Format a UTC or local time for display var time = moment.unix(s["time"]).utc(); if (useLocalTime) { time = time.local(); } var time_formatted = time.format("HH:mm"); // Format DX flag var dx_flag = ""; if (s["dx_flag"] && s["dx_flag"] != null && s["dx_flag"] != "") { dx_flag = s["dx_flag"]; } // Format dx country var dx_country = s["dx_country"]; if (dx_country == null) { dx_country = "Unknown or not a country"; } // Format the frequency var mhz = Math.floor(s["freq"] / 1000000.0); var khz = Math.floor((s["freq"] - (mhz * 1000000.0)) / 1000.0); var hz = Math.floor(s["freq"] - (mhz * 1000000.0) - (khz * 1000.0)); var hz_string = (hz > 0) ? hz.toFixed(0)[0] : ""; var freq_string = `${mhz.toFixed(0)}${khz.toFixed(0).padStart(3, '0')}${hz_string}` // Format the mode mode_string = s["mode"]; if (s["mode"] == null) { mode_string = "???"; } if (s["mode_source"] == "BANDPLAN") { mode_string = mode_string + "" } // Format comment var commentText = ""; if (s["comment"] != null) { commentText = escapeHtml(s["comment"]); } // Sig or fallback to source var sigSourceText = s["source"]; if (s["sig"]) { sigSourceText = s["sig"]; } // Format sig_refs var sig_refs = ""; if (s["sig_refs"]) { sig_refs = s["sig_refs"].map(s => `${s}`).join(", "); } // Format sig_refs title var sig_refs_title_string = ""; if (s["sig_refs_names"]) { sig_refs_title_string = " title=\"" + s["sig_refs_names"].join(", ") + "\""; } // Format DE flag var de_flag = ""; if (s["de_flag"] && s["de_flag"] != null && s["de_flag"] != "") { de_flag = s["de_flag"]; } // Format de country var de_country = s["de_country"]; if (de_country == null) { de_country = "Unknown or not a country"; } // Format de call var de_call = s["de_call"]; if (de_call == null) { de_call = ""; de_flag = ""; } // CSS doesn't like classes with decimal points in, so we need to replace that in the same way as when we originally // queried the options endpoint and set our CSS. var cssFormattedBandName = s['band'] ? s['band'].replace('.', 'p') : "unknown"; var bandFullName = s['band'] ? s['band'] + " band": "Unknown band"; // Populate the row $tr.append(``); $tr.append(``); $tr.append(``); $tr.append(``); $tr.append(``); $tr.append(``); $tr.append(``); $tr.append(``); table.find('tbody').append($tr); // Second row for mobile view only, containing source, ref & comment $tr2 = $(""); if (s["qrt"] == true) { $tr2.addClass("table-faded"); } $tr2.append(``); table.find('tbody').append($tr2); }); // Update DOM $('#table-container').html(table); } // Load server options. Once a successful callback is made from this, we then query spots and set up the timer to query // spots repeatedly. function loadOptions() { $.getJSON('/api/v1/options', function(jsonData) { // Store options options = jsonData; // Add CSS for band bullets and band toggle buttons addBandColourCSS(options["bands"]); // Populate the filters panel $("#filters-container-1").append(generateBandsMultiToggleFilterCard(options["bands"])); $("#filters-container-2").append(generateMultiToggleFilterCard("DX Continent", "dx_continent", options["continents"])); $("#filters-container-2").append(generateMultiToggleFilterCard("DE Continent", "de_continent", options["continents"])); $("#filters-container-2").append(generateMultiToggleFilterCard("Modes", "mode_type", options["mode_types"])); $("#filters-container-2").append(generateMultiToggleFilterCard("Sources", "source", options["spot_sources"])); // Load filters from settings storage loadSettings(); // Load spots and set up the timer loadSpots(); setInterval(loadSpots, REFRESH_INTERVAL_SEC * 1000); }); } // Dynamically add CSS code for the band bullets and band toggle buttons to be in the appropriate colour. // Some band names contain decimal points which are not allowed in CSS classes, so we text-replace them to "p". function addBandColourCSS(band_options) { var $style = $('
${useLocalTime ? "Local" : "UTC"}DXFrequencyModeCommentSourceRef.DE
No spots match your filters.
${time_formatted}${dx_flag}${s["dx_call"]}${freq_string}${mode_string}${commentText} ${sigSourceText}${sig_refs}${de_flag}${de_call}
${sig_refs} ${commentText}