mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-02-04 01:04:33 +00:00
Style improvements and fixes #3
This commit is contained in:
@@ -10,7 +10,7 @@ from data.sig_ref import SIGRef
|
||||
|
||||
# Alert provider for Beaches on the Air
|
||||
class BOTA(HTTPAlertProvider):
|
||||
POLL_INTERVAL_SEC = 3600
|
||||
POLL_INTERVAL_SEC = 1800
|
||||
ALERTS_URL = "https://www.beachesontheair.com/"
|
||||
|
||||
def __init__(self, provider_config):
|
||||
|
||||
@@ -10,7 +10,7 @@ from data.alert import Alert
|
||||
|
||||
# Alert provider NG3K DXpedition list
|
||||
class NG3K(HTTPAlertProvider):
|
||||
POLL_INTERVAL_SEC = 3600
|
||||
POLL_INTERVAL_SEC = 1800
|
||||
ALERTS_URL = "https://www.ng3k.com/adxo.xml"
|
||||
AS_CALL_PATTERN = re.compile("as ([a-z0-9/]+)", re.IGNORECASE)
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ from data.sig_ref import SIGRef
|
||||
|
||||
# Alert provider for Parks n Peaks
|
||||
class ParksNPeaks(HTTPAlertProvider):
|
||||
POLL_INTERVAL_SEC = 3600
|
||||
POLL_INTERVAL_SEC = 1800
|
||||
ALERTS_URL = "http://parksnpeaks.org/api/ALERTS/"
|
||||
|
||||
def __init__(self, provider_config):
|
||||
|
||||
@@ -9,7 +9,7 @@ from data.sig_ref import SIGRef
|
||||
|
||||
# Alert provider for Parks on the Air
|
||||
class POTA(HTTPAlertProvider):
|
||||
POLL_INTERVAL_SEC = 3600
|
||||
POLL_INTERVAL_SEC = 1800
|
||||
ALERTS_URL = "https://api.pota.app/activation"
|
||||
|
||||
def __init__(self, provider_config):
|
||||
|
||||
@@ -9,7 +9,7 @@ from data.sig_ref import SIGRef
|
||||
|
||||
# Alert provider for Summits on the Air
|
||||
class SOTA(HTTPAlertProvider):
|
||||
POLL_INTERVAL_SEC = 3600
|
||||
POLL_INTERVAL_SEC = 1800
|
||||
ALERTS_URL = "https://api-db2.sota.org.uk/api/alerts/365/all/all"
|
||||
|
||||
def __init__(self, provider_config):
|
||||
|
||||
@@ -10,7 +10,7 @@ from data.sig_ref import SIGRef
|
||||
|
||||
# Alert provider for Wainwrights on the Air
|
||||
class WOTA(HTTPAlertProvider):
|
||||
POLL_INTERVAL_SEC = 3600
|
||||
POLL_INTERVAL_SEC = 1800
|
||||
ALERTS_URL = "https://www.wota.org.uk/alerts_rss.php"
|
||||
RSS_DATE_TIME_FORMAT = "%a, %d %b %Y %H:%M:%S %z"
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ from data.sig_ref import SIGRef
|
||||
|
||||
# Alert provider for Worldwide Flora and Fauna
|
||||
class WWFF(HTTPAlertProvider):
|
||||
POLL_INTERVAL_SEC = 3600
|
||||
POLL_INTERVAL_SEC = 1800
|
||||
ALERTS_URL = "https://spots.wwff.co/static/agendas.json"
|
||||
|
||||
def __init__(self, provider_config):
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<p>Spothole is a Progressive Web App, which means you can install it on an Android or iOS device by opening the site in Chrome or Safari respectively, and clicking "Install" on the pop-up panel. It'll only prompt you once, so if you dismiss the prompt and change your mind, you'll find an Install / Add to Home Screen option on your browser's menu.</p>
|
||||
<p>Installing Spothole on your phone is completely optional, the website works exactly the same way as the "app" does.</p>
|
||||
<h4 class="mt-4">Why hasn't my spot/alert shown up yet?</h4>
|
||||
<p>To avoid putting too much load on the various servers that Spothole connects to, the Spothole server only polls them once every two minutes for spots, and once every hour for alerts. (Some sources, such as DX clusters, RBN, APRS-IS and WWBOTA use a non-polling mechanism, and their updates will therefore arrive more quickly.) Then if you are using the web interface, that has its own rate at which it reloads the data from Spothole, which is once a minute for spots or 30 minutes for alerts. So you could be waiting around three minutes to see a newly added spot, or 90 minutes to see a newly added alert.</p>
|
||||
<p>To avoid putting too much load on the various servers that Spothole connects to, the Spothole server only polls them once every two minutes for spots, and once every 30 minutes for alerts. (Some sources, such as DX clusters, RBN, APRS-IS and WWBOTA use a non-polling mechanism, and their updates will therefore arrive more quickly.) Then if you are using the web interface, that has its own rate at which it fetches the data from Spothole. This is instant for the main spots list, with new spots appearing immediately at the top of the list, while the map and bands displays update once a minute, and the alerts display updates once every 5 minutes. So you could be waiting around three minutes to see a newly added spot, or 40 minutes to see a newly added alert.</p>
|
||||
<h4 class="mt-4">What licence does Spothole use?</h4>
|
||||
<p>Spothole's source code is licenced under the Public Domain. You can write a Spothole client, run your own server, modify it however you like, you can claim you wrote it and charge people £1000 for a copy, I don't really mind. (Please don't do the last one. But if you're using my code for something cool, it would be nice to hear from you!)</p>
|
||||
<h2 class="mt-4">Data Accuracy</h2>
|
||||
|
||||
@@ -161,7 +161,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="table-container"></div>
|
||||
<div id="table-container">
|
||||
<table id="table" class="table"><thead><tr class="table-primary"></tr></thead><tbody></tbody></table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -204,7 +204,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="table-container"></div>
|
||||
<div id="table-container">
|
||||
<table id="table" class="table"><thead><tr class="table-primary"></tr></thead><tbody></tbody></table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -174,6 +174,19 @@ tr.table-faded td span {
|
||||
text-decoration: line-through !important;
|
||||
}
|
||||
|
||||
/* New spot styles */
|
||||
tr.new td {
|
||||
animation: 2s linear newspotanim;
|
||||
}
|
||||
@keyframes newspotanim {
|
||||
0% {
|
||||
background-color: var(--bs-success-border-subtle);
|
||||
}
|
||||
100% {
|
||||
background-color: intial;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fudge apply our own "dark primary" and "dark danger" backgrounds as Bootstrap doesn't do this itself */
|
||||
[data-bs-theme=dark] tr.table-primary {
|
||||
--bs-table-bg: #053680;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// How often to query the server?
|
||||
const REFRESH_INTERVAL_SEC = 60 * 30;
|
||||
const REFRESH_INTERVAL_SEC = 60 * 10;
|
||||
|
||||
// Storage for the alert data that the server gives us.
|
||||
var alerts = []
|
||||
@@ -51,7 +51,8 @@ function updateTable() {
|
||||
var showRef = $("#tableShowRef")[0].checked;
|
||||
|
||||
// Populate table with headers
|
||||
let table = $('<table class="table">').append('<thead><tr class="table-primary"></tr></thead><tbody></tbody>');
|
||||
let table = $("#table");
|
||||
table.find('thead tr').empty();
|
||||
if (showStartTime) {
|
||||
table.find('thead tr').append(`<th>${useLocalTime ? "Start (Local)" : "Start UTC"}</th>`);
|
||||
}
|
||||
@@ -74,6 +75,8 @@ function updateTable() {
|
||||
table.find('thead tr').append(`<th class='hideonmobile'>Ref.</th>`);
|
||||
}
|
||||
|
||||
table.find('tbody').empty();
|
||||
|
||||
// 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<now<end, or events with no end time that started in the
|
||||
// last hour.
|
||||
@@ -100,9 +103,6 @@ function updateTable() {
|
||||
if (onNow.length == 0 && next24h.length == 0 && later.length == 0) {
|
||||
table.find('tbody').append('<tr class="table-danger"><td colspan="100" style="text-align:center;">No alerts match your filters.</td></tr>');
|
||||
}
|
||||
|
||||
// Update DOM
|
||||
$('#table-container').html(table);
|
||||
}
|
||||
|
||||
// Add a row to tbody for each alert in the provided list
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// SSE event source
|
||||
let evtSource;
|
||||
// Table row count, to alternate shading
|
||||
let rowCount = 0;
|
||||
|
||||
// Load spots and populate the table.
|
||||
function loadSpots() {
|
||||
@@ -28,12 +30,12 @@ function restartSSEConnection() {
|
||||
// Store last updated time
|
||||
lastUpdateTime = moment.utc();
|
||||
updateRefreshDisplay();
|
||||
// Add spot to table
|
||||
// Add spot to internal data store
|
||||
newSpot = JSON.parse(event.data);
|
||||
console.log(newSpot);
|
||||
spots.unshift(newSpot);
|
||||
spots = spots.slice(0, -1);
|
||||
updateTable();
|
||||
// Add spot to table
|
||||
addSpotToTopOfTable(newSpot, true);
|
||||
};
|
||||
|
||||
evtSource.onerror = function(err) {
|
||||
@@ -76,7 +78,8 @@ function updateTable() {
|
||||
var showDE = $("#tableShowDE")[0].checked;
|
||||
|
||||
// Populate table with headers
|
||||
let table = $('<table class="table">').append('<thead><tr class="table-primary"></tr></thead><tbody></tbody>');
|
||||
let table = $("#table");
|
||||
table.find('thead tr').empty();
|
||||
if (showTime) {
|
||||
table.find('thead tr').append(`<th>${useLocalTime ? "Local" : "UTC"}</th>`);
|
||||
}
|
||||
@@ -105,199 +108,230 @@ function updateTable() {
|
||||
table.find('thead tr').append(`<th class='hideonmobile'>DE</th>`);
|
||||
}
|
||||
|
||||
table.find('tbody').empty();
|
||||
if (spots.length == 0) {
|
||||
table.find('tbody').append('<tr class="table-danger"><td colspan="100" style="text-align:center;">No spots match your filters.</td></tr>');
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
spots.forEach(s => {
|
||||
// Create row
|
||||
let $tr = $('<tr>');
|
||||
spots.reverse();
|
||||
spots.forEach(s => addSpotToTopOfTable(s, false));
|
||||
}
|
||||
|
||||
// Apply striping to the table. We can't just use Bootstrap's table-striped class because we have all sorts of
|
||||
// extra faff to deal with, like the mobile view having extra rows, and the On Now / Next 24h / Later banners
|
||||
// which cause the table-striped colouring to go awry.
|
||||
if (count % 2 == 1) {
|
||||
$tr.addClass("table-active");
|
||||
}
|
||||
// Add rows corresponding to a new spot to the top of the table
|
||||
// highlightNew = false for an initial load, true for new SSE-loaded spots
|
||||
function addSpotToTopOfTable(s, highlightNew) {
|
||||
let rows = createNewTableRowsForSpot(s, highlightNew);
|
||||
$("#table").find('tbody').prepend(rows[1]);
|
||||
$("#table").find('tbody').prepend(rows[0]);
|
||||
}
|
||||
|
||||
// Show faded out if QRT
|
||||
if (s["qrt"] == true) {
|
||||
$tr.addClass("table-faded");
|
||||
}
|
||||
// Turn a spot into a set of table rows to represent it. This is actually two table rows because we need a second
|
||||
// separate row for the mobile view.
|
||||
// highlightNew = false for an initial load, true for new SSE-loaded spots
|
||||
function createNewTableRowsForSpot(s, highlightNew) {
|
||||
// Use local time instead of UTC?
|
||||
var useLocalTime = $("#timeZone")[0].value == "local";
|
||||
|
||||
// Format a UTC or local time for display
|
||||
var time = moment.unix(s["time"]).utc();
|
||||
if (useLocalTime) {
|
||||
time.local();
|
||||
}
|
||||
var time_formatted = time.format("HH:mm");
|
||||
// Get user grid if valid, this will be null if it's not.
|
||||
var userPos = latLonForGridCentre($("#userGrid").val());
|
||||
|
||||
// Format DX call
|
||||
var dx_call = s["dx_call"];
|
||||
if (dx_call == null) {
|
||||
dx_call = "";
|
||||
dx_flag = "";
|
||||
}
|
||||
if (s["dx_ssid"] != null) {
|
||||
dx_call = dx_call + "-" + s["dx_ssid"];
|
||||
}
|
||||
// Table data toggles
|
||||
var showTime = $("#tableShowTime")[0].checked;
|
||||
var showDX = $("#tableShowDX")[0].checked;
|
||||
var showFreq = $("#tableShowFreq")[0].checked;
|
||||
var showMode = $("#tableShowMode")[0].checked;
|
||||
var showComment = $("#tableShowComment")[0].checked;
|
||||
var showBearing = $("#tableShowBearing")[0].checked && userPos != null;
|
||||
var showType = $("#tableShowType")[0].checked;
|
||||
var showRef = $("#tableShowRef")[0].checked;
|
||||
var showDE = $("#tableShowDE")[0].checked;
|
||||
|
||||
// Format dx country
|
||||
var dx_country = s["dx_country"];
|
||||
if (dx_country == null) {
|
||||
dx_country = "Unknown or not a country";
|
||||
}
|
||||
// Create row
|
||||
let $tr = $('<tr>');
|
||||
if (highlightNew) {
|
||||
$tr.addClass("new");
|
||||
}
|
||||
|
||||
// Format DX flag
|
||||
var dx_flag = "<i class='fa-solid fa-globe-africa'></i>";
|
||||
if (s["dx_dxcc_id"] && s["dx_dxcc_id"] != null && s["dx_dxcc_id"] != 0) {
|
||||
dx_flag = `<img src="img/flags/${s['dx_dxcc_id']}.png" class="flag" width="24" alt="${dx_country}" title="${dx_country}"/>`;
|
||||
}
|
||||
// Apply striping to the table. We can't just use Bootstrap's table-striped class because we have all sorts of
|
||||
// extra faff to deal with, like the mobile view having extra rows, and the On Now / Next 24h / Later banners
|
||||
// which cause the table-striped colouring to go awry.
|
||||
if (rowCount % 2 == 1) {
|
||||
$tr.addClass("table-active");
|
||||
}
|
||||
|
||||
// Format the frequency
|
||||
var freq_string = "Unknown"
|
||||
if (s["freq"] != null) {
|
||||
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] : "";
|
||||
freq_string = `<span class='freq-mhz freq-mhz-pad'>${mhz.toFixed(0)}</span><span class='freq-khz'>${khz.toFixed(0).padStart(3, '0')}</span><span class='freq-hz hideonmobile'>${hz_string}</span>`
|
||||
}
|
||||
// Show faded out if QRT
|
||||
if (s["qrt"] == true) {
|
||||
$tr.addClass("table-faded");
|
||||
}
|
||||
|
||||
// Format the mode
|
||||
mode_string = s["mode"];
|
||||
if (s["mode"] == null) {
|
||||
mode_string = "???";
|
||||
}
|
||||
if (s["mode_source"] == "BANDPLAN") {
|
||||
mode_string = mode_string + "<span class='mode-q hideonmobile'><i class='fa-solid fa-circle-question' title='The mode was not reported via the spotting service. This is a guess based on the frequency.'></i></span>";
|
||||
}
|
||||
// Format a UTC or local time for display
|
||||
var time = moment.unix(s["time"]).utc();
|
||||
if (useLocalTime) {
|
||||
time.local();
|
||||
}
|
||||
var time_formatted = time.format("HH:mm");
|
||||
|
||||
// Format comment
|
||||
var commentText = "";
|
||||
if (s["comment"] != null) {
|
||||
commentText = escapeHtml(s["comment"]);
|
||||
}
|
||||
// Format DX call
|
||||
var dx_call = s["dx_call"];
|
||||
if (dx_call == null) {
|
||||
dx_call = "";
|
||||
dx_flag = "";
|
||||
}
|
||||
if (s["dx_ssid"] != null) {
|
||||
dx_call = dx_call + "-" + s["dx_ssid"];
|
||||
}
|
||||
|
||||
// Format bearing text
|
||||
var bearingText = "---<span class='bearing-q hideonmobile'><i class='fa-solid fa-circle-question' title='The position was not reported via the spotting service, and we could not determine one. A bearing to this DX is not available.'></i></span>";
|
||||
if (userPos != null && s["dx_latitude"] != null && s["dx_longitude"] != null) {
|
||||
var bearing = calcBearing(userPos[0], userPos[1], s["dx_latitude"], s["dx_longitude"]);
|
||||
bearingText = bearing.toFixed(0).padStart(3, '0') + "°";
|
||||
if (s["dx_location_good"] == null || s["dx_location_good"] == false) {
|
||||
if (s["dx_location_source"] == "HOME QTH") {
|
||||
bearingText = bearingText + "<span class='bearing-q hideonmobile'><i class='fa-solid fa-circle-question' title='The position was not reported via the spotting service. We had to fall back to a QRZ \"home\" location for a portable/mobile/alternative spot, so this bearing may not be accurate if the DX is close to you..'></i></span>";
|
||||
} else {
|
||||
bearingText = bearingText + "<span class='bearing-q hideonmobile'><i class='fa-solid fa-circle-question' title='The position was not reported via the spotting service. We had to fall back to just using the centre of a DXCC entity, so this bearing may not be accurate if the DX is close to you.'></i></span>";
|
||||
}
|
||||
// Format dx country
|
||||
var dx_country = s["dx_country"];
|
||||
if (dx_country == null) {
|
||||
dx_country = "Unknown or not a country";
|
||||
}
|
||||
|
||||
// Format DX flag
|
||||
var dx_flag = "<i class='fa-solid fa-globe-africa'></i>";
|
||||
if (s["dx_dxcc_id"] && s["dx_dxcc_id"] != null && s["dx_dxcc_id"] != 0) {
|
||||
dx_flag = `<img src="img/flags/${s['dx_dxcc_id']}.png" class="flag" width="24" alt="${dx_country}" title="${dx_country}"/>`;
|
||||
}
|
||||
|
||||
// Format the frequency
|
||||
var freq_string = "Unknown"
|
||||
if (s["freq"] != null) {
|
||||
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] : "";
|
||||
freq_string = `<span class='freq-mhz freq-mhz-pad'>${mhz.toFixed(0)}</span><span class='freq-khz'>${khz.toFixed(0).padStart(3, '0')}</span><span class='freq-hz hideonmobile'>${hz_string}</span>`
|
||||
}
|
||||
|
||||
// Format the mode
|
||||
mode_string = s["mode"];
|
||||
if (s["mode"] == null) {
|
||||
mode_string = "???";
|
||||
}
|
||||
if (s["mode_source"] == "BANDPLAN") {
|
||||
mode_string = mode_string + "<span class='mode-q hideonmobile'><i class='fa-solid fa-circle-question' title='The mode was not reported via the spotting service. This is a guess based on the frequency.'></i></span>";
|
||||
}
|
||||
|
||||
// Format comment
|
||||
var commentText = "";
|
||||
if (s["comment"] != null) {
|
||||
commentText = escapeHtml(s["comment"]);
|
||||
}
|
||||
|
||||
// Format bearing text
|
||||
var bearingText = "---<span class='bearing-q hideonmobile'><i class='fa-solid fa-circle-question' title='The position was not reported via the spotting service, and we could not determine one. A bearing to this DX is not available.'></i></span>";
|
||||
if (userPos != null && s["dx_latitude"] != null && s["dx_longitude"] != null) {
|
||||
var bearing = calcBearing(userPos[0], userPos[1], s["dx_latitude"], s["dx_longitude"]);
|
||||
bearingText = bearing.toFixed(0).padStart(3, '0') + "°";
|
||||
if (s["dx_location_good"] == null || s["dx_location_good"] == false) {
|
||||
if (s["dx_location_source"] == "HOME QTH") {
|
||||
bearingText = bearingText + "<span class='bearing-q hideonmobile'><i class='fa-solid fa-circle-question' title='The position was not reported via the spotting service. We had to fall back to a QRZ \"home\" location for a portable/mobile/alternative spot, so this bearing may not be accurate if the DX is close to you..'></i></span>";
|
||||
} else {
|
||||
bearingText = bearingText + "<span class='bearing-q hideonmobile'><i class='fa-solid fa-circle-question' title='The position was not reported via the spotting service. We had to fall back to just using the centre of a DXCC entity, so this bearing may not be accurate if the DX is close to you.'></i></span>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format "type" (Sig or fallback to source)
|
||||
var typeText = s["source"];
|
||||
if (s["sig"]) {
|
||||
typeText = s["sig"];
|
||||
}
|
||||
// Format "type" (Sig or fallback to source)
|
||||
var typeText = s["source"];
|
||||
if (s["sig"]) {
|
||||
typeText = s["sig"];
|
||||
}
|
||||
|
||||
// Format sig_refs
|
||||
var sig_refs = "";
|
||||
if (s["sig_refs"] != null) {
|
||||
var items = []
|
||||
for (var i = 0; i < s["sig_refs"].length; i++) {
|
||||
if (s["sig_refs"][i]["url"] != null) {
|
||||
items[i] = `<a href='${s["sig_refs"][i]["url"]}' title='${s["sig_refs"][i]["name"]}' target='_new' class='sig-ref-link'>${s["sig_refs"][i]["id"]}</a>`
|
||||
} else {
|
||||
items[i] = `${s["sig_refs"][i]["id"]}`
|
||||
}
|
||||
// Format sig_refs
|
||||
var sig_refs = "";
|
||||
if (s["sig_refs"] != null) {
|
||||
var items = []
|
||||
for (var i = 0; i < s["sig_refs"].length; i++) {
|
||||
if (s["sig_refs"][i]["url"] != null) {
|
||||
items[i] = `<a href='${s["sig_refs"][i]["url"]}' title='${s["sig_refs"][i]["name"]}' target='_new' class='sig-ref-link'>${s["sig_refs"][i]["id"]}</a>`
|
||||
} else {
|
||||
items[i] = `${s["sig_refs"][i]["id"]}`
|
||||
}
|
||||
sig_refs = items.join(", ");
|
||||
}
|
||||
sig_refs = items.join(", ");
|
||||
}
|
||||
|
||||
// Format de country
|
||||
var de_country = s["de_country"];
|
||||
if (de_country == null) {
|
||||
de_country = "Unknown or not a country";
|
||||
}
|
||||
// Format de country
|
||||
var de_country = s["de_country"];
|
||||
if (de_country == null) {
|
||||
de_country = "Unknown or not a country";
|
||||
}
|
||||
|
||||
// Format DE flag
|
||||
var de_flag = "<i class='fa-solid fa-circle-question'></i>";
|
||||
if (s["de_dxcc_id"] && s["de_dxcc_id"] != null && s["de_dxcc_id"] != 0) {
|
||||
de_flag = `<img src="img/flags/${s['de_dxcc_id']}.png" class="flag" width="24" alt="${de_country}" title="${de_country}"/>`;
|
||||
}
|
||||
// Format DE flag
|
||||
var de_flag = "<i class='fa-solid fa-circle-question'></i>";
|
||||
if (s["de_dxcc_id"] && s["de_dxcc_id"] != null && s["de_dxcc_id"] != 0) {
|
||||
de_flag = `<img src="img/flags/${s['de_dxcc_id']}.png" class="flag" width="24" alt="${de_country}" title="${de_country}"/>`;
|
||||
}
|
||||
|
||||
// Format de call
|
||||
var de_call = s["de_call"];
|
||||
if (de_call == null) {
|
||||
de_call = "";
|
||||
de_flag = "";
|
||||
}
|
||||
if (s["de_ssid"] != null) {
|
||||
de_call = de_call + "-" + s["de_ssid"];
|
||||
}
|
||||
// Format de call
|
||||
var de_call = s["de_call"];
|
||||
if (de_call == null) {
|
||||
de_call = "";
|
||||
de_flag = "";
|
||||
}
|
||||
if (s["de_ssid"] != null) {
|
||||
de_call = de_call + "-" + s["de_ssid"];
|
||||
}
|
||||
|
||||
// Format band name
|
||||
var bandFullName = s['band'] ? s['band'] + " band": "Unknown band";
|
||||
// Format band name
|
||||
var bandFullName = s['band'] ? s['band'] + " band": "Unknown band";
|
||||
|
||||
// Populate the row
|
||||
if (showTime) {
|
||||
$tr.append(`<td class='nowrap'>${time_formatted}</td>`);
|
||||
}
|
||||
if (showDX) {
|
||||
$tr.append(`<td class='nowrap'><span class='flag-wrapper hideonmobile' title='${dx_country}'>${dx_flag}</span><a class='dx-link' href='https://qrz.com/db/${s["dx_call"]}' target='_new' title='${s["dx_name"] != null ? s["dx_name"] : ""}'>${dx_call}</a></td>`);
|
||||
}
|
||||
if (showFreq) {
|
||||
$tr.append(`<td class='nowrap'><span class='band-bullet' title='${bandFullName}' style='${(s["freq"] != null) ? "color: " + s["band_color"] : "display: none;"}'>■</span>${freq_string}</td>`);
|
||||
}
|
||||
if (showMode) {
|
||||
$tr.append(`<td class='nowrap'>${mode_string}</td>`);
|
||||
}
|
||||
if (showComment) {
|
||||
$tr.append(`<td class='hideonmobile'>${commentText}</td>`);
|
||||
}
|
||||
if (showBearing) {
|
||||
$tr.append(`<td class='nowrap hideonmobile'>${bearingText}</td>`);
|
||||
}
|
||||
if (showType) {
|
||||
$tr.append(`<td class='nowrap hideonmobile'><span class='icon-wrapper'><i class='fa-solid fa-${s["icon"]}'></i></span> ${typeText}</td>`);
|
||||
}
|
||||
if (showRef) {
|
||||
$tr.append(`<td class='hideonmobile'>${sig_refs}</td>`);
|
||||
}
|
||||
if (showDE) {
|
||||
$tr.append(`<td class='nowrap hideonmobile'><span class='flag-wrapper' title='${de_country}'>${de_flag}</span>${de_call}</td>`);
|
||||
}
|
||||
table.find('tbody').append($tr);
|
||||
// Populate the row
|
||||
if (showTime) {
|
||||
$tr.append(`<td class='nowrap'>${time_formatted}</td>`);
|
||||
}
|
||||
if (showDX) {
|
||||
$tr.append(`<td class='nowrap'><span class='flag-wrapper hideonmobile' title='${dx_country}'>${dx_flag}</span><a class='dx-link' href='https://qrz.com/db/${s["dx_call"]}' target='_new' title='${s["dx_name"] != null ? s["dx_name"] : ""}'>${dx_call}</a></td>`);
|
||||
}
|
||||
if (showFreq) {
|
||||
$tr.append(`<td class='nowrap'><span class='band-bullet' title='${bandFullName}' style='${(s["freq"] != null) ? "color: " + s["band_color"] : "display: none;"}'>■</span>${freq_string}</td>`);
|
||||
}
|
||||
if (showMode) {
|
||||
$tr.append(`<td class='nowrap'>${mode_string}</td>`);
|
||||
}
|
||||
if (showComment) {
|
||||
$tr.append(`<td class='hideonmobile'>${commentText}</td>`);
|
||||
}
|
||||
if (showBearing) {
|
||||
$tr.append(`<td class='nowrap hideonmobile'>${bearingText}</td>`);
|
||||
}
|
||||
if (showType) {
|
||||
$tr.append(`<td class='nowrap hideonmobile'><span class='icon-wrapper'><i class='fa-solid fa-${s["icon"]}'></i></span> ${typeText}</td>`);
|
||||
}
|
||||
if (showRef) {
|
||||
$tr.append(`<td class='hideonmobile'>${sig_refs}</td>`);
|
||||
}
|
||||
if (showDE) {
|
||||
$tr.append(`<td class='nowrap hideonmobile'><span class='flag-wrapper' title='${de_country}'>${de_flag}</span>${de_call}</td>`);
|
||||
}
|
||||
|
||||
// Second row for mobile view only, containing type, ref & comment
|
||||
$tr2 = $("<tr class='hidenotonmobile'>");
|
||||
if (count % 2 == 1) {
|
||||
$tr2.addClass("table-active");
|
||||
}
|
||||
if (s["qrt"] == true) {
|
||||
$tr2.addClass("table-faded");
|
||||
}
|
||||
$td2 = $("<td colspan='100'>");
|
||||
if (showType) {
|
||||
$td2.append(`<span class='icon-wrapper'><i class='fa-solid fa-${s["icon"]}'></i></span> ${typeText} `);
|
||||
}
|
||||
if (showRef) {
|
||||
$td2.append(`${sig_refs} `);
|
||||
}
|
||||
if (showBearing) {
|
||||
$td2.append(` Bearing: ${bearingText} `);
|
||||
}
|
||||
if (showComment) {
|
||||
$td2.append(`<br/>${commentText}`);
|
||||
}
|
||||
$tr2.append($td2);
|
||||
table.find('tbody').append($tr2);
|
||||
// Second row for mobile view only, containing type, ref & comment
|
||||
$tr2 = $("<tr class='hidenotonmobile'>");
|
||||
if (rowCount % 2 == 1) {
|
||||
$tr2.addClass("table-active");
|
||||
}
|
||||
if (s["qrt"] == true) {
|
||||
$tr2.addClass("table-faded");
|
||||
}
|
||||
$td2 = $("<td colspan='100'>");
|
||||
if (showType) {
|
||||
$td2.append(`<span class='icon-wrapper'><i class='fa-solid fa-${s["icon"]}'></i></span> ${typeText} `);
|
||||
}
|
||||
if (showRef) {
|
||||
$td2.append(`${sig_refs} `);
|
||||
}
|
||||
if (showBearing) {
|
||||
$td2.append(` Bearing: ${bearingText} `);
|
||||
}
|
||||
if (showComment) {
|
||||
$td2.append(`<br/>${commentText}`);
|
||||
}
|
||||
$tr2.append($td2);
|
||||
|
||||
count++;
|
||||
});
|
||||
rowCount++;
|
||||
|
||||
// Update DOM
|
||||
$('#table-container').html(table);
|
||||
return [$tr, $tr2];
|
||||
}
|
||||
|
||||
// Load server options. Once a successful callback is made from this, we then query spots and set up the timer to query
|
||||
|
||||
Reference in New Issue
Block a user