mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-02-04 09:14:30 +00:00
191 lines
8.8 KiB
JavaScript
191 lines
8.8 KiB
JavaScript
// Storage for the options that the server gives us. This will define our filters.
|
|
var options = {};
|
|
// Last time we updated the spots/alerts list on display.
|
|
var lastUpdateTime;
|
|
|
|
// 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
|
|
// from the various filter & display options, so this 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 occurs..
|
|
function loadURLParams() {
|
|
let params = new URLSearchParams(document.location.search);
|
|
|
|
// Handle embedded mode. We set a global to e.g. suppress loading/saving settings, and apply an attribute to the
|
|
// top-level html element to use CSS selectors to remove bits of UI.
|
|
let embedded = params.get("embedded");
|
|
if (embedded != null && embedded === "true") {
|
|
useLocalStorage = false;
|
|
$("html").attr("embedded-mode", "true");
|
|
}
|
|
|
|
// Handle other params
|
|
updateSelectFromParam(params, "color-scheme", "color-scheme");
|
|
updateSelectFromParam(params, "time-zone", "time-zone"); // Only on Spots and Alerts pages
|
|
updateSelectFromParam(params, "limit", "spots-to-fetch"); // Only on Spots page
|
|
updateSelectFromParam(params, "limit", "alerts-to-fetch"); // Only on Alerts page
|
|
updateSelectFromParam(params, "max_age", "max-spot-age"); // Only on Map & Bands pages
|
|
updateFilterFromParam(params, "band", "band");
|
|
updateFilterFromParam(params, "sig", "sig");
|
|
updateFilterFromParam(params, "source", "source");
|
|
updateFilterFromParam(params, "mode", "mode");
|
|
updateFilterFromParam(params, "dx_continent", "dx_continent");
|
|
updateFilterFromParam(params, "de_continent", "de_continent");
|
|
}
|
|
|
|
// Update an HTML checkbox element so that its selected matches the given parameter (which must have a true or false value)
|
|
function updateCheckboxFromParam(params, paramName, checkboxID) {
|
|
let v = params.get(paramName);
|
|
if (v != null) {
|
|
$("#" + checkboxID).prop("checked", (v === "true") ? true : false);
|
|
}
|
|
}
|
|
|
|
// Update an HTML select element so that its value matches the given parameter
|
|
function updateSelectFromParam(params, paramName, selectID) {
|
|
let v = params.get(paramName);
|
|
if (v != null) {
|
|
$("#" + selectID).prop("value", v);
|
|
// Extra check if this is the "color scheme" select
|
|
if (selectID == "color-scheme") {
|
|
setColorScheme(v);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update a set of HTML checkbox elements describing a filter of the given name, so that any items named in the
|
|
// parameter (as a comma-separated list) will be enabled, and all others disabled. e.g. if paramName is
|
|
// "filter-band" and the params contain "filter-band=20m,40m", and prefix is "band", then #filter-button-band-30m
|
|
// would be disabled but #filter-button-band-20m and #filter-button-band-40m would be enabled.
|
|
function updateFilterFromParam(params, paramName, filterName) {
|
|
let v = params.get(paramName);
|
|
if (v != null) {
|
|
// First uncheck all options for the filter
|
|
$(".filter-button-" + filterName).prop("checked", false);
|
|
// Now find out which ones should be enabled
|
|
let s = v.split(",");
|
|
s.forEach(val => $("#filter-button-" + filterName + "-" + val).prop("checked", true));
|
|
}
|
|
}
|
|
|
|
// For a parameter, such as dx_continent, get the query string for the current filter options.
|
|
function getQueryStringFor(parameter) {
|
|
return parameter + "=" + encodeURIComponent(getSelectedFilterOptions(parameter));
|
|
}
|
|
|
|
// For a parameter, such as dx_continent, get the filter options that are currently selected in the UI.
|
|
function getSelectedFilterOptions(parameter) {
|
|
return $(".filter-button-" + parameter).filter(function() {
|
|
return this.checked;
|
|
}).map(function() {
|
|
return this.value;
|
|
}).get().join(",");
|
|
}
|
|
|
|
// For a parameter, such as dx_continent, return true if all possible options are enabled. (In this case, we don't need
|
|
// to bother sending this as one of the query parameters to the API; no parameter provided implies "send everything".)
|
|
function allFilterOptionsSelected(parameter) {
|
|
var filter = $(".filter-button-" + parameter).filter(function() {
|
|
return !this.checked;
|
|
}).get();
|
|
return filter.length == 0;
|
|
}
|
|
|
|
|
|
// Generate a filter card with multiple toggle buttons plus All/None buttons.
|
|
function generateMultiToggleFilterCard(elementID, filterQuery, options) {
|
|
// Create a button for each option
|
|
options.forEach(o => {
|
|
$(elementID).append(`<input type="checkbox" class="btn-check filter-button-${filterQuery} storeable-checkbox" name="options" id="filter-button-${filterQuery}-${o}" value="${o}" autocomplete="off" onClick="filtersUpdated()" checked><label class="btn btn-outline-primary" for="filter-button-${filterQuery}-${o}">${o}</label> `);
|
|
});
|
|
// Create All/None buttons
|
|
$(elementID).append(` <span style="display: inline-block"><button id="filter-button-${filterQuery}-all" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('${filterQuery}', true);">All</button> <button id="filter-button-${filterQuery}-none" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('${filterQuery}', false);">None</button></span>`);
|
|
}
|
|
|
|
// Method called when "All" or "None" is clicked
|
|
function toggleFilterButtons(filterQuery, state) {
|
|
$(".filter-button-" + filterQuery).each(function() {
|
|
$(this).prop('checked', state);
|
|
});
|
|
filtersUpdated();
|
|
}
|
|
|
|
// Update the refresh timing display
|
|
function updateRefreshDisplay() {
|
|
if (lastUpdateTime != null) {
|
|
let secSinceUpdate = moment.duration(moment().diff(lastUpdateTime)).asSeconds();
|
|
let count = REFRESH_INTERVAL_SEC;
|
|
let updatingString = "Updating..."
|
|
if (secSinceUpdate < REFRESH_INTERVAL_SEC) {
|
|
count = REFRESH_INTERVAL_SEC - secSinceUpdate;
|
|
if (count <= 60) {
|
|
var number = count.toFixed(0);
|
|
updatingString = "<span class='nowrap'>Updating in " + number + " second" + (number != "1" ? "s" : "") + ".</span>";
|
|
} else {
|
|
var number = Math.round(count / 60.0).toFixed(0);
|
|
updatingString = "<span class='nowrap'>Updating in " + number + " minute" + (number != "1" ? "s" : "") + ".</span>";
|
|
}
|
|
}
|
|
$("#timing-container").html("Last updated at " + lastUpdateTime.format('HH:mm') + " UTC. " + updatingString);
|
|
}
|
|
}
|
|
|
|
// When the "use local time" field is changed, reload the table and save settings
|
|
function timeZoneUpdated() {
|
|
updateTable();
|
|
saveSettings();
|
|
}
|
|
|
|
// When one of the column toggle checkboxes are changed, reload the table and save settings
|
|
function columnsUpdated() {
|
|
updateTable();
|
|
saveSettings();
|
|
}
|
|
|
|
// Function to set the colour scheme based on the state of the UI select box
|
|
function setColorSchemeFromUI() {
|
|
let theme = $("#color-scheme option:selected").val();
|
|
if (theme != "") {
|
|
setColorScheme(theme);
|
|
saveSettings();
|
|
}
|
|
}
|
|
|
|
// Function to set the color scheme. Supported values: "dark", "light", "auto"
|
|
function setColorScheme(mode) {
|
|
let effectiveModeDark = mode == "dark";
|
|
if (mode == "auto") {
|
|
effectiveModeDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
}
|
|
$("html").attr("data-bs-theme", effectiveModeDark ? "dark" : "light");
|
|
const metaThemeColor = document.querySelector("meta[name=theme-color]");
|
|
metaThemeColor.setAttribute("content", effectiveModeDark ? "black" : "white");
|
|
const metaAppleStatusBarStyle = document.querySelector("meta[name=apple-mobile-web-app-status-bar-style]");
|
|
metaAppleStatusBarStyle.setAttribute("content", effectiveModeDark ? "black-translucent" : "white-translucent");
|
|
}
|
|
|
|
// Startup function to determine whether to use light or dark mode, or leave as auto
|
|
function usePreferredTheme() {
|
|
// Work out if we have ever explicitly saved the value of our select box. If so, we set our colour scheme now based
|
|
// on that. If not, we let the select stay with nothing selected, so that the server sets it to whatever the
|
|
// server's default is when the options call is retrieved.
|
|
let val = localStorage.getItem("#color-scheme:value");
|
|
if (val != null) {
|
|
setColorScheme(JSON.parse(val));
|
|
}
|
|
}
|
|
|
|
// Sets up a listener on the OS light-dark theme change. If the Spothole user theme is set to Auto, the UI will be
|
|
// updated, otherwise if the Spothole user theme is forced to light or dark, that preference will remain.
|
|
function listenForOSThemeChange() {
|
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
|
setColorScheme($("#color-scheme option:selected").val());
|
|
});
|
|
}
|
|
|
|
// Startup
|
|
$(document).ready(function() {
|
|
usePreferredTheme();
|
|
listenForOSThemeChange();
|
|
});
|