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:
@@ -76,7 +76,6 @@ class NG3K(HTTPAlertProvider):
|
|||||||
dx_country=dx_country,
|
dx_country=dx_country,
|
||||||
freqs_modes=bands + (("; " + modes) if modes != "" else ""),
|
freqs_modes=bands + (("; " + modes) if modes != "" else ""),
|
||||||
comment=by + "; " + comment + "; " + qsl_info,
|
comment=by + "; " + comment + "; " + qsl_info,
|
||||||
icon="globe-africa",
|
|
||||||
start_time=start_timestamp,
|
start_time=start_timestamp,
|
||||||
end_time=end_timestamp,
|
end_time=end_timestamp,
|
||||||
is_dxpedition=True)
|
is_dxpedition=True)
|
||||||
|
|||||||
@@ -12,25 +12,25 @@ HAMQTH_PRG = (SOFTWARE_NAME + " v" + SOFTWARE_VERSION + " operated by " + SERVER
|
|||||||
|
|
||||||
# Special Interest Groups
|
# Special Interest Groups
|
||||||
SIGS = [
|
SIGS = [
|
||||||
SIG(name="POTA", description="Parks on the Air", icon="tree", ref_regex=r"[A-Z]{2}\-\d{4,5}"),
|
SIG(name="POTA", description="Parks on the Air", ref_regex=r"[A-Z]{2}\-\d{4,5}"),
|
||||||
SIG(name="SOTA", description="Summits on the Air", icon="mountain-sun", ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{2}\-\d{3}"),
|
SIG(name="SOTA", description="Summits on the Air", ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{2}\-\d{3}"),
|
||||||
SIG(name="WWFF", description="World Wide Flora & Fauna", icon="seedling", ref_regex=r"[A-Z0-9]{1,3}FF\-\d{4}"),
|
SIG(name="WWFF", description="World Wide Flora & Fauna", ref_regex=r"[A-Z0-9]{1,3}FF\-\d{4}"),
|
||||||
SIG(name="GMA", description="Global Mountain Activity", icon="person-hiking", ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{2}\-\d{3}"),
|
SIG(name="GMA", description="Global Mountain Activity", ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{2}\-\d{3}"),
|
||||||
SIG(name="WWBOTA", description="Worldwide Bunkers on the Air", icon="radiation", ref_regex=r"B\/[A-Z0-9]{1,3}\-\d{3,4}"),
|
SIG(name="WWBOTA", description="Worldwide Bunkers on the Air", ref_regex=r"B\/[A-Z0-9]{1,3}\-\d{3,4}"),
|
||||||
SIG(name="HEMA", description="HuMPs Excluding Marilyns Award", icon="mound", ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{3}\-\d{3}"),
|
SIG(name="HEMA", description="HuMPs Excluding Marilyns Award", ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{3}\-\d{3}"),
|
||||||
SIG(name="IOTA", description="Islands on the Air", icon="umbrella-beach", ref_regex=r"[A-Z]{2}\-\d{3}"),
|
SIG(name="IOTA", description="Islands on the Air", ref_regex=r"[A-Z]{2}\-\d{3}"),
|
||||||
SIG(name="MOTA", description="Mills on the Air", icon="fan", ref_regex=r"X\d{4-6}"),
|
SIG(name="MOTA", description="Mills on the Air", ref_regex=r"X\d{4-6}"),
|
||||||
SIG(name="ARLHS", description="Amateur Radio Lighthouse Society", icon="tower-observation", ref_regex=r"[A-Z]{3}\-\d{3,4}"),
|
SIG(name="ARLHS", description="Amateur Radio Lighthouse Society", ref_regex=r"[A-Z]{3}\-\d{3,4}"),
|
||||||
SIG(name="ILLW", description="International Lighthouse & Lightship Weekend", icon="tower-observation", ref_regex=r"[A-Z]{2}\d{4}"),
|
SIG(name="ILLW", description="International Lighthouse & Lightship Weekend", ref_regex=r"[A-Z]{2}\d{4}"),
|
||||||
SIG(name="SIOTA", description="Silos on the Air", icon="wheat-awn", ref_regex=r"[A-Z]{2}\-[A-Z]{3}\d"),
|
SIG(name="SIOTA", description="Silos on the Air", ref_regex=r"[A-Z]{2}\-[A-Z]{3}\d"),
|
||||||
SIG(name="WCA", description="World Castles Award", icon="chess-rook", ref_regex=r"[A-Z0-9]{1,3}\-\d{5}"),
|
SIG(name="WCA", description="World Castles Award", ref_regex=r"[A-Z0-9]{1,3}\-\d{5}"),
|
||||||
SIG(name="ZLOTA", description="New Zealand on the Air", icon="kiwi-bird", ref_regex=r"ZL[A-Z]/[A-Z]{2}\-\d{3,4}"),
|
SIG(name="ZLOTA", description="New Zealand on the Air", ref_regex=r"ZL[A-Z]/[A-Z]{2}\-\d{3,4}"),
|
||||||
SIG(name="WOTA", description="Wainwrights on the Air", icon="w", ref_regex=r"[A-Z]{3}-[0-9]{2}"),
|
SIG(name="WOTA", description="Wainwrights on the Air", ref_regex=r"[A-Z]{3}-[0-9]{2}"),
|
||||||
SIG(name="BOTA", description="Beaches on the Air", icon="water"),
|
SIG(name="BOTA", description="Beaches on the Air"),
|
||||||
SIG(name="KRMNPA", description="Keith Roget Memorial National Parks Award", icon="earth-oceania"),
|
SIG(name="KRMNPA", description="Keith Roget Memorial National Parks Award"),
|
||||||
SIG(name="WAB", description="Worked All Britain", icon="table-cells-large", ref_regex=r"[A-Z]{1,2}[0-9]{2}"),
|
SIG(name="WAB", description="Worked All Britain", ref_regex=r"[A-Z]{1,2}[0-9]{2}"),
|
||||||
SIG(name="WAI", description="Worked All Ireland", icon="table-cells-large", ref_regex=r"[A-Z][0-9]{2}"),
|
SIG(name="WAI", description="Worked All Ireland", ref_regex=r"[A-Z][0-9]{2}"),
|
||||||
SIG(name="TOTA", description="Toilets on the Air", icon="toilet", ref_regex=r"T\-[0-9]{2}")
|
SIG(name="TOTA", description="Toilets on the Air", ref_regex=r"T\-[0-9]{2}")
|
||||||
]
|
]
|
||||||
|
|
||||||
# Modes. Note "DIGI" and "DIGITAL" are also supported but are normalised into "DATA".
|
# Modes. Note "DIGI" and "DIGITAL" are also supported but are normalised into "DATA".
|
||||||
@@ -42,33 +42,33 @@ MODE_TYPES = ["CW", "PHONE", "DATA"]
|
|||||||
|
|
||||||
# Band definitions
|
# Band definitions
|
||||||
BANDS = [
|
BANDS = [
|
||||||
Band(name="2200m", start_freq=135700, end_freq=137800, color="#ff4500", contrast_color="white"),
|
Band(name="2200m", start_freq=135700, end_freq=137800),
|
||||||
Band(name="600m", start_freq=472000, end_freq=479000, color="#1e90ff", contrast_color="white"),
|
Band(name="600m", start_freq=472000, end_freq=479000),
|
||||||
Band(name="160m", start_freq=1800000, end_freq=2000000, color="#7cfc00", contrast_color="black"),
|
Band(name="160m", start_freq=1800000, end_freq=2000000),
|
||||||
Band(name="80m", start_freq=3500000, end_freq=4000000, color="#e550e5", contrast_color="black"),
|
Band(name="80m", start_freq=3500000, end_freq=4000000),
|
||||||
Band(name="60m", start_freq=5250000, end_freq=5410000, color="#00008b", contrast_color="white"),
|
Band(name="60m", start_freq=5250000, end_freq=5410000),
|
||||||
Band(name="40m", start_freq=7000000, end_freq=7300000, color="#5959ff", contrast_color="white"),
|
Band(name="40m", start_freq=7000000, end_freq=7300000),
|
||||||
Band(name="30m", start_freq=10100000, end_freq=10150000, color="#62d962", contrast_color="black"),
|
Band(name="30m", start_freq=10100000, end_freq=10150000),
|
||||||
Band(name="20m", start_freq=14000000, end_freq=14350000, color="#f2c40c", contrast_color="black"),
|
Band(name="20m", start_freq=14000000, end_freq=14350000),
|
||||||
Band(name="17m", start_freq=18068000, end_freq=18168000, color="#f2f261", contrast_color="black"),
|
Band(name="17m", start_freq=18068000, end_freq=18168000),
|
||||||
Band(name="15m", start_freq=21000000, end_freq=21450000, color="#cca166", contrast_color="black"),
|
Band(name="15m", start_freq=21000000, end_freq=21450000),
|
||||||
Band(name="12m", start_freq=24890000, end_freq=24990000, color="#b22222", contrast_color="white"),
|
Band(name="12m", start_freq=24890000, end_freq=24990000),
|
||||||
Band(name="11m", start_freq=26965000, end_freq=27405000, color="#00ff00", contrast_color="black"),
|
Band(name="11m", start_freq=26965000, end_freq=27405000),
|
||||||
Band(name="10m", start_freq=28000000, end_freq=29700000, color="#ff69b4", contrast_color="black"),
|
Band(name="10m", start_freq=28000000, end_freq=29700000),
|
||||||
Band(name="6m", start_freq=50000000, end_freq=54000000, color="#FF0000", contrast_color="white"),
|
Band(name="6m", start_freq=50000000, end_freq=54000000),
|
||||||
Band(name="5m", start_freq=56000000, end_freq=60500000, color="#e0e0e0", contrast_color="black"),
|
Band(name="5m", start_freq=56000000, end_freq=60500000),
|
||||||
Band(name="4m", start_freq=70000000, end_freq=70500000, color="#cc0044", contrast_color="white"),
|
Band(name="4m", start_freq=70000000, end_freq=70500000),
|
||||||
Band(name="2m", start_freq=144000000, end_freq=148000000, color="#FF1493", contrast_color="black"),
|
Band(name="2m", start_freq=144000000, end_freq=148000000),
|
||||||
Band(name="1.25m", start_freq=219000000, end_freq=225000000, color="#CCFF00", contrast_color="black"),
|
Band(name="1.25m", start_freq=219000000, end_freq=225000000),
|
||||||
Band(name="70cm", start_freq=420000000, end_freq=450000000, color="#999900", contrast_color="white"),
|
Band(name="70cm", start_freq=420000000, end_freq=450000000),
|
||||||
Band(name="23cm", start_freq=1240000000, end_freq=1325000000, color="#5AB8C7", contrast_color="black"),
|
Band(name="23cm", start_freq=1240000000, end_freq=1325000000),
|
||||||
Band(name="2.4GHz", start_freq=2300000000, end_freq=2450000000, color="#FF7F50", contrast_color="black"),
|
Band(name="2.4GHz", start_freq=2300000000, end_freq=2450000000),
|
||||||
Band(name="5.8GHz", start_freq=5725000000, end_freq=5850000000, color="#cc0099", contrast_color="white"),
|
Band(name="5.8GHz", start_freq=5725000000, end_freq=5850000000),
|
||||||
Band(name="10GHz", start_freq=10000000000, end_freq=10500000000, color="#696969", contrast_color="white"),
|
Band(name="10GHz", start_freq=10000000000, end_freq=10500000000),
|
||||||
Band(name="24GHz", start_freq=24000000000, end_freq=24050000000, color="#f3edc6", contrast_color="black"),
|
Band(name="24GHz", start_freq=24000000000, end_freq=24050000000),
|
||||||
Band(name="47GHz", start_freq=47000000000, end_freq=47200000000, color="#ffe786", contrast_color="black"),
|
Band(name="47GHz", start_freq=47000000000, end_freq=47200000000),
|
||||||
Band(name="76GHz", start_freq=75500000000, end_freq=81500000000, color="#baf9d8", contrast_color="black")]
|
Band(name="76GHz", start_freq=75500000000, end_freq=81500000000)]
|
||||||
UNKNOWN_BAND = Band(name="Unknown", start_freq=0, end_freq=0, color="black", contrast_color="white")
|
UNKNOWN_BAND = Band(name="Unknown", start_freq=0, end_freq=0)
|
||||||
|
|
||||||
# Continents
|
# Continents
|
||||||
CONTINENTS = ["EU", "NA", "SA", "AS", "AF", "OC", "AN"]
|
CONTINENTS = ["EU", "NA", "SA", "AS", "AF", "OC", "AN"]
|
||||||
|
|||||||
@@ -8,14 +8,6 @@ from core.constants import SIGS, HTTP_HEADERS
|
|||||||
from core.geo_utils import wab_wai_square_to_lat_lon
|
from core.geo_utils import wab_wai_square_to_lat_lon
|
||||||
|
|
||||||
|
|
||||||
# Utility function to get the icon for a named SIG. If no match is found, the "circle-question" icon will be returned.
|
|
||||||
def get_icon_for_sig(sig):
|
|
||||||
for s in SIGS:
|
|
||||||
if s.name == sig:
|
|
||||||
return s.icon
|
|
||||||
return "circle-question"
|
|
||||||
|
|
||||||
|
|
||||||
# Utility function to get the regex string for a SIG reference for a named SIG. If no match is found, None will be returned.
|
# Utility function to get the regex string for a SIG reference for a named SIG. If no match is found, None will be returned.
|
||||||
def get_ref_regex_for_sig(sig):
|
def get_ref_regex_for_sig(sig):
|
||||||
for s in SIGS:
|
for s in SIGS:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from datetime import datetime, timedelta
|
|||||||
import pytz
|
import pytz
|
||||||
|
|
||||||
from core.lookup_helper import lookup_helper
|
from core.lookup_helper import lookup_helper
|
||||||
from core.sig_utils import get_icon_for_sig, populate_sig_ref_info
|
from core.sig_utils import populate_sig_ref_info
|
||||||
|
|
||||||
|
|
||||||
# Data class that defines an alert.
|
# Data class that defines an alert.
|
||||||
@@ -55,8 +55,6 @@ class Alert:
|
|||||||
sig_refs: list = None
|
sig_refs: list = None
|
||||||
# Activation score. SOTA only
|
# Activation score. SOTA only
|
||||||
activation_score: int = None
|
activation_score: int = None
|
||||||
# Icon, from the Font Awesome set. This is fairly opinionated but is here to help the alerthole web UI and Field alertter. Does not include the "fa-" prefix.
|
|
||||||
icon: str = None
|
|
||||||
# Whether this alert is for a DXpedition, as opposed to e.g. an xOTA programme.
|
# Whether this alert is for a DXpedition, as opposed to e.g. an xOTA programme.
|
||||||
is_dxpedition: bool = False
|
is_dxpedition: bool = False
|
||||||
# Where we got the alert from, e.g. "POTA", "SOTA"...
|
# Where we got the alert from, e.g. "POTA", "SOTA"...
|
||||||
@@ -109,10 +107,6 @@ class Alert:
|
|||||||
if self.sig_refs and len(self.sig_refs) > 0 and not self.sig:
|
if self.sig_refs and len(self.sig_refs) > 0 and not self.sig:
|
||||||
self.sig = self.sig_refs[0].sig
|
self.sig = self.sig_refs[0].sig
|
||||||
|
|
||||||
# Icon from SIG
|
|
||||||
if self.sig and not self.icon:
|
|
||||||
self.icon = get_icon_for_sig(self.sig)
|
|
||||||
|
|
||||||
# DX operator details lookup, using QRZ.com. This should be the last resort compared to taking the data from
|
# DX operator details lookup, using QRZ.com. This should be the last resort compared to taking the data from
|
||||||
# the actual alertting service, e.g. we don't want to accidentally use a user's QRZ.com home lat/lon instead of
|
# the actual alertting service, e.g. we don't want to accidentally use a user's QRZ.com home lat/lon instead of
|
||||||
# the one from the park reference they're at.
|
# the one from the park reference they're at.
|
||||||
|
|||||||
@@ -8,8 +8,4 @@ class Band:
|
|||||||
# Start frequency, in Hz
|
# Start frequency, in Hz
|
||||||
start_freq: float
|
start_freq: float
|
||||||
# Stop frequency, in Hz
|
# Stop frequency, in Hz
|
||||||
end_freq: float
|
end_freq: float
|
||||||
# Colour to use for this band, as per PSK Reporter
|
|
||||||
color: str
|
|
||||||
# Contrast colour to use for text against a background of the band colour
|
|
||||||
contrast_color: str
|
|
||||||
@@ -7,8 +7,5 @@ class SIG:
|
|||||||
name: str
|
name: str
|
||||||
# Description, e.g. "Parks on the Air"
|
# Description, e.g. "Parks on the Air"
|
||||||
description: str
|
description: str
|
||||||
# Icon to use for it, from the Font Awesome set. This is fairly opinionated but is here to help the Spothole web UI
|
|
||||||
# and Field Spotter. Does not include the "fa-" prefix.
|
|
||||||
icon: str
|
|
||||||
# Regex matcher for references, e.g. for POTA r"[A-Z]{2}\-\d+".
|
# Regex matcher for references, e.g. for POTA r"[A-Z]{2}\-\d+".
|
||||||
ref_regex: str = None
|
ref_regex: str = None
|
||||||
22
data/spot.py
22
data/spot.py
@@ -11,7 +11,7 @@ from pyhamtools.locator import locator_to_latlong, latlong_to_locator
|
|||||||
|
|
||||||
from core.config import MAX_SPOT_AGE
|
from core.config import MAX_SPOT_AGE
|
||||||
from core.lookup_helper import lookup_helper
|
from core.lookup_helper import lookup_helper
|
||||||
from core.sig_utils import get_icon_for_sig, populate_sig_ref_info, ANY_SIG_REGEX, get_ref_regex_for_sig
|
from core.sig_utils import populate_sig_ref_info, ANY_SIG_REGEX, get_ref_regex_for_sig
|
||||||
from data.sig_ref import SIGRef
|
from data.sig_ref import SIGRef
|
||||||
|
|
||||||
|
|
||||||
@@ -109,16 +109,6 @@ class Spot:
|
|||||||
# Activation score. SOTA only
|
# Activation score. SOTA only
|
||||||
activation_score: int = None
|
activation_score: int = None
|
||||||
|
|
||||||
# Display guidance (optional)
|
|
||||||
|
|
||||||
# Icon, from the Font Awesome set. This is fairly opinionated but is here to help the Spothole web UI and Field
|
|
||||||
# Spotter. Does not include the "fa-" prefix.
|
|
||||||
icon: str = None
|
|
||||||
# Colour to represent this spot, if a client chooses to colour spots based on their frequency band, using PSK
|
|
||||||
# Reporter's default colours. HTML colour e.g. hex. A contrast colour is also provided which will be black or white.
|
|
||||||
band_color: str = None
|
|
||||||
band_contrast_color: str = None
|
|
||||||
|
|
||||||
# Timing info
|
# Timing info
|
||||||
|
|
||||||
# Time of the spot, UTC seconds since UNIX epoch
|
# Time of the spot, UTC seconds since UNIX epoch
|
||||||
@@ -214,8 +204,6 @@ class Spot:
|
|||||||
if self.freq and not self.band:
|
if self.freq and not self.band:
|
||||||
band = lookup_helper.infer_band_from_freq(self.freq)
|
band = lookup_helper.infer_band_from_freq(self.freq)
|
||||||
self.band = band.name
|
self.band = band.name
|
||||||
self.band_color = band.color
|
|
||||||
self.band_contrast_color = band.contrast_color
|
|
||||||
|
|
||||||
# Mode from comments or bandplan
|
# Mode from comments or bandplan
|
||||||
if self.mode:
|
if self.mode:
|
||||||
@@ -296,14 +284,6 @@ class Spot:
|
|||||||
if self.sig_refs and len(self.sig_refs) > 0 and not self.sig:
|
if self.sig_refs and len(self.sig_refs) > 0 and not self.sig:
|
||||||
self.sig = self.sig_refs[0].sig
|
self.sig = self.sig_refs[0].sig
|
||||||
|
|
||||||
# Icon from SIG if we have one
|
|
||||||
if self.sig:
|
|
||||||
self.icon = get_icon_for_sig(self.sig)
|
|
||||||
|
|
||||||
# Default "radio" icon if nothing else has set it
|
|
||||||
if not self.icon:
|
|
||||||
self.icon = "tower-cell"
|
|
||||||
|
|
||||||
# DX Grid to lat/lon and vice versa in case one is missing
|
# DX Grid to lat/lon and vice versa in case one is missing
|
||||||
if self.dx_grid and not self.dx_latitude:
|
if self.dx_grid and not self.dx_latitude:
|
||||||
ll = locator_to_latlong(self.dx_grid)
|
ll = locator_to_latlong(self.dx_grid)
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ class APRSIS(SpotProvider):
|
|||||||
comment=data["comment"] if "comment" in data else None,
|
comment=data["comment"] if "comment" in data else None,
|
||||||
dx_latitude=data["latitude"] if "latitude" in data else None,
|
dx_latitude=data["latitude"] if "latitude" in data else None,
|
||||||
dx_longitude=data["longitude"] if "longitude" in data else None,
|
dx_longitude=data["longitude"] if "longitude" in data else None,
|
||||||
icon="tower-cell",
|
|
||||||
time=datetime.now(pytz.UTC).timestamp()) # APRS-IS spots are live so we can assume spot time is "now"
|
time=datetime.now(pytz.UTC).timestamp()) # APRS-IS spots are live so we can assume spot time is "now"
|
||||||
|
|
||||||
# Add to our list
|
# Add to our list
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ class DXCluster(SpotProvider):
|
|||||||
de_call=match.group(1),
|
de_call=match.group(1),
|
||||||
freq=float(match.group(2)) * 1000,
|
freq=float(match.group(2)) * 1000,
|
||||||
comment=match.group(4).strip(),
|
comment=match.group(4).strip(),
|
||||||
icon="tower-cell",
|
|
||||||
time=spot_datetime.timestamp())
|
time=spot_datetime.timestamp())
|
||||||
|
|
||||||
# Add to our list
|
# Add to our list
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ class RBN(SpotProvider):
|
|||||||
de_call=match.group(1),
|
de_call=match.group(1),
|
||||||
freq=float(match.group(2)) * 1000,
|
freq=float(match.group(2)) * 1000,
|
||||||
comment=match.group(4).strip(),
|
comment=match.group(4).strip(),
|
||||||
icon="tower-cell",
|
|
||||||
time=spot_datetime.timestamp())
|
time=spot_datetime.timestamp())
|
||||||
|
|
||||||
# Add to our list
|
# Add to our list
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ class UKPacketNet(HTTPSpotProvider):
|
|||||||
freq=freq,
|
freq=freq,
|
||||||
mode="PKT",
|
mode="PKT",
|
||||||
comment=comment,
|
comment=comment,
|
||||||
icon="tower-cell",
|
|
||||||
time=datetime.strptime(heard["lastHeard"], "%Y-%m-%d %H:%M:%S").replace(tzinfo=pytz.UTC).timestamp(),
|
time=datetime.strptime(heard["lastHeard"], "%Y-%m-%d %H:%M:%S").replace(tzinfo=pytz.UTC).timestamp(),
|
||||||
de_grid=node["location"]["locator"] if "locator" in node["location"] else None,
|
de_grid=node["location"]["locator"] if "locator" in node["location"] else None,
|
||||||
de_latitude=node["location"]["coords"]["lat"],
|
de_latitude=node["location"]["coords"]["lat"],
|
||||||
|
|||||||
@@ -63,7 +63,11 @@
|
|||||||
<p>This software is dedicated to the memory of Tom G1PJB, SK, a friend and colleague who sadly passed away around the time I started writing it in Autumn 2025. I was looking forward to showing it to you when it was done.</p>
|
<p>This software is dedicated to the memory of Tom G1PJB, SK, a friend and colleague who sadly passed away around the time I started writing it in Autumn 2025. I was looking forward to showing it to you when it was done.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/js/common.js?v=3"></script>
|
<script src="/js/ham-utils/utils.js?v=5"></script>
|
||||||
|
<script src="/js/ham-utils/storage.js?v=5"></script>
|
||||||
|
<script src="/js/ham-utils/ui.js?v=5"></script>
|
||||||
|
<script src="/js/ham-utils/geo.js?v=5"></script>
|
||||||
|
<script src="/js/common.js?v=5"></script>
|
||||||
<script>$(document).ready(function() { $("#nav-link-about").addClass("active"); }); <!-- highlight active page in nav --></script>
|
<script>$(document).ready(function() { $("#nav-link-about").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
@@ -69,8 +69,12 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/js/common.js?v=3"></script>
|
<script src="/js/ham-utils/utils.js?v=5"></script>
|
||||||
<script src="/js/add-spot.js?v=3"></script>
|
<script src="/js/ham-utils/storage.js?v=5"></script>
|
||||||
|
<script src="/js/ham-utils/ui.js?v=5"></script>
|
||||||
|
<script src="/js/ham-utils/geo.js?v=5"></script>
|
||||||
|
<script src="/js/common.js?v=5"></script>
|
||||||
|
<script src="/js/add-spot.js?v=5"></script>
|
||||||
<script>$(document).ready(function() { $("#nav-link-add-spot").addClass("active"); }); <!-- highlight active page in nav --></script>
|
<script>$(document).ready(function() { $("#nav-link-add-spot").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
@@ -168,8 +168,12 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/js/common.js?v=3"></script>
|
<script src="/js/ham-utils/utils.js?v=5"></script>
|
||||||
<script src="/js/alerts.js?v=3"></script>
|
<script src="/js/ham-utils/storage.js?v=5"></script>
|
||||||
|
<script src="/js/ham-utils/ui.js?v=5"></script>
|
||||||
|
<script src="/js/ham-utils/geo.js?v=5"></script>
|
||||||
|
<script src="/js/common.js?v=5"></script>
|
||||||
|
<script src="/js/alerts.js?v=5"></script>
|
||||||
<script>$(document).ready(function() { $("#nav-link-alerts").addClass("active"); }); <!-- highlight active page in nav --></script>
|
<script>$(document).ready(function() { $("#nav-link-alerts").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
@@ -117,6 +117,11 @@
|
|||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="darkMode" value="darkMode" oninput="toggleDarkMode();">
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="darkMode" value="darkMode" oninput="toggleDarkMode();">
|
||||||
<label class="form-check-label" for="darkMode">Dark mode</label>
|
<label class="form-check-label" for="darkMode">Dark mode</label>
|
||||||
</div>
|
</div>
|
||||||
|
<p class="card-text spothole-card-text">
|
||||||
|
Band color scheme<br/>
|
||||||
|
<select id="band-color-scheme" class="storeable-select form-select d-inline-block" oninput="setBandColorSchemeFromUI();" style="display: inline-block;">
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -129,9 +134,13 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/js/common.js?v=3"></script>
|
<script src="/js/ham-utils/utils.js?v=5"></script>
|
||||||
<script src="/js/spotsbandsandmap.js?v=3"></script>
|
<script src="/js/ham-utils/storage.js?v=5"></script>
|
||||||
<script src="/js/bands.js?v=3"></script>
|
<script src="/js/ham-utils/ui.js?v=5"></script>
|
||||||
|
<script src="/js/ham-utils/geo.js?v=5"></script>
|
||||||
|
<script src="/js/common.js?v=5"></script>
|
||||||
|
<script src="/js/spotsbandsandmap.js?v=5"></script>
|
||||||
|
<script src="/js/bands.js?v=5"></script>
|
||||||
<script>$(document).ready(function() { $("#nav-link-bands").addClass("active"); }); <!-- highlight active page in nav --></script>
|
<script>$(document).ready(function() { $("#nav-link-bands").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
@@ -44,6 +44,7 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"
|
||||||
integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"
|
integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"
|
||||||
crossorigin="anonymous"></script>
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/tinycolor2@1.6.0/cjs/tinycolor.min.js"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -129,6 +129,11 @@
|
|||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="darkMode" value="darkMode" oninput="toggleDarkMode();">
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="darkMode" value="darkMode" oninput="toggleDarkMode();">
|
||||||
<label class="form-check-label" for="darkMode">Dark mode</label>
|
<label class="form-check-label" for="darkMode">Dark mode</label>
|
||||||
</div>
|
</div>
|
||||||
|
<p class="card-text spothole-card-text">
|
||||||
|
Band color scheme<br/>
|
||||||
|
<select id="band-color-scheme" class="storeable-select form-select d-inline-block" oninput="setBandColorSchemeFromUI();" style="display: inline-block;">
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -147,9 +152,13 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/leaflet.geodesic"></script>
|
<script src="https://cdn.jsdelivr.net/npm/leaflet.geodesic"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@joergdietrich/leaflet.terminator@1.1.0/L.Terminator.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@joergdietrich/leaflet.terminator@1.1.0/L.Terminator.min.js"></script>
|
||||||
|
|
||||||
<script src="/js/common.js?v=3"></script>
|
<script src="/js/ham-utils/utils.js?v=5"></script>
|
||||||
<script src="/js/spotsbandsandmap.js?v=3"></script>
|
<script src="/js/ham-utils/storage.js?v=5"></script>
|
||||||
<script src="/js/map.js?v=3"></script>
|
<script src="/js/ham-utils/ui.js?v=5"></script>
|
||||||
|
<script src="/js/ham-utils/geo.js?v=5"></script>
|
||||||
|
<script src="/js/common.js?v=5"></script>
|
||||||
|
<script src="/js/spotsbandsandmap.js?v=5"></script>
|
||||||
|
<script src="/js/map.js?v=5"></script>
|
||||||
<script>$(document).ready(function() { $("#nav-link-map").addClass("active"); }); <!-- highlight active page in nav --></script>
|
<script>$(document).ready(function() { $("#nav-link-map").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
@@ -154,12 +154,17 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Theme</h5>
|
<h5 class="card-title">Theme</h5>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="darkMode" value="darkMode" oninput="toggleDarkMode();">
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="darkMode" value="darkMode" oninput="toggleDarkMode();">
|
||||||
<label class="form-check-label" for="darkMode">Dark mode</label>
|
<label class="form-check-label" for="darkMode">Dark mode</label>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="card-text spothole-card-text">
|
||||||
|
Band color scheme<br/>
|
||||||
|
<select id="band-color-scheme" class="storeable-select form-select d-inline-block" oninput="setBandColorSchemeFromUI();" style="display: inline-block;">
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -218,9 +223,13 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/js/common.js?v=3"></script>
|
<script src="/js/ham-utils/utils.js?v=5"></script>
|
||||||
<script src="/js/spotsbandsandmap.js?v=3"></script>
|
<script src="/js/ham-utils/storage.js?v=5"></script>
|
||||||
<script src="/js/spots.js?v=4"></script>
|
<script src="/js/ham-utils/ui.js?v=5"></script>
|
||||||
|
<script src="/js/ham-utils/geo.js?v=5"></script>
|
||||||
|
<script src="/js/common.js?v=5"></script>
|
||||||
|
<script src="/js/spotsbandsandmap.js?v=5"></script>
|
||||||
|
<script src="/js/spots.js?v=5"></script>
|
||||||
<script>$(document).ready(function() { $("#nav-link-spots").addClass("active"); }); <!-- highlight active page in nav --></script>
|
<script>$(document).ready(function() { $("#nav-link-spots").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
@@ -3,8 +3,12 @@
|
|||||||
|
|
||||||
<div id="status-container" class="row row-cols-1 row-cols-md-4 g-4 mt-4"></div>
|
<div id="status-container" class="row row-cols-1 row-cols-md-4 g-4 mt-4"></div>
|
||||||
|
|
||||||
<script src="/js/common.js?v=3"></script>
|
<script src="/js/ham-utils/utils.js?v=5"></script>
|
||||||
<script src="/js/status.js?v=3"></script>
|
<script src="/js/ham-utils/storage.js?v=5"></script>
|
||||||
|
<script src="/js/ham-utils/ui.js?v=5"></script>
|
||||||
|
<script src="/js/ham-utils/geo.js?v=5"></script>
|
||||||
|
<script src="/js/common.js?v=5"></script>
|
||||||
|
<script src="/js/status.js?v=5"></script>
|
||||||
<script>$(document).ready(function() { $("#nav-link-status").addClass("active"); }); <!-- highlight active page in nav --></script>
|
<script>$(document).ready(function() { $("#nav-link-status").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
@@ -1084,18 +1084,6 @@ components:
|
|||||||
type: integer
|
type: integer
|
||||||
description: Activation score. SOTA only
|
description: Activation score. SOTA only
|
||||||
example: 0
|
example: 0
|
||||||
icon:
|
|
||||||
type: string
|
|
||||||
descripton: Icon, from the Font Awesome set. This is fairly opinionated but is here to help the Spothole web UI and Field Spotter. Does not include the "fa-" prefix.
|
|
||||||
example: tree
|
|
||||||
band_color:
|
|
||||||
type: string
|
|
||||||
descripton: Colour to represent this spot, if a client chooses to colour spots based on their frequency band, using PSK Reporter's default colours. HTML colour e.g. hex.
|
|
||||||
example: "#ff0000"
|
|
||||||
band_contrast_color:
|
|
||||||
type: string
|
|
||||||
descripton: Black or white, whichever best contrasts with "band_color".
|
|
||||||
example: "white"
|
|
||||||
qrt:
|
qrt:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: QRT state. Some APIs return spots marked as QRT. Otherwise we can check the comments.
|
description: QRT state. Some APIs return spots marked as QRT. Otherwise we can check the comments.
|
||||||
@@ -1204,10 +1192,6 @@ components:
|
|||||||
type: integer
|
type: integer
|
||||||
description: Activation score. SOTA only
|
description: Activation score. SOTA only
|
||||||
example: 0
|
example: 0
|
||||||
icon:
|
|
||||||
type: string
|
|
||||||
descripton: Icon, from the Font Awesome set. This is fairly opinionated but is here to help the Spothole web UI and Field Spotter. Does not include the "fa-" prefix.
|
|
||||||
example: tree
|
|
||||||
source:
|
source:
|
||||||
type: string
|
type: string
|
||||||
description: Where we got the alert from.
|
description: Where we got the alert from.
|
||||||
@@ -1283,14 +1267,6 @@ components:
|
|||||||
type: int
|
type: int
|
||||||
description: The end frequency of this band, in Hz.
|
description: The end frequency of this band, in Hz.
|
||||||
example: 7200000
|
example: 7200000
|
||||||
color:
|
|
||||||
type: string
|
|
||||||
description: The color associated with this mode, as used on PSK Reporter.
|
|
||||||
example: "#5959ff"
|
|
||||||
contrast_color:
|
|
||||||
type: string
|
|
||||||
description: Black or white, whichever provides the best contrast against the band colour.
|
|
||||||
example: white
|
|
||||||
|
|
||||||
SIG:
|
SIG:
|
||||||
type: object
|
type: object
|
||||||
@@ -1302,10 +1278,6 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
description: The full name of the SIG
|
description: The full name of the SIG
|
||||||
example: Parks on the Air
|
example: Parks on the Air
|
||||||
icon:
|
|
||||||
type: string
|
|
||||||
description: Icon, from the Font Awesome set. This is fairly opinionated but is here to help the Spothole web UI and Field Spotter. Does not include the "fa-" prefix.
|
|
||||||
example: tree
|
|
||||||
ref_regex:
|
ref_regex:
|
||||||
type: string
|
type: string
|
||||||
description: Regex that matches this SIG's reference IDs. Generally for Spothole's own internal use, clients probably won't need this.
|
description: Regex that matches this SIG's reference IDs. Generally for Spothole's own internal use, clients probably won't need this.
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ function addAlertRowsToTable(tbody, alerts) {
|
|||||||
$tr.append(`<td class='hideonmobile'>${commentText}</td>`);
|
$tr.append(`<td class='hideonmobile'>${commentText}</td>`);
|
||||||
}
|
}
|
||||||
if (showSource) {
|
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) {
|
if (showRef) {
|
||||||
$tr.append(`<td class='hideonmobile'>${sig_refs}</td>`);
|
$tr.append(`<td class='hideonmobile'>${sig_refs}</td>`);
|
||||||
@@ -257,7 +257,7 @@ function addAlertRowsToTable(tbody, alerts) {
|
|||||||
}
|
}
|
||||||
$td2 = $("<td colspan='100'>");
|
$td2 = $("<td colspan='100'>");
|
||||||
if (showSource) {
|
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) {
|
if (showRef) {
|
||||||
$td2.append(`${sig_refs} `);
|
$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>');
|
var table = $('<table id="bands-table">').append('<thead><tr></tr></thead><tbody><tr></tr></tbody>');
|
||||||
bandToSpots.forEach(function (spotList, bandName) {
|
bandToSpots.forEach(function (spotList, bandName) {
|
||||||
// Get the colours for the band from the first spot, and prepare the header
|
// 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
|
// Get the band data to fetch start and end frequencies
|
||||||
let band = options["bands"].filter(function (b) {
|
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.
|
// Now each spot is tagged with how far down the div it should go, add them to the DOM.
|
||||||
spotList.forEach(s => {
|
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
|
// 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.beginPath();
|
||||||
ctx.lineWidth = 2;
|
ctx.lineWidth = 2;
|
||||||
ctx.lineCap = "round";
|
ctx.lineCap = "round";
|
||||||
ctx.strokeStyle = s.band_color;
|
ctx.strokeStyle = bandToColor(s['band']);
|
||||||
ctx.moveTo(0, pxDownBandFreq);
|
ctx.moveTo(0, pxDownBandFreq);
|
||||||
ctx.lineTo(BAND_COLUMN_CANVAS_WIDTH_PX, pxDownBandLabel);
|
ctx.lineTo(BAND_COLUMN_CANVAS_WIDTH_PX, pxDownBandLabel);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
@@ -228,6 +228,21 @@ function loadOptions() {
|
|||||||
// Store options
|
// Store options
|
||||||
options = jsonData;
|
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
|
// Add CSS for band toggle buttons
|
||||||
addBandToggleColourCSS(options["bands"]);
|
addBandToggleColourCSS(options["bands"]);
|
||||||
|
|
||||||
@@ -239,13 +254,6 @@ function loadOptions() {
|
|||||||
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
||||||
generateMultiToggleFilterCard("#source-options", "source", options["spot_sources"]);
|
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
|
// 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
|
// 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.
|
// loading settings, so this needs to be called before that.
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
var options = {};
|
var options = {};
|
||||||
// Last time we updated the spots/alerts list on display.
|
// Last time we updated the spots/alerts list on display.
|
||||||
var lastUpdateTime;
|
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
|
// 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
|
// 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.
|
// top-level html element to use CSS selectors to remove bits of UI.
|
||||||
let embedded = params.get("embedded");
|
let embedded = params.get("embedded");
|
||||||
if (embedded != null && embedded === "true") {
|
if (embedded != null && embedded === "true") {
|
||||||
embeddedMode = true;
|
useLocalStorage = false;
|
||||||
$("html").attr("embedded-mode", "true");
|
$("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
|
// When the "use local time" field is changed, reload the table and save settings
|
||||||
function timeZoneUpdated() {
|
function timeZoneUpdated() {
|
||||||
updateTable();
|
updateTable();
|
||||||
@@ -166,106 +142,6 @@ function columnsUpdated() {
|
|||||||
saveSettings();
|
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 to set dark mode on or off
|
||||||
function enableDarkMode(dark) {
|
function enableDarkMode(dark) {
|
||||||
$("html").attr("data-bs-theme", dark ? "dark" : "light");
|
$("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
|
// Startup
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
usePreferredTheme();
|
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
|
// Create geodesics if required
|
||||||
if ($("#mapShowGeodesics")[0].checked && s["de_latitude"] != null && s["de_longitude"] != null) {
|
if ($("#mapShowGeodesics")[0].checked && s["de_latitude"] != null && s["de_longitude"] != null) {
|
||||||
var geodesic = L.geodesic([[s["de_latitude"], s["de_longitude"]], m.getLatLng()], {
|
try {
|
||||||
color: s["band_color"],
|
var geodesic = L.geodesic([[s["de_latitude"], s["de_longitude"]], m.getLatLng()], {
|
||||||
wrap: false,
|
color: bandToColor(s['band']),
|
||||||
steps: 5
|
wrap: false,
|
||||||
});
|
steps: 5
|
||||||
geodesicsLayer.addLayer(geodesic);
|
});
|
||||||
|
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.
|
// Get an icon for a spot, based on its band, using PSK Reporter colours, its program etc.
|
||||||
function getIcon(s) {
|
function getIcon(s) {
|
||||||
return L.ExtraMarkers.icon({
|
return L.ExtraMarkers.icon({
|
||||||
icon: "fa-" + s["icon"],
|
icon: sigToIcon(s["sig"], "fa-tower-cell"),
|
||||||
iconColor: s["band_contrast_color"],
|
iconColor: bandToContrastColor(s["band"]),
|
||||||
markerColor: s["band_color"],
|
markerColor: bandToColor(s["band"]),
|
||||||
shape: 'circle',
|
shape: 'circle',
|
||||||
prefix: 'fa',
|
prefix: 'fa',
|
||||||
svg: true
|
svg: true
|
||||||
@@ -136,7 +140,7 @@ function getTooltipText(s) {
|
|||||||
ttt += "<br/>";
|
ttt += "<br/>";
|
||||||
|
|
||||||
// Source / SIG / Ref
|
// 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
|
// Time
|
||||||
ttt += `<span class='icon-wrapper'><i class='fa-solid fa-clock markerPopupIcon'></i></span> ${moment.unix(s["time"]).fromNow()}`;
|
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
|
// Store options
|
||||||
options = jsonData;
|
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
|
// Add CSS for band toggle buttons
|
||||||
addBandToggleColourCSS(options["bands"]);
|
addBandToggleColourCSS(options["bands"]);
|
||||||
|
|
||||||
@@ -167,13 +186,6 @@ function loadOptions() {
|
|||||||
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
||||||
generateMultiToggleFilterCard("#source-options", "source", options["spot_sources"]);
|
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
|
// 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
|
// 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.
|
// 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>`);
|
$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) {
|
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) {
|
if (showMode) {
|
||||||
$tr.append(`<td class='nowrap'>${mode_string}</td>`);
|
$tr.append(`<td class='nowrap'>${mode_string}</td>`);
|
||||||
@@ -340,7 +340,7 @@ function createNewTableRowsForSpot(s, highlightNew) {
|
|||||||
$tr.append(`<td class='nowrap hideonmobile'>${bearingText}</td>`);
|
$tr.append(`<td class='nowrap hideonmobile'>${bearingText}</td>`);
|
||||||
}
|
}
|
||||||
if (showType) {
|
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) {
|
if (showRef) {
|
||||||
$tr.append(`<td class='hideonmobile' style='max-width: 11em;'>${sig_refs}</td>`);
|
$tr.append(`<td class='hideonmobile' style='max-width: 11em;'>${sig_refs}</td>`);
|
||||||
@@ -366,7 +366,7 @@ function createNewTableRowsForSpot(s, highlightNew) {
|
|||||||
$td2 = $("<td colspan='100'>");
|
$td2 = $("<td colspan='100'>");
|
||||||
$td2floatleft = $(`<div style="float: left;">`);
|
$td2floatleft = $(`<div style="float: left;">`);
|
||||||
if (showType) {
|
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) {
|
if (showRef) {
|
||||||
$td2floatleft.append(`${sig_refs} `);
|
$td2floatleft.append(`${sig_refs} `);
|
||||||
@@ -398,6 +398,21 @@ function loadOptions() {
|
|||||||
// Store options
|
// Store options
|
||||||
options = jsonData;
|
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
|
// Add CSS for band toggle buttons
|
||||||
addBandToggleColourCSS(options["bands"]);
|
addBandToggleColourCSS(options["bands"]);
|
||||||
|
|
||||||
@@ -409,13 +424,6 @@ function loadOptions() {
|
|||||||
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
||||||
generateMultiToggleFilterCard("#source-options", "source", options["spot_sources"]);
|
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
|
// 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
|
// 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.
|
// loading settings, so this needs to be called before that.
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ function addBandToggleColourCSS(band_options) {
|
|||||||
band_options.forEach(o => {
|
band_options.forEach(o => {
|
||||||
// CSS doesn't like IDs with decimal points in, so we need to replace that
|
// CSS doesn't like IDs with decimal points in, so we need to replace that
|
||||||
var cssFormattedBandName = o['name'] ? o['name'].replace('.', 'p') : "unknown";
|
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(`#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: ${o['color']}; color: ${o['contrast_color']};}`);
|
$style.append(`.btn-check:checked + #filter-button-label-band-${cssFormattedBandName} { background-color: ${bandToColor(o['name'])}; color: ${bandToContrastColor(o['name'])};}`);
|
||||||
});
|
});
|
||||||
$('html > head').append($style);
|
$('html > head').append($style);
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ function generateBandsMultiToggleFilterCard(band_options) {
|
|||||||
function setHamHFBandToggles() {
|
function setHamHFBandToggles() {
|
||||||
const hamHFBands = ["160m", "80m", "60m", "40m", "30m", "20m", "17m", "15m", "12m", "10m", "6m"];
|
const hamHFBands = ["160m", "80m", "60m", "40m", "30m", "20m", "17m", "15m", "12m", "10m", "6m"];
|
||||||
$(".filter-button-band").each(function() {
|
$(".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();
|
filtersUpdated();
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ function setHamHFBandToggles() {
|
|||||||
function generateSIGsMultiToggleFilterCard(sig_options) {
|
function generateSIGsMultiToggleFilterCard(sig_options) {
|
||||||
// Create a button for each option
|
// Create a button for each option
|
||||||
sig_options.forEach(o => {
|
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
|
// 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> `);
|
$("#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();
|
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
|
// 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.
|
// after some time has passed with it in the background.
|
||||||
addEventListener("visibilitychange", (event) => {
|
addEventListener("visibilitychange", (event) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user