mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-02-04 01:04:33 +00:00
Separate colours and icons out of the Spothole API and re-implement them in the client; provide new colour schemes. #88
This commit is contained in:
@@ -243,7 +243,7 @@ function addAlertRowsToTable(tbody, alerts) {
|
||||
$tr.append(`<td class='hideonmobile'>${commentText}</td>`);
|
||||
}
|
||||
if (showSource) {
|
||||
$tr.append(`<td class='nowrap hideonmobile'><span class='icon-wrapper'><i class='fa-solid fa-${a["icon"]}'></i></span> ${sigSourceText}</td>`);
|
||||
$tr.append(`<td class='nowrap hideonmobile'><span class='icon-wrapper'><i class='fa-solid ${sigToIcon(a["sig"], "fa-globe-africa")}'></i></span> ${sigSourceText}</td>`);
|
||||
}
|
||||
if (showRef) {
|
||||
$tr.append(`<td class='hideonmobile'>${sig_refs}</td>`);
|
||||
@@ -257,7 +257,7 @@ function addAlertRowsToTable(tbody, alerts) {
|
||||
}
|
||||
$td2 = $("<td colspan='100'>");
|
||||
if (showSource) {
|
||||
$td2.append(`<span class='icon-wrapper'><i class='fa-solid fa-${a["icon"]}'></i></span> `);
|
||||
$td2.append(`<span class='icon-wrapper'><i class='fa-solid ${sigToIcon(a["sig"], "fa-globe-africa")}'></i></span> `);
|
||||
}
|
||||
if (showRef) {
|
||||
$td2.append(`${sig_refs} `);
|
||||
|
||||
@@ -70,7 +70,7 @@ function updateBands() {
|
||||
var table = $('<table id="bands-table">').append('<thead><tr></tr></thead><tbody><tr></tr></tbody>');
|
||||
bandToSpots.forEach(function (spotList, bandName) {
|
||||
// Get the colours for the band from the first spot, and prepare the header
|
||||
table.find('thead tr').append(`<th style='background-color:${spotList[0].band_color}; color:${spotList[0].band_contrast_color}'>${spotList[0].band}</th>`);
|
||||
table.find('thead tr').append(`<th style='background-color:${bandToColor(spotList[0].band)}; color:${bandToContrastColor(spotList[0].band)}'>${spotList[0].band}</th>`);
|
||||
|
||||
// Get the band data to fetch start and end frequencies
|
||||
let band = options["bands"].filter(function (b) {
|
||||
@@ -145,7 +145,7 @@ function updateBands() {
|
||||
|
||||
// Now each spot is tagged with how far down the div it should go, add them to the DOM.
|
||||
spotList.forEach(s => {
|
||||
bandSpotsDiv.append(`<div class="band-spot" style="top: ${s['pxDownBandLabel']}px; border-top: 1px solid ${s.band_color}; border-left: 5px solid ${s.band_color}; border-bottom: 1px solid ${s.band_color}; border-right: 1px solid ${s.band_color};"><span class="band-spot-call">${s.dx_call}${s.dx_ssid != null ? "-" + s.dx_ssid : ""}</span><span class="band-spot-info">${s.dx_call}${s.dx_ssid != null ? "-" + s.dx_ssid : ""} ${(s.freq/1000000).toFixed(3)} ${s.mode}</span></div>`);
|
||||
bandSpotsDiv.append(`<div class="band-spot" style="top: ${s['pxDownBandLabel']}px; border-top: 1px solid ${bandToColor(s['band'])}; border-left: 5px solid ${bandToColor(s['band'])}; border-bottom: 1px solid ${bandToColor(s['band'])}; border-right: 1px solid ${bandToColor(s['band'])};"><span class="band-spot-call">${s.dx_call}${s.dx_ssid != null ? "-" + s.dx_ssid : ""}</span><span class="band-spot-info">${s.dx_call}${s.dx_ssid != null ? "-" + s.dx_ssid : ""} ${(s.freq/1000000).toFixed(3)} ${s.mode}</span></div>`);
|
||||
});
|
||||
|
||||
// Work out how tall the canvas should be. Normally this is matching the normal band column height, but if some
|
||||
@@ -167,7 +167,7 @@ function updateBands() {
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = 2;
|
||||
ctx.lineCap = "round";
|
||||
ctx.strokeStyle = s.band_color;
|
||||
ctx.strokeStyle = bandToColor(s['band']);
|
||||
ctx.moveTo(0, pxDownBandFreq);
|
||||
ctx.lineTo(BAND_COLUMN_CANVAS_WIDTH_PX, pxDownBandLabel);
|
||||
ctx.stroke();
|
||||
@@ -228,6 +228,21 @@ function loadOptions() {
|
||||
// Store options
|
||||
options = jsonData;
|
||||
|
||||
// Populate the Display panel
|
||||
options["web-ui-options"]["max-spot-age"].forEach(sc => $("#max-spot-age").append($('<option>', {
|
||||
value: sc * 60,
|
||||
text: sc
|
||||
})));
|
||||
$("#max-spot-age").val(options["web-ui-options"]["max-spot-age-default"] * 60);
|
||||
getAvailableBandColorSchemes().forEach(sc => $("#band-color-scheme").append($('<option>', {
|
||||
value: sc,
|
||||
text: sc
|
||||
})));
|
||||
|
||||
// First pass loading settings, so we can load the band colour scheme before the filters that need to use it
|
||||
loadSettings();
|
||||
setBandColorScheme($("#band-color-scheme option:selected").val());
|
||||
|
||||
// Add CSS for band toggle buttons
|
||||
addBandToggleColourCSS(options["bands"]);
|
||||
|
||||
@@ -239,13 +254,6 @@ function loadOptions() {
|
||||
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
||||
generateMultiToggleFilterCard("#source-options", "source", options["spot_sources"]);
|
||||
|
||||
// Populate the Display panel
|
||||
options["web-ui-options"]["max-spot-age"].forEach(sc => $("#max-spot-age").append($('<option>', {
|
||||
value: sc * 60,
|
||||
text: sc
|
||||
})));
|
||||
$("#max-spot-age").val(options["web-ui-options"]["max-spot-age-default"] * 60);
|
||||
|
||||
// Load URL params. These may select things from the various filter & display options, so the function needs
|
||||
// to be called after these are set up, but if the URL params ask for "embedded mode", this will suppress
|
||||
// loading settings, so this needs to be called before that.
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
var options = {};
|
||||
// Last time we updated the spots/alerts list on display.
|
||||
var lastUpdateTime;
|
||||
// Whether "embedded mode" is being used. This removes headers and footers, maximises the remaining content, and
|
||||
// uses URL params to configure the interface options rather than using the user's localstorage.
|
||||
var embeddedMode = false;
|
||||
|
||||
// Load and apply any URL params. This is used for "embedded mode" where another site can embed a version of
|
||||
// Spothole and provide its own interface options rather than using the user's saved ones. These may select things
|
||||
@@ -18,7 +15,7 @@ function loadURLParams() {
|
||||
// top-level html element to use CSS selectors to remove bits of UI.
|
||||
let embedded = params.get("embedded");
|
||||
if (embedded != null && embedded === "true") {
|
||||
embeddedMode = true;
|
||||
useLocalStorage = false;
|
||||
$("html").attr("embedded-mode", "true");
|
||||
}
|
||||
|
||||
@@ -133,27 +130,6 @@ function updateRefreshDisplay() {
|
||||
}
|
||||
}
|
||||
|
||||
// Utility function to escape HTML characters from a string.
|
||||
function escapeHtml(str) {
|
||||
if (typeof str !== 'string') {
|
||||
return '';
|
||||
}
|
||||
|
||||
const escapeCharacter = (match) => {
|
||||
switch (match) {
|
||||
case '&': return '&';
|
||||
case '<': return '<';
|
||||
case '>': return '>';
|
||||
case '"': return '"';
|
||||
case '\'': return ''';
|
||||
case '`': return '`';
|
||||
default: return match;
|
||||
}
|
||||
};
|
||||
|
||||
return str.replace(/[&<>"'`]/g, escapeCharacter);
|
||||
}
|
||||
|
||||
// When the "use local time" field is changed, reload the table and save settings
|
||||
function timeZoneUpdated() {
|
||||
updateTable();
|
||||
@@ -166,106 +142,6 @@ function columnsUpdated() {
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
// Calculate great circle bearing between two lat/lon points.
|
||||
function calcBearing(lat1, lon1, lat2, lon2) {
|
||||
lat1 *= Math.PI / 180;
|
||||
lon1 *= Math.PI / 180;
|
||||
lat2 *= Math.PI / 180;
|
||||
lon2 *= Math.PI / 180;
|
||||
var lonDelta = lon2 - lon1;
|
||||
var y = Math.sin(lonDelta) * Math.cos(lat2);
|
||||
var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(lonDelta);
|
||||
var bearing = Math.atan2(y, x);
|
||||
bearing = bearing * (180 / Math.PI);
|
||||
if ( bearing < 0 ) { bearing += 360; }
|
||||
return bearing;
|
||||
}
|
||||
|
||||
// Convert a Maidenhead grid reference of arbitrary precision to the lat/long of the centre point of the square.
|
||||
// Returns null if the grid format is invalid.
|
||||
function latLonForGridCentre(grid) {
|
||||
let [lat, lon, latCellSize, lonCellSize] = latLonForGridSWCornerPlusSize(grid);
|
||||
if (lat != null && lon != null && latCellSize != null && lonCellSize != null) {
|
||||
return [lat + latCellSize / 2.0, lon + lonCellSize / 2.0];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a Maidenhead grid reference of arbitrary precision to lat/long, including in the result the size of the
|
||||
// lowest grid square. This is a utility method used by the main methods that return the centre, southwest, and
|
||||
// northeast coordinates of a grid square.
|
||||
// The return type is always an array of size 4. The elements in it are null if the grid format is invalid.
|
||||
function latLonForGridSWCornerPlusSize(grid) {
|
||||
// Make sure we are in upper case so our maths works. Case is arbitrary for Maidenhead references
|
||||
grid = grid.toUpperCase();
|
||||
|
||||
// Return null if our Maidenhead string is invalid or too short
|
||||
let len = grid.length;
|
||||
if (len <= 0 || (len % 2) !== 0) {
|
||||
return [null, null, null, null];
|
||||
}
|
||||
|
||||
let lat = 0.0; // aggregated latitude
|
||||
let lon = 0.0; // aggregated longitude
|
||||
let latCellSize = 10; // Size in degrees latitude of the current cell. Starts at 20 and gets smaller as the calculation progresses
|
||||
let lonCellSize = 20; // Size in degrees longitude of the current cell. Starts at 20 and gets smaller as the calculation progresses
|
||||
let latCellNo; // grid latitude cell number this time
|
||||
let lonCellNo; // grid longitude cell number this time
|
||||
|
||||
// Iterate through blocks (two-character sections)
|
||||
for (let block = 0; block * 2 < len; block += 1) {
|
||||
if (block % 2 === 0) {
|
||||
// Letters in this block
|
||||
lonCellNo = grid.charCodeAt(block * 2) - 'A'.charCodeAt(0);
|
||||
latCellNo = grid.charCodeAt(block * 2 + 1) - 'A'.charCodeAt(0);
|
||||
// Bail if the values aren't in range. Allowed values are A-R (0-17) for the first letter block, or
|
||||
// A-X (0-23) thereafter.
|
||||
let maxCellNo = (block === 0) ? 17 : 23;
|
||||
if (latCellNo < 0 || latCellNo > maxCellNo || lonCellNo < 0 || lonCellNo > maxCellNo) {
|
||||
return [null, null, null, null];
|
||||
}
|
||||
} else {
|
||||
// Numbers in this block
|
||||
lonCellNo = parseInt(grid.charAt(block * 2));
|
||||
latCellNo = parseInt(grid.charAt(block * 2 + 1));
|
||||
// Bail if the values aren't in range 0-9..
|
||||
if (latCellNo < 0 || latCellNo > 9 || lonCellNo < 0 || lonCellNo > 9) {
|
||||
return [null, null, null, null];
|
||||
}
|
||||
}
|
||||
|
||||
// Aggregate the angles
|
||||
lat += latCellNo * latCellSize;
|
||||
lon += lonCellNo * lonCellSize;
|
||||
|
||||
// Reduce the cell size for the next block, unless we are on the last cell.
|
||||
if (block * 2 < len - 2) {
|
||||
// Still have more work to do, so reduce the cell size
|
||||
if (block % 2 === 0) {
|
||||
// Just dealt with letters, next block will be numbers so cells will be 1/10 the current size
|
||||
latCellSize = latCellSize / 10.0;
|
||||
lonCellSize = lonCellSize / 10.0;
|
||||
} else {
|
||||
// Just dealt with numbers, next block will be letters so cells will be 1/24 the current size
|
||||
latCellSize = latCellSize / 24.0;
|
||||
lonCellSize = lonCellSize / 24.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Offset back to (-180, -90) where the grid starts
|
||||
lon -= 180.0;
|
||||
lat -= 90.0;
|
||||
|
||||
// Return nulls on maths errors
|
||||
if (isNaN(lat) || isNaN(lon) || isNaN(latCellSize) || isNaN(lonCellSize)) {
|
||||
return [null, null, null, null];
|
||||
}
|
||||
|
||||
return [lat, lon, latCellSize, lonCellSize];
|
||||
}
|
||||
|
||||
// Function to set dark mode on or off
|
||||
function enableDarkMode(dark) {
|
||||
$("html").attr("data-bs-theme", dark ? "dark" : "light");
|
||||
@@ -289,37 +165,6 @@ function usePreferredTheme() {
|
||||
}
|
||||
}
|
||||
|
||||
// Save settings to local storage. Suppressed if "embedded mode" is in use.
|
||||
function saveSettings() {
|
||||
if (!embeddedMode) {
|
||||
// Find all storeable UI elements, store a key of "element id:property name" mapped to the value of that
|
||||
// property. For a checkbox, that's the "checked" property.
|
||||
$(".storeable-checkbox").each(function() {
|
||||
localStorage.setItem("#" + $(this)[0].id + ":checked", JSON.stringify($(this)[0].checked));
|
||||
});
|
||||
$(".storeable-select").each(function() {
|
||||
localStorage.setItem("#" + $(this)[0].id + ":value", JSON.stringify($(this)[0].value));
|
||||
});
|
||||
$(".storeable-text").each(function() {
|
||||
localStorage.setItem("#" + $(this)[0].id + ":value", JSON.stringify($(this)[0].value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Load settings from local storage and set up the filter selectors. Suppressed if "embedded mode" is in use.
|
||||
function loadSettings() {
|
||||
if (!embeddedMode) {
|
||||
// Find all local storage entries and push their data to the corresponding UI element
|
||||
Object.keys(localStorage).forEach(function(key) {
|
||||
if (key.startsWith("#") && key.includes(":")) {
|
||||
// Split the key back into an element ID and a property
|
||||
var split = key.split(":");
|
||||
$(split[0]).prop(split[1], JSON.parse(localStorage.getItem(key)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Startup
|
||||
$(document).ready(function() {
|
||||
usePreferredTheme();
|
||||
|
||||
99
webassets/js/ham-utils/geo.js
Normal file
99
webassets/js/ham-utils/geo.js
Normal file
@@ -0,0 +1,99 @@
|
||||
// Calculate great circle bearing between two lat/lon points.
|
||||
function calcBearing(lat1, lon1, lat2, lon2) {
|
||||
lat1 *= Math.PI / 180;
|
||||
lon1 *= Math.PI / 180;
|
||||
lat2 *= Math.PI / 180;
|
||||
lon2 *= Math.PI / 180;
|
||||
var lonDelta = lon2 - lon1;
|
||||
var y = Math.sin(lonDelta) * Math.cos(lat2);
|
||||
var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(lonDelta);
|
||||
var bearing = Math.atan2(y, x);
|
||||
bearing = bearing * (180 / Math.PI);
|
||||
if ( bearing < 0 ) { bearing += 360; }
|
||||
return bearing;
|
||||
}
|
||||
|
||||
// Convert a Maidenhead grid reference of arbitrary precision to the lat/long of the centre point of the square.
|
||||
// Returns null if the grid format is invalid.
|
||||
function latLonForGridCentre(grid) {
|
||||
let [lat, lon, latCellSize, lonCellSize] = latLonForGridSWCornerPlusSize(grid);
|
||||
if (lat != null && lon != null && latCellSize != null && lonCellSize != null) {
|
||||
return [lat + latCellSize / 2.0, lon + lonCellSize / 2.0];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a Maidenhead grid reference of arbitrary precision to lat/long, including in the result the size of the
|
||||
// lowest grid square. This is a utility method used by the main methods that return the centre, southwest, and
|
||||
// northeast coordinates of a grid square.
|
||||
// The return type is always an array of size 4. The elements in it are null if the grid format is invalid.
|
||||
function latLonForGridSWCornerPlusSize(grid) {
|
||||
// Make sure we are in upper case so our maths works. Case is arbitrary for Maidenhead references
|
||||
grid = grid.toUpperCase();
|
||||
|
||||
// Return null if our Maidenhead string is invalid or too short
|
||||
let len = grid.length;
|
||||
if (len <= 0 || (len % 2) !== 0) {
|
||||
return [null, null, null, null];
|
||||
}
|
||||
|
||||
let lat = 0.0; // aggregated latitude
|
||||
let lon = 0.0; // aggregated longitude
|
||||
let latCellSize = 10; // Size in degrees latitude of the current cell. Starts at 20 and gets smaller as the calculation progresses
|
||||
let lonCellSize = 20; // Size in degrees longitude of the current cell. Starts at 20 and gets smaller as the calculation progresses
|
||||
let latCellNo; // grid latitude cell number this time
|
||||
let lonCellNo; // grid longitude cell number this time
|
||||
|
||||
// Iterate through blocks (two-character sections)
|
||||
for (let block = 0; block * 2 < len; block += 1) {
|
||||
if (block % 2 === 0) {
|
||||
// Letters in this block
|
||||
lonCellNo = grid.charCodeAt(block * 2) - 'A'.charCodeAt(0);
|
||||
latCellNo = grid.charCodeAt(block * 2 + 1) - 'A'.charCodeAt(0);
|
||||
// Bail if the values aren't in range. Allowed values are A-R (0-17) for the first letter block, or
|
||||
// A-X (0-23) thereafter.
|
||||
let maxCellNo = (block === 0) ? 17 : 23;
|
||||
if (latCellNo < 0 || latCellNo > maxCellNo || lonCellNo < 0 || lonCellNo > maxCellNo) {
|
||||
return [null, null, null, null];
|
||||
}
|
||||
} else {
|
||||
// Numbers in this block
|
||||
lonCellNo = parseInt(grid.charAt(block * 2));
|
||||
latCellNo = parseInt(grid.charAt(block * 2 + 1));
|
||||
// Bail if the values aren't in range 0-9..
|
||||
if (latCellNo < 0 || latCellNo > 9 || lonCellNo < 0 || lonCellNo > 9) {
|
||||
return [null, null, null, null];
|
||||
}
|
||||
}
|
||||
|
||||
// Aggregate the angles
|
||||
lat += latCellNo * latCellSize;
|
||||
lon += lonCellNo * lonCellSize;
|
||||
|
||||
// Reduce the cell size for the next block, unless we are on the last cell.
|
||||
if (block * 2 < len - 2) {
|
||||
// Still have more work to do, so reduce the cell size
|
||||
if (block % 2 === 0) {
|
||||
// Just dealt with letters, next block will be numbers so cells will be 1/10 the current size
|
||||
latCellSize = latCellSize / 10.0;
|
||||
lonCellSize = lonCellSize / 10.0;
|
||||
} else {
|
||||
// Just dealt with numbers, next block will be letters so cells will be 1/24 the current size
|
||||
latCellSize = latCellSize / 24.0;
|
||||
lonCellSize = lonCellSize / 24.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Offset back to (-180, -90) where the grid starts
|
||||
lon -= 180.0;
|
||||
lat -= 90.0;
|
||||
|
||||
// Return nulls on maths errors
|
||||
if (isNaN(lat) || isNaN(lon) || isNaN(latCellSize) || isNaN(lonCellSize)) {
|
||||
return [null, null, null, null];
|
||||
}
|
||||
|
||||
return [lat, lon, latCellSize, lonCellSize];
|
||||
}
|
||||
32
webassets/js/ham-utils/storage.js
Normal file
32
webassets/js/ham-utils/storage.js
Normal file
@@ -0,0 +1,32 @@
|
||||
let useLocalStorage = true;
|
||||
|
||||
// Save settings to local storage. Suppressed if "use local storage" is false.
|
||||
function saveSettings() {
|
||||
if (useLocalStorage) {
|
||||
// Find all storeable UI elements, store a key of "element id:property name" mapped to the value of that
|
||||
// property. For a checkbox, that's the "checked" property.
|
||||
$(".storeable-checkbox").each(function() {
|
||||
localStorage.setItem("#" + $(this)[0].id + ":checked", JSON.stringify($(this)[0].checked));
|
||||
});
|
||||
$(".storeable-select").each(function() {
|
||||
localStorage.setItem("#" + $(this)[0].id + ":value", JSON.stringify($(this)[0].value));
|
||||
});
|
||||
$(".storeable-text").each(function() {
|
||||
localStorage.setItem("#" + $(this)[0].id + ":value", JSON.stringify($(this)[0].value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Load settings from local storage and set up the filter selectors. Suppressed if "use local storage" is false.
|
||||
function loadSettings() {
|
||||
if (useLocalStorage) {
|
||||
// Find all local storage entries and push their data to the corresponding UI element
|
||||
Object.keys(localStorage).forEach(function(key) {
|
||||
if (key.startsWith("#") && key.includes(":")) {
|
||||
// Split the key back into an element ID and a property
|
||||
var split = key.split(":");
|
||||
$(split[0]).prop(split[1], JSON.parse(localStorage.getItem(key)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
372
webassets/js/ham-utils/ui.js
Normal file
372
webassets/js/ham-utils/ui.js
Normal file
@@ -0,0 +1,372 @@
|
||||
const BAND_COLOR_SCHEMES = {
|
||||
"PSK Reporter": {
|
||||
"2200m": "#ff4500",
|
||||
"600m": "#1e90ff",
|
||||
"160m": "#7cfc00",
|
||||
"80m": "#e550e5",
|
||||
"60m": "#00008b",
|
||||
"40m": "#5959ff",
|
||||
"30m": "#62d962",
|
||||
"20m": "#f2c40c",
|
||||
"17m": "#f2f261",
|
||||
"15m": "#cca166",
|
||||
"12m": "#b22222",
|
||||
"11m": "#00ff00",
|
||||
"10m": "#ff69b4",
|
||||
"6m": "#FF0000",
|
||||
"5m": "#e0e0e0",
|
||||
"4m": "#cc0044",
|
||||
"2m": "#FF1493",
|
||||
"1.25m": "#CCFF00",
|
||||
"70cm": "#999900",
|
||||
"23cm": "#5AB8C7",
|
||||
"2.4GHz": "#FF7F50",
|
||||
"5.8GHz": "#cc0099",
|
||||
"10GHz": "#696969",
|
||||
"24GHz": "#f3edc6",
|
||||
"47GHz": "#ffe786",
|
||||
"76GHz": "#baf9d8"
|
||||
},
|
||||
"PSK Reporter (Adjusted)": {
|
||||
"2200m": "#ff4500",
|
||||
"600m": "#1e90ff",
|
||||
"160m": "#7cfc00",
|
||||
"80m": "#b33fb3",
|
||||
"60m": "#00008b",
|
||||
"40m": "#5959ff",
|
||||
"30m": "#62d962",
|
||||
"20m": "#f2c40c",
|
||||
"17m": "#f2f261",
|
||||
"15m": "#cca166",
|
||||
"12m": "#b22222",
|
||||
"11m": "#00ff00",
|
||||
"10m": "#ff7eb4",
|
||||
"6m": "#FF0000",
|
||||
"5m": "#e0e0e0",
|
||||
"4m": "#cc0044",
|
||||
"2m": "#FF1493",
|
||||
"1.25m": "#CCFF00",
|
||||
"70cm": "#999900",
|
||||
"23cm": "#5AB8C7",
|
||||
"2.4GHz": "#FF7F50",
|
||||
"5.8GHz": "#cc0099",
|
||||
"10GHz": "#696969",
|
||||
"24GHz": "#f3edc6",
|
||||
"47GHz": "#ffe786",
|
||||
"76GHz": "#baf9d8"
|
||||
},
|
||||
"RBN": {
|
||||
"2200m": "#000000",
|
||||
"600m": "#aaaaaa",
|
||||
"160m": "#ffe000",
|
||||
"80m": "#093F00",
|
||||
"60m": "#777777",
|
||||
"40m": "#ffa500",
|
||||
"30m": "#ff0000",
|
||||
"20m": "#800080",
|
||||
"17m": "#0000ff",
|
||||
"15m": "#444444",
|
||||
"12m": "#00ffff",
|
||||
"11m": "#000000",
|
||||
"10m": "#ff00ff",
|
||||
"6m": "#ffc0cb",
|
||||
"5m": "#000000",
|
||||
"4m": "#a276ff",
|
||||
"2m": "#92FF7F",
|
||||
"1.25m": "#000000",
|
||||
"70cm": "#000000",
|
||||
"23cm": "#000000",
|
||||
"2.4GHz": "#000000",
|
||||
"5.8GHz": "#000000",
|
||||
"10GHz": "#000000",
|
||||
"24GHz": "#000000",
|
||||
"47GHz": "#000000",
|
||||
"76GHz": "#000000"
|
||||
},
|
||||
"Ham Rainbow": {
|
||||
"2200m": "#8e4f37",
|
||||
"600m": "#8e4f37",
|
||||
"160m": "#8e3737",
|
||||
"80m": "#da2f93",
|
||||
"60m": "#792fda",
|
||||
"40m": "#2f4bda",
|
||||
"30m": "#2fdad2",
|
||||
"20m": "#68da2f",
|
||||
"17m": "#dad52f",
|
||||
"15m": "#da832f",
|
||||
"12m": "#da5c2f",
|
||||
"11m": "#8e8e8e",
|
||||
"10m": "#da2f2f",
|
||||
"6m": "#8e377a",
|
||||
"5m": "#8e8e8e",
|
||||
"4m": "#42378e",
|
||||
"2m": "#37748e",
|
||||
"1.25m": "#8e8e8e",
|
||||
"70cm": "#378e65",
|
||||
"23cm": "#8e8e37",
|
||||
"2.4GHz": "#8e6037",
|
||||
"5.8GHz": "#8e6037",
|
||||
"10GHz": "#8e6037",
|
||||
"24GHz": "#8e6037",
|
||||
"47GHz": "#8e6037",
|
||||
"76GHz": "#8e6037"
|
||||
},
|
||||
"Ham Rainbow (Reverse)": {
|
||||
"2200m": "#42378e",
|
||||
"600m": "#42378e",
|
||||
"160m": "#8e377a",
|
||||
"80m": "#da2f2f",
|
||||
"60m": "#da5c2f",
|
||||
"40m": "#da832f",
|
||||
"30m": "#dad52f",
|
||||
"20m": "#68da2f",
|
||||
"17m": "#2fdad2",
|
||||
"15m": "#2f4bda",
|
||||
"12m": "#792fda",
|
||||
"11m": "#8e8e8e",
|
||||
"10m": "#da2f93",
|
||||
"6m": "#8e3737",
|
||||
"5m": "#8e8e8e",
|
||||
"4m": "#8e4f37",
|
||||
"2m": "#8e6037",
|
||||
"1.25m": "#8e8e8e",
|
||||
"70cm": "#8e8e37",
|
||||
"23cm": "#378e65",
|
||||
"2.4GHz": "#37748e",
|
||||
"5.8GHz": "#37748e",
|
||||
"10GHz": "#37748e",
|
||||
"24GHz": "#37748e",
|
||||
"47GHz": "#37748e",
|
||||
"76GHz": "#37748e",
|
||||
},
|
||||
"Kate Morley": {
|
||||
"2200m": "#817",
|
||||
"600m": "#817",
|
||||
"160m": "#817",
|
||||
"80m": "#a35",
|
||||
"60m": "#c66",
|
||||
"40m": "#e94",
|
||||
"30m": "#ed0",
|
||||
"20m": "#9d5",
|
||||
"17m": "#4d8",
|
||||
"15m": "#2cb",
|
||||
"12m": "#0bc",
|
||||
"11m": "#09c",
|
||||
"10m": "#09c",
|
||||
"6m": "#36b",
|
||||
"5m": "#36b",
|
||||
"4m": "#36b",
|
||||
"2m": "#36b",
|
||||
"1.25m": "#36b",
|
||||
"70cm": "#639",
|
||||
"23cm": "#639",
|
||||
"2.4GHz": "#639",
|
||||
"5.8GHz": "#639",
|
||||
"10GHz": "#639",
|
||||
"24GHz": "#639",
|
||||
"47GHz": "#639",
|
||||
"76GHz": "#639",
|
||||
},
|
||||
"ColorBrewer": {
|
||||
"2200m": "#54278f",
|
||||
"600m": "#756bb1",
|
||||
"160m": "#9e9ac8",
|
||||
"80m": "#cbc9e2",
|
||||
"60m": "#08519c",
|
||||
"40m": "#3182bd",
|
||||
"30m": "#6baed6",
|
||||
"20m": "#bdd7e7",
|
||||
"17m": "#006d2c",
|
||||
"15m": "#31a354",
|
||||
"12m": "#74c476",
|
||||
"11m": "#bae4b3",
|
||||
"10m": "#a63603",
|
||||
"6m": "#e6550d",
|
||||
"5m": "#fd8d3c",
|
||||
"4m": "#fdbe85",
|
||||
"2m": "#a50f15",
|
||||
"1.25m": "#de2d26",
|
||||
"70cm": "#fb6a4a",
|
||||
"23cm": "#fcae91",
|
||||
"2.4GHz": "#636363",
|
||||
"5.8GHz": "#636363",
|
||||
"10GHz": "#969696",
|
||||
"24GHz": "#969696",
|
||||
"47GHz": "#cccccc",
|
||||
"76GHz": "#cccccc",
|
||||
},
|
||||
"IWantHue": {
|
||||
"2200m": "#409271",
|
||||
"600m": "#b03ce1",
|
||||
"160m": "#50c640",
|
||||
"80m": "#d545b7",
|
||||
"60m": "#99b936",
|
||||
"40m": "#7260db",
|
||||
"30m": "#60af57",
|
||||
"20m": "#d54788",
|
||||
"17m": "#58c79f",
|
||||
"15m": "#e2462a",
|
||||
"12m": "#49b1d3",
|
||||
"11m": "#df872f",
|
||||
"10m": "#506bb0",
|
||||
"6m": "#c6a639",
|
||||
"5m": "#9554a3",
|
||||
"4m": "#36783c",
|
||||
"2m": "#da405b",
|
||||
"1.25m": "#657527",
|
||||
"70cm": "#8c97e2",
|
||||
"23cm": "#b44f2f",
|
||||
"2.4GHz": "#d386c8",
|
||||
"5.8GHz": "#aaac66",
|
||||
"10GHz": "#9d4760",
|
||||
"24GHz": "#90672c",
|
||||
"47GHz": "#e08086",
|
||||
"76GHz": "#dc9769",
|
||||
},
|
||||
"IWantHue (Color Blind)": {
|
||||
"2200m": "#bf9e3d",
|
||||
"600m": "#9d2fec",
|
||||
"160m": "#79df39",
|
||||
"80m": "#d445db",
|
||||
"60m": "#5dd175",
|
||||
"40m": "#814dd8",
|
||||
"30m": "#d7ce2f",
|
||||
"20m": "#657af1",
|
||||
"17m": "#8cc34a",
|
||||
"15m": "#d635aa",
|
||||
"12m": "#6cbd80",
|
||||
"11m": "#b860c1",
|
||||
"10m": "#e48721",
|
||||
"6m": "#686ccc",
|
||||
"5m": "#d44e2b",
|
||||
"4m": "#51b3db",
|
||||
"2m": "#d74058",
|
||||
"1.25m": "#56c5ad",
|
||||
"70cm": "#d0478d",
|
||||
"23cm": "#708940",
|
||||
"2.4GHz": "#c380c2",
|
||||
"5.8GHz": "#cab775",
|
||||
"10GHz": "#7a7fc2",
|
||||
"24GHz": "#b87148",
|
||||
"47GHz": "#bd678c",
|
||||
"76GHz": "#c3666b",
|
||||
},
|
||||
"Mokole": {
|
||||
"2200m": "#8b4513",
|
||||
"600m": "#006400",
|
||||
"160m": "#808000",
|
||||
"80m": "#483d8b",
|
||||
"60m": "#5f9ea0",
|
||||
"40m": "#000080",
|
||||
"30m": "#9acd32",
|
||||
"20m": "#8b008b",
|
||||
"17m": "#ff0000",
|
||||
"15m": "#ff8c00",
|
||||
"12m": "#ffd700",
|
||||
"11m": "#7fff00",
|
||||
"10m": "#8a2be2",
|
||||
"6m": "#00ff7f",
|
||||
"5m": "#dc143c",
|
||||
"4m": "#00bfff",
|
||||
"2m": "#0000ff",
|
||||
"1.25m": "#d8bfd8",
|
||||
"70cm": "#ff00ff",
|
||||
"23cm": "#1e90ff",
|
||||
"2.4GHz": "#db7093",
|
||||
"5.8GHz": "#f0e68c",
|
||||
"10GHz": "#ff1493",
|
||||
"24GHz": "#ffa07a",
|
||||
"47GHz": "#ee82ee",
|
||||
"76GHz": "#7fffd4",
|
||||
}
|
||||
};
|
||||
let bandColorScheme = "PSK Reporter (Adjusted)";
|
||||
|
||||
// Set the band colour scheme. Returns true if successful, false if the requested scheme was not known
|
||||
function setBandColorScheme(scheme) {
|
||||
let ret = BAND_COLOR_SCHEMES[scheme]
|
||||
if (ret) {
|
||||
bandColorScheme = scheme;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Get the list of known bands
|
||||
function getKnownBands() {
|
||||
return Array.from(Object.keys(BAND_COLOR_SCHEMES[bandColorScheme]));
|
||||
}
|
||||
|
||||
// Get the list of available band colour schemes
|
||||
function getAvailableBandColorSchemes() {
|
||||
return Array.from(Object.keys(BAND_COLOR_SCHEMES));
|
||||
}
|
||||
|
||||
// Band name to colour (in the current colour scheme). If the band is unknown, black will be returned.
|
||||
function bandToColor(band) {
|
||||
let col = (band != null) ? BAND_COLOR_SCHEMES[bandColorScheme][band] : null;
|
||||
if (col) {
|
||||
return col;
|
||||
} else {
|
||||
return "black";
|
||||
}
|
||||
}
|
||||
|
||||
// Band name to contrast colour (in the current colour scheme). This is either black or white, contrasting as well as
|
||||
// possible with the band colour. If the band is unknown, white will be returned.
|
||||
function bandToContrastColor(band) {
|
||||
let tc = tinycolor(bandToColor(band));
|
||||
return tc.isLight() ? "black" : "white";
|
||||
}
|
||||
|
||||
const MODE_TYPE_COLOR_SCHEMES = {
|
||||
"CW": "green",
|
||||
"PHONE": "red",
|
||||
"DATA": "blue"
|
||||
}
|
||||
|
||||
// Mode type (CW, PHONE, DATA) to colour. If the mode type is unknown, black will be returned.
|
||||
function modeTypeToColor(modeType) {
|
||||
let col = (modeType != null) ? MODE_TYPE_COLOR_SCHEMES[modeType.toUpperCase()] : null;
|
||||
if (col) {
|
||||
return col;
|
||||
} else {
|
||||
return "black";
|
||||
}
|
||||
}
|
||||
|
||||
const SIG_ICONS = {
|
||||
"POTA": "fa-tree",
|
||||
"SOTA": "fa-mountain-sun",
|
||||
"WWFF": "fa-seedling",
|
||||
"GMA": "fa-person-hiking",
|
||||
"WWBOTA": "fa-radiation",
|
||||
"HEMA": "fa-mound",
|
||||
"IOTA": "fa-umbrella-beach",
|
||||
"MOTA": "fa-fan",
|
||||
"ARLHS": "fa-tower-observation",
|
||||
"ILLW": "fa-tower-observation",
|
||||
"SIOTA": "fa-wheat-awn",
|
||||
"WCA": "fa-chess-rook",
|
||||
"ZLOTA": "fa-kiwi-bird",
|
||||
"WOTA": "fa-w",
|
||||
"BOTA": "fa-water",
|
||||
"KRMNPA": "fa-earth-oceania",
|
||||
"WAB": "fa-table-cells-large",
|
||||
"WAI": "fa-table-cells-large",
|
||||
"TOTA": "fa-toilet"
|
||||
}
|
||||
|
||||
// Get the Font Awesome icon for a given SIG. If the SIG is unknown, the provided default symbol will be returned
|
||||
function sigToIcon(sig, defaultIcon) {
|
||||
let col = (sig != null) ? SIG_ICONS[sig.toUpperCase()] : null;
|
||||
if (col) {
|
||||
return col;
|
||||
} else {
|
||||
return defaultIcon;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the list of known SIGs
|
||||
function getKnownSIGs() {
|
||||
return Array.from(Object.keys(SIG_ICONS));
|
||||
}
|
||||
20
webassets/js/ham-utils/utils.js
Normal file
20
webassets/js/ham-utils/utils.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// Utility function to escape HTML characters from a string.
|
||||
function escapeHtml(str) {
|
||||
if (typeof str !== 'string') {
|
||||
return '';
|
||||
}
|
||||
|
||||
const escapeCharacter = (match) => {
|
||||
switch (match) {
|
||||
case '&': return '&';
|
||||
case '<': return '<';
|
||||
case '>': return '>';
|
||||
case '"': return '"';
|
||||
case '\'': return ''';
|
||||
case '`': return '`';
|
||||
default: return match;
|
||||
}
|
||||
};
|
||||
|
||||
return str.replace(/[&<>"'`]/g, escapeCharacter);
|
||||
}
|
||||
@@ -45,12 +45,16 @@ function updateMap() {
|
||||
|
||||
// Create geodesics if required
|
||||
if ($("#mapShowGeodesics")[0].checked && s["de_latitude"] != null && s["de_longitude"] != null) {
|
||||
var geodesic = L.geodesic([[s["de_latitude"], s["de_longitude"]], m.getLatLng()], {
|
||||
color: s["band_color"],
|
||||
wrap: false,
|
||||
steps: 5
|
||||
});
|
||||
geodesicsLayer.addLayer(geodesic);
|
||||
try {
|
||||
var geodesic = L.geodesic([[s["de_latitude"], s["de_longitude"]], m.getLatLng()], {
|
||||
color: bandToColor(s['band']),
|
||||
wrap: false,
|
||||
steps: 5
|
||||
});
|
||||
geodesicsLayer.addLayer(geodesic);
|
||||
} catch (e) {
|
||||
// Not sure what causes these but better to continue than to crash out
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -58,9 +62,9 @@ function updateMap() {
|
||||
// Get an icon for a spot, based on its band, using PSK Reporter colours, its program etc.
|
||||
function getIcon(s) {
|
||||
return L.ExtraMarkers.icon({
|
||||
icon: "fa-" + s["icon"],
|
||||
iconColor: s["band_contrast_color"],
|
||||
markerColor: s["band_color"],
|
||||
icon: sigToIcon(s["sig"], "fa-tower-cell"),
|
||||
iconColor: bandToContrastColor(s["band"]),
|
||||
markerColor: bandToColor(s["band"]),
|
||||
shape: 'circle',
|
||||
prefix: 'fa',
|
||||
svg: true
|
||||
@@ -136,7 +140,7 @@ function getTooltipText(s) {
|
||||
ttt += "<br/>";
|
||||
|
||||
// Source / SIG / Ref
|
||||
ttt += `<span class='nowrap'><span class='icon-wrapper'><i class='fa-solid fa-${s["icon"]}'></i></span> ${sigSourceText} ${sig_refs}</span><br/>`;
|
||||
ttt += `<span class='nowrap'><span class='icon-wrapper'><i class='fa-solid ${sigToIcon(s["sig"], "fa-tower-cell")}'></i></span> ${sigSourceText} ${sig_refs}</span><br/>`;
|
||||
|
||||
// Time
|
||||
ttt += `<span class='icon-wrapper'><i class='fa-solid fa-clock markerPopupIcon'></i></span> ${moment.unix(s["time"]).fromNow()}`;
|
||||
@@ -156,6 +160,21 @@ function loadOptions() {
|
||||
// Store options
|
||||
options = jsonData;
|
||||
|
||||
// Populate the Display panel
|
||||
options["web-ui-options"]["max-spot-age"].forEach(sc => $("#max-spot-age").append($('<option>', {
|
||||
value: sc * 60,
|
||||
text: sc
|
||||
})));
|
||||
$("#max-spot-age").val(options["web-ui-options"]["max-spot-age-default"] * 60);
|
||||
getAvailableBandColorSchemes().forEach(sc => $("#band-color-scheme").append($('<option>', {
|
||||
value: sc,
|
||||
text: sc
|
||||
})));
|
||||
|
||||
// First pass loading settings, so we can load the band colour scheme before the filters that need to use it
|
||||
loadSettings();
|
||||
setBandColorScheme($("#band-color-scheme option:selected").val());
|
||||
|
||||
// Add CSS for band toggle buttons
|
||||
addBandToggleColourCSS(options["bands"]);
|
||||
|
||||
@@ -167,13 +186,6 @@ function loadOptions() {
|
||||
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
||||
generateMultiToggleFilterCard("#source-options", "source", options["spot_sources"]);
|
||||
|
||||
// Populate the Display panel
|
||||
options["web-ui-options"]["max-spot-age"].forEach(sc => $("#max-spot-age").append($('<option>', {
|
||||
value: sc * 60,
|
||||
text: sc
|
||||
})));
|
||||
$("#max-spot-age").val(options["web-ui-options"]["max-spot-age-default"] * 60);
|
||||
|
||||
// Load URL params. These may select things from the various filter & display options, so the function needs
|
||||
// to be called after these are set up, but if the URL params ask for "embedded mode", this will suppress
|
||||
// loading settings, so this needs to be called before that.
|
||||
|
||||
@@ -328,7 +328,7 @@ function createNewTableRowsForSpot(s, highlightNew) {
|
||||
$tr.append(`<td class='nowrap'><span class='flag-wrapper' 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>`);
|
||||
$tr.append(`<td class='nowrap'><span class='band-bullet' title='${bandFullName}' style='${(s["freq"] != null) ? "color: " + bandToColor(s["band"]) : "display: none;"}'>■</span>${freq_string}</td>`);
|
||||
}
|
||||
if (showMode) {
|
||||
$tr.append(`<td class='nowrap'>${mode_string}</td>`);
|
||||
@@ -340,7 +340,7 @@ function createNewTableRowsForSpot(s, highlightNew) {
|
||||
$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>`);
|
||||
$tr.append(`<td class='nowrap hideonmobile'><span class='icon-wrapper'><i class='fa-solid ${sigToIcon(s["sig"], "fa-tower-cell")}'></i></span> ${typeText}</td>`);
|
||||
}
|
||||
if (showRef) {
|
||||
$tr.append(`<td class='hideonmobile' style='max-width: 11em;'>${sig_refs}</td>`);
|
||||
@@ -366,7 +366,7 @@ function createNewTableRowsForSpot(s, highlightNew) {
|
||||
$td2 = $("<td colspan='100'>");
|
||||
$td2floatleft = $(`<div style="float: left;">`);
|
||||
if (showType) {
|
||||
$td2floatleft.append(`<span class='icon-wrapper'><i class='fa-solid fa-${s["icon"]}'></i></span> ${typeText} `);
|
||||
$td2floatleft.append(`<span class='icon-wrapper'><i class='fa-solid ${sigToIcon(s["sig"], "fa-tower-cell")}'></i></span> ${typeText} `);
|
||||
}
|
||||
if (showRef) {
|
||||
$td2floatleft.append(`${sig_refs} `);
|
||||
@@ -398,6 +398,21 @@ function loadOptions() {
|
||||
// Store options
|
||||
options = jsonData;
|
||||
|
||||
// Populate the Display panel
|
||||
options["web-ui-options"]["spot-count"].forEach(sc => $("#spots-to-fetch").append($('<option>', {
|
||||
value: sc,
|
||||
text: sc
|
||||
})));
|
||||
$("#spots-to-fetch").val(options["web-ui-options"]["spot-count-default"]);
|
||||
getAvailableBandColorSchemes().forEach(sc => $("#band-color-scheme").append($('<option>', {
|
||||
value: sc,
|
||||
text: sc
|
||||
})));
|
||||
|
||||
// First pass loading settings, so we can load the band colour scheme before the filters that need to use it
|
||||
loadSettings();
|
||||
setBandColorScheme($("#band-color-scheme option:selected").val());
|
||||
|
||||
// Add CSS for band toggle buttons
|
||||
addBandToggleColourCSS(options["bands"]);
|
||||
|
||||
@@ -409,13 +424,6 @@ function loadOptions() {
|
||||
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
||||
generateMultiToggleFilterCard("#source-options", "source", options["spot_sources"]);
|
||||
|
||||
// Populate the Display panel
|
||||
options["web-ui-options"]["spot-count"].forEach(sc => $("#spots-to-fetch").append($('<option>', {
|
||||
value: sc,
|
||||
text: sc
|
||||
})));
|
||||
$("#spots-to-fetch").val(options["web-ui-options"]["spot-count-default"]);
|
||||
|
||||
// Load URL params. These may select things from the various filter & display options, so the function needs
|
||||
// to be called after these are set up, but if the URL params ask for "embedded mode", this will suppress
|
||||
// loading settings, so this needs to be called before that.
|
||||
|
||||
@@ -8,8 +8,8 @@ function addBandToggleColourCSS(band_options) {
|
||||
band_options.forEach(o => {
|
||||
// CSS doesn't like IDs with decimal points in, so we need to replace that
|
||||
var cssFormattedBandName = o['name'] ? o['name'].replace('.', 'p') : "unknown";
|
||||
$style.append(`#filter-button-label-band-${cssFormattedBandName} { border-color: ${o['color']}; color: var(--bs-primary);}`);
|
||||
$style.append(`.btn-check:checked + #filter-button-label-band-${cssFormattedBandName} { background-color: ${o['color']}; color: ${o['contrast_color']};}`);
|
||||
$style.append(`#filter-button-label-band-${cssFormattedBandName} { border-color: ${bandToColor(o['name'])}; color: var(--bs-primary);}`);
|
||||
$style.append(`.btn-check:checked + #filter-button-label-band-${cssFormattedBandName} { background-color: ${bandToColor(o['name'])}; color: ${bandToContrastColor(o['name'])};}`);
|
||||
});
|
||||
$('html > head').append($style);
|
||||
}
|
||||
@@ -32,7 +32,7 @@ function generateBandsMultiToggleFilterCard(band_options) {
|
||||
function setHamHFBandToggles() {
|
||||
const hamHFBands = ["160m", "80m", "60m", "40m", "30m", "20m", "17m", "15m", "12m", "10m", "6m"];
|
||||
$(".filter-button-band").each(function() {
|
||||
$(this).prop('checked', hamHFBands.includes($(this).attr('id').replace("filter-button-band-", "")));
|
||||
$(this).prop('checked', hamHFBands.includes($(this).val().replace("filter-button-band-", "")));
|
||||
});
|
||||
filtersUpdated();
|
||||
}
|
||||
@@ -41,7 +41,7 @@ function setHamHFBandToggles() {
|
||||
function generateSIGsMultiToggleFilterCard(sig_options) {
|
||||
// Create a button for each option
|
||||
sig_options.forEach(o => {
|
||||
$("#sig-options").append(`<input type="checkbox" class="btn-check filter-button-sig storeable-checkbox" name="options" id="filter-button-sig-${o['name']}" value="${o['name']}" autocomplete="off" onClick="filtersUpdated()" checked><label class="btn btn-outline-primary" id="filter-button-label-sig-${o['name']}" for="filter-button-sig-${o['name']}" title="${o['description']}"><i class="fa-solid fa-${o['icon']}"></i> ${o['name']}</label> `);
|
||||
$("#sig-options").append(`<input type="checkbox" class="btn-check filter-button-sig storeable-checkbox" name="options" id="filter-button-sig-${o['name']}" value="${o['name']}" autocomplete="off" onClick="filtersUpdated()" checked><label class="btn btn-outline-primary" id="filter-button-label-sig-${o['name']}" for="filter-button-sig-${o['name']}" title="${o['description']}"><i class="fa-solid ${sigToIcon(o['name'], 'fa-tower-cell')}"></i> ${o['name']}</label> `);
|
||||
});
|
||||
// Create a bonus "NO_SIG" / "General DX" option
|
||||
$("#sig-options").append(`<input type="checkbox" class="btn-check filter-button-sig storeable-checkbox" name="options" id="filter-button-sig-NO_SIG" value="NO_SIG" autocomplete="off" onClick="filtersUpdated()" checked><label class="btn btn-outline-primary" id="filter-button-label-sig-NO_SIG" for="filter-button-sig-NO_SIG"><i class="fa-solid fa-tower-cell"></i> General DX</label> `);
|
||||
@@ -61,6 +61,14 @@ function toggleDarkMode() {
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
// Function to update the band colour scheme in spots, bands and map pages
|
||||
function setBandColorSchemeFromUI() {
|
||||
setBandColorScheme($("#band-color-scheme option:selected").val());
|
||||
saveSettings();
|
||||
// Fudge a full reload because we need to update not just colours in the list/map/bands but also the filters
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
// Reload spots on becoming visible. This forces a refresh when used as a PWA and the user switches back to the PWA
|
||||
// after some time has passed with it in the background.
|
||||
addEventListener("visibilitychange", (event) => {
|
||||
|
||||
Reference in New Issue
Block a user