mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-02-04 09:14:30 +00:00
Style improvements and fixes #3
This commit is contained in:
@@ -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